@umituz/web-ai 1.0.3 → 1.0.5

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 (47) hide show
  1. package/dist/{AIErrors-B2ogd1PB.d.ts → AIErrors-CLjXRty-.d.ts} +99 -92
  2. package/dist/{AIErrors-C5EasV_c.d.cts → AIErrors-DjZXUiLT.d.cts} +99 -92
  3. package/dist/{cancellation-error-DwHrDKBz.d.cts → cancellation-error-Cj7fJyCg.d.cts} +3 -3
  4. package/dist/{cancellation-error-CKcZRhFg.d.ts → cancellation-error-CkMn5Kft.d.ts} +3 -3
  5. package/dist/chat/index.cjs.map +1 -1
  6. package/dist/chat/index.d.cts +3 -3
  7. package/dist/chat/index.d.ts +3 -3
  8. package/dist/chat/index.js.map +1 -1
  9. package/dist/{chat.service-CL1qtYxe.d.cts → chat.service-9M-KpLo-.d.cts} +1 -1
  10. package/dist/{chat.service-q05GnhEm.d.ts → chat.service-B4FLFxR1.d.ts} +1 -1
  11. package/dist/content/index.cjs +1047 -490
  12. package/dist/content/index.cjs.map +1 -1
  13. package/dist/content/index.d.cts +165 -219
  14. package/dist/content/index.d.ts +165 -219
  15. package/dist/content/index.js +983 -491
  16. package/dist/content/index.js.map +1 -1
  17. package/dist/{create-ai-9PW752z8.d.ts → create-ai-0eZFFqTw.d.ts} +1 -1
  18. package/dist/{create-ai-CXYKPK6G.d.cts → create-ai-BrKR_5fQ.d.cts} +1 -1
  19. package/dist/hooks/index.cjs +66 -8
  20. package/dist/hooks/index.cjs.map +1 -1
  21. package/dist/hooks/index.d.cts +21 -11
  22. package/dist/hooks/index.d.ts +21 -11
  23. package/dist/hooks/index.js +65 -9
  24. package/dist/hooks/index.js.map +1 -1
  25. package/dist/index-BUVULo3h.d.ts +177 -0
  26. package/dist/index-BulSkB2L.d.cts +177 -0
  27. package/dist/index.cjs +1800 -15
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +8 -5
  30. package/dist/index.d.ts +8 -5
  31. package/dist/index.js +1757 -15
  32. package/dist/index.js.map +1 -1
  33. package/dist/{provider.factory-DfDfk0dK.d.ts → provider.factory-BwPnqSEU.d.ts} +3 -1
  34. package/dist/{provider.factory-BxsfK4WW.d.cts → provider.factory-DyfJtR8M.d.cts} +3 -1
  35. package/dist/providers/index.cjs +9 -0
  36. package/dist/providers/index.cjs.map +1 -1
  37. package/dist/providers/index.d.cts +10 -8
  38. package/dist/providers/index.d.ts +10 -8
  39. package/dist/providers/index.js +7 -1
  40. package/dist/providers/index.js.map +1 -1
  41. package/dist/requestQueue-CkJ6Xc9K.d.cts +261 -0
  42. package/dist/requestQueue-CvxWxC-z.d.ts +261 -0
  43. package/dist/wizard/index.d.cts +2 -86
  44. package/dist/wizard/index.d.ts +2 -86
  45. package/package.json +16 -1
  46. package/dist/ContentGeneration-B6iK2B33.d.cts +0 -93
  47. package/dist/ContentGeneration-B6iK2B33.d.ts +0 -93
package/dist/index.cjs CHANGED
@@ -1084,6 +1084,26 @@ function isMediaProvider(provider) {
1084
1084
  return provider.capabilities.includes("image") || provider.capabilities.includes("video");
1085
1085
  }
1086
1086
 
1087
+ // src/content/domain/errors/AIErrors.ts
1088
+ var ValidationError = class extends AIError {
1089
+ constructor(message) {
1090
+ super(message, "VALIDATION_ERROR");
1091
+ this.name = "ValidationError";
1092
+ }
1093
+ };
1094
+ var ProviderUnavailableError = class extends AIError {
1095
+ constructor(providerId) {
1096
+ super(`Provider ${providerId} is not available`, "PROVIDER_UNAVAILABLE");
1097
+ this.name = "ProviderUnavailableError";
1098
+ }
1099
+ };
1100
+ var ProviderQuotaExhaustedError = class extends AIError {
1101
+ constructor(providerId) {
1102
+ super(`Provider ${providerId} quota exhausted`, "PROVIDER_QUOTA_EXHAUSTED");
1103
+ this.name = "ProviderQuotaExhaustedError";
1104
+ }
1105
+ };
1106
+
1087
1107
  // src/content/domain/limits/ProviderTimingConfig.ts
1088
1108
  var ProviderTimingConfig = {
1089
1109
  DEFAULT_TIMEOUT_MS: 3e4,
@@ -1426,6 +1446,9 @@ var ProviderFactory = class {
1426
1446
  );
1427
1447
  }
1428
1448
  };
1449
+ function createProviderFactory(config) {
1450
+ return new ProviderFactory(config);
1451
+ }
1429
1452
 
1430
1453
  // src/providers/anthropic/anthropic.constants.ts
1431
1454
  var ANTHROPIC_MODELS = {
@@ -2455,6 +2478,9 @@ function toGroqMessages(messages, systemPrompt) {
2455
2478
  }
2456
2479
  return wire;
2457
2480
  }
2481
+ function createGroqProvider(config) {
2482
+ return new GroqProvider(config);
2483
+ }
2458
2484
  function findLastIndex2(items, predicate) {
2459
2485
  for (let i = items.length - 1; i >= 0; i--) {
2460
2486
  if (predicate(items[i])) return i;
@@ -2721,6 +2747,128 @@ function createGeminiProvider(config) {
2721
2747
  return new GeminiProvider(config);
2722
2748
  }
2723
2749
 
2750
+ // src/content/domain/predicates/KeywordProminenceClassifier.ts
2751
+ var HIGH_DENSITY_THRESHOLD = 2;
2752
+ var MEDIUM_DENSITY_THRESHOLD = 1;
2753
+ function classifyKeywordProminence(densityPercent) {
2754
+ if (densityPercent > HIGH_DENSITY_THRESHOLD) return "high";
2755
+ if (densityPercent > MEDIUM_DENSITY_THRESHOLD) return "medium";
2756
+ return "low";
2757
+ }
2758
+ function isUnderutilizedKeyword(densityPercent) {
2759
+ return densityPercent < 1;
2760
+ }
2761
+
2762
+ // src/content/domain/types/index.ts
2763
+ var PLATFORM_SPECS = {
2764
+ twitter: {
2765
+ maxLength: 280,
2766
+ style: "concise, engaging, thread-friendly",
2767
+ emojiSupport: true,
2768
+ hashtagSupport: true,
2769
+ imageSupport: true,
2770
+ videoSupport: true,
2771
+ maxHashtags: 5
2772
+ },
2773
+ linkedin: {
2774
+ maxLength: 3e3,
2775
+ style: "professional, insightful, industry-focused",
2776
+ emojiSupport: true,
2777
+ hashtagSupport: true,
2778
+ imageSupport: true,
2779
+ videoSupport: true,
2780
+ maxHashtags: 5
2781
+ },
2782
+ instagram: {
2783
+ maxLength: 2200,
2784
+ style: "visual, engaging, emoji-rich",
2785
+ emojiSupport: true,
2786
+ hashtagSupport: true,
2787
+ imageSupport: true,
2788
+ videoSupport: true,
2789
+ maxHashtags: 30
2790
+ },
2791
+ threads: {
2792
+ maxLength: 500,
2793
+ style: "conversational, community-driven",
2794
+ emojiSupport: true,
2795
+ hashtagSupport: true,
2796
+ imageSupport: true,
2797
+ videoSupport: true,
2798
+ maxHashtags: 5
2799
+ },
2800
+ tiktok: {
2801
+ maxLength: 150,
2802
+ style: "catchy, trending, short-form",
2803
+ emojiSupport: true,
2804
+ hashtagSupport: true,
2805
+ imageSupport: false,
2806
+ videoSupport: true,
2807
+ maxHashtags: 5
2808
+ },
2809
+ facebook: {
2810
+ maxLength: 63206,
2811
+ style: "conversational, engaging, shareable",
2812
+ emojiSupport: true,
2813
+ hashtagSupport: true,
2814
+ imageSupport: true,
2815
+ videoSupport: true,
2816
+ maxHashtags: 5
2817
+ }
2818
+ };
2819
+
2820
+ // src/content/domain/predicates/HashtagPriorityRanker.ts
2821
+ var DEFAULT_HASHTAG_BUDGET = Number.MAX_SAFE_INTEGER;
2822
+ function resolveHashtagBudget(platform) {
2823
+ return PLATFORM_SPECS[platform]?.maxHashtags ?? DEFAULT_HASHTAG_BUDGET;
2824
+ }
2825
+ function rankAndTrimHashtags(hashtags, budget) {
2826
+ if (hashtags.length <= budget) return [...hashtags];
2827
+ return [...hashtags].sort((a, b) => b.length - a.length).slice(0, budget);
2828
+ }
2829
+
2830
+ // src/content/domain/predicates/QualityRecommenderByRatio.ts
2831
+ var WIDE_RATIOS = /* @__PURE__ */ new Set(["16:9", "4:3"]);
2832
+ function recommendQualityForAspectRatio(ratio) {
2833
+ return WIDE_RATIOS.has(ratio) ? "hd" : "standard";
2834
+ }
2835
+
2836
+ // src/content/domain/predicates/PlatformSpecResolver.ts
2837
+ var FALLBACK_SPEC = {
2838
+ maxLength: 280,
2839
+ style: "concise, engaging, audience-friendly",
2840
+ emojiSupport: true,
2841
+ hashtagSupport: true,
2842
+ imageSupport: true,
2843
+ videoSupport: true,
2844
+ maxHashtags: 5
2845
+ };
2846
+ function resolvePlatformSpec(platform) {
2847
+ return PLATFORM_SPECS[platform] ?? FALLBACK_SPEC;
2848
+ }
2849
+ function resolveMaxContentLength(platform, override) {
2850
+ return override ?? resolvePlatformSpec(platform).maxLength;
2851
+ }
2852
+
2853
+ // src/content/domain/predicates/ScoreRangeGuard.ts
2854
+ var SCORE_MIN = 0;
2855
+ var SCORE_MAX = 100;
2856
+ var SCORE_FALLBACK = 50;
2857
+ var CONFIDENCE_MIN = 0;
2858
+ var CONFIDENCE_MAX = 1;
2859
+ var CONFIDENCE_FALLBACK = 0.5;
2860
+ function clampScoreToValidRange(score, fallback = SCORE_FALLBACK) {
2861
+ if (!Number.isFinite(score)) return fallback;
2862
+ return Math.max(SCORE_MIN, Math.min(SCORE_MAX, score));
2863
+ }
2864
+ function clampConfidenceToValidRange(confidence, fallback = CONFIDENCE_FALLBACK) {
2865
+ if (!Number.isFinite(confidence)) return fallback;
2866
+ return Math.max(CONFIDENCE_MIN, Math.min(CONFIDENCE_MAX, confidence));
2867
+ }
2868
+
2869
+ // src/content/domain/predicates/AbTestConfidence.ts
2870
+ var AB_TEST_DEFAULT_CONFIDENCE = 0.7;
2871
+
2724
2872
  // src/content/domain/predicates/FalImageRenderingOptions.ts
2725
2873
  var FAL_FORMAT_TO_IMAGE_SIZE = {
2726
2874
  square: "square_hd",
@@ -2991,7 +3139,7 @@ var TimeoutError = class extends Error {
2991
3139
  };
2992
3140
 
2993
3141
  // src/providers/pruna/client/core/errors/validation-error.ts
2994
- var ValidationError = class extends Error {
3142
+ var ValidationError2 = class extends Error {
2995
3143
  field;
2996
3144
  reason;
2997
3145
  receivedValue;
@@ -3276,21 +3424,21 @@ function deriveGenerationCacheKey(apiKey, input) {
3276
3424
  // src/providers/pruna/client/core/validation/api-key-validator.ts
3277
3425
  function assertValidApiKey(apiKey) {
3278
3426
  if (typeof apiKey !== "string") {
3279
- throw new ValidationError({
3427
+ throw new ValidationError2({
3280
3428
  field: "apiKey",
3281
3429
  reason: "must be a string",
3282
3430
  receivedValue: typeof apiKey
3283
3431
  });
3284
3432
  }
3285
3433
  if (apiKey.length < MIN_API_KEY_LENGTH) {
3286
- throw new ValidationError({
3434
+ throw new ValidationError2({
3287
3435
  field: "apiKey",
3288
3436
  reason: "must not be empty",
3289
3437
  receivedValue: apiKey
3290
3438
  });
3291
3439
  }
3292
3440
  if (apiKey.length > MAX_API_KEY_LENGTH) {
3293
- throw new ValidationError({
3441
+ throw new ValidationError2({
3294
3442
  field: "apiKey",
3295
3443
  reason: `exceeds maximum length of ${MAX_API_KEY_LENGTH}`,
3296
3444
  receivedValue: apiKey.length
@@ -3307,7 +3455,7 @@ function classifyImageSource(source) {
3307
3455
  }
3308
3456
  function assertSecureImageSource(image, fieldName = "image") {
3309
3457
  if (typeof image !== "string") {
3310
- throw new ValidationError({
3458
+ throw new ValidationError2({
3311
3459
  field: fieldName,
3312
3460
  reason: "must be a string",
3313
3461
  receivedValue: typeof image
@@ -3315,14 +3463,14 @@ function assertSecureImageSource(image, fieldName = "image") {
3315
3463
  }
3316
3464
  const kind = classifyImageSource(image);
3317
3465
  if (kind === "http-url") {
3318
- throw new ValidationError({
3466
+ throw new ValidationError2({
3319
3467
  field: fieldName,
3320
3468
  reason: "HTTP URLs are not supported; use HTTPS or a base64 data URI",
3321
3469
  receivedValue: image
3322
3470
  });
3323
3471
  }
3324
3472
  if (kind === "unknown") {
3325
- throw new ValidationError({
3473
+ throw new ValidationError2({
3326
3474
  field: fieldName,
3327
3475
  reason: "must be an HTTPS URL or a base64 data URI",
3328
3476
  receivedValue: typeof image
@@ -3346,7 +3494,7 @@ function isSupportedAspectRatio(value) {
3346
3494
  function assertValidAspectRatio(value, fieldName = "aspect_ratio") {
3347
3495
  if (value === void 0 || value === null) return;
3348
3496
  if (!isSupportedAspectRatio(value)) {
3349
- throw new ValidationError({
3497
+ throw new ValidationError2({
3350
3498
  field: fieldName,
3351
3499
  reason: `must be one of ${SUPPORTED_ASPECT_RATIOS.join(", ")}`,
3352
3500
  receivedValue: value
@@ -3362,7 +3510,7 @@ function isSupportedResolution(value) {
3362
3510
  function assertValidResolution(value, fieldName = "resolution") {
3363
3511
  if (value === void 0 || value === null) return;
3364
3512
  if (!isSupportedResolution(value)) {
3365
- throw new ValidationError({
3513
+ throw new ValidationError2({
3366
3514
  field: fieldName,
3367
3515
  reason: `must be one of ${SUPPORTED_RESOLUTIONS.join(", ")}`,
3368
3516
  receivedValue: value
@@ -3379,7 +3527,7 @@ function isValidDuration(value) {
3379
3527
  function assertValidDuration(value, fieldName = "duration") {
3380
3528
  if (value === void 0 || value === null) return;
3381
3529
  if (!isValidDuration(value)) {
3382
- throw new ValidationError({
3530
+ throw new ValidationError2({
3383
3531
  field: fieldName,
3384
3532
  reason: `must be an integer between ${MIN_VIDEO_DURATION_SECONDS} and ${MAX_VIDEO_DURATION_SECONDS} seconds`,
3385
3533
  receivedValue: value
@@ -3392,21 +3540,21 @@ var MIN_PROMPT_LENGTH = 1;
3392
3540
  var MAX_PROMPT_LENGTH = 4e3;
3393
3541
  function assertValidPrompt(value, fieldName = "prompt") {
3394
3542
  if (typeof value !== "string") {
3395
- throw new ValidationError({
3543
+ throw new ValidationError2({
3396
3544
  field: fieldName,
3397
3545
  reason: "must be a string",
3398
3546
  receivedValue: typeof value
3399
3547
  });
3400
3548
  }
3401
3549
  if (value.trim().length < MIN_PROMPT_LENGTH) {
3402
- throw new ValidationError({
3550
+ throw new ValidationError2({
3403
3551
  field: fieldName,
3404
3552
  reason: "must not be empty or whitespace-only",
3405
3553
  receivedValue: value
3406
3554
  });
3407
3555
  }
3408
3556
  if (value.length > MAX_PROMPT_LENGTH) {
3409
- throw new ValidationError({
3557
+ throw new ValidationError2({
3410
3558
  field: fieldName,
3411
3559
  reason: `exceeds maximum length of ${MAX_PROMPT_LENGTH} characters`,
3412
3560
  receivedValue: value.length
@@ -3423,7 +3571,7 @@ function isValidSeed(value) {
3423
3571
  function assertValidSeed(value, fieldName = "seed") {
3424
3572
  if (value === void 0 || value === null) return;
3425
3573
  if (!isValidSeed(value)) {
3426
- throw new ValidationError({
3574
+ throw new ValidationError2({
3427
3575
  field: fieldName,
3428
3576
  reason: `must be an integer in [${MIN_SEED}, ${MAX_SEED}]`,
3429
3577
  receivedValue: value
@@ -4357,6 +4505,1602 @@ function resetProvider() {
4357
4505
  container = null;
4358
4506
  }
4359
4507
 
4508
+ // src/content/domain/calculations/ContentCreditCost.ts
4509
+ var CREDIT_COSTS = {
4510
+ "text-to-image": { standard: 1, hd: 2, "4k": 4 },
4511
+ "image-to-image": { standard: 1, hd: 2, "4k": 4 },
4512
+ "image-to-video": { standard: 3, hd: 5, "4k": 8 },
4513
+ "text-to-video": { standard: 5, hd: 8, "4k": 12 }
4514
+ };
4515
+ var DEFAULT_CREDIT_COST = 0;
4516
+ function toQualityTier(quality) {
4517
+ if (quality === "4k") return "4k";
4518
+ if (quality === "hd" || quality === "1080p") return "hd";
4519
+ return "standard";
4520
+ }
4521
+ function calculateContentCreditCost(type, quality = "standard") {
4522
+ const tierCosts = CREDIT_COSTS[type];
4523
+ if (!tierCosts) return DEFAULT_CREDIT_COST;
4524
+ return tierCosts[toQualityTier(quality)] ?? tierCosts.standard;
4525
+ }
4526
+
4527
+ // src/content/domain/limits/ImageFormatOptions.ts
4528
+ var ASPECT_RATIO_OPTIONS = [
4529
+ { value: "1:1", label: "Square", width: 1024, height: 1024 },
4530
+ { value: "16:9", label: "Landscape", width: 1920, height: 1080 },
4531
+ { value: "9:16", label: "Portrait", width: 1080, height: 1920 },
4532
+ { value: "4:3", label: "Standard", width: 1440, height: 1080 },
4533
+ { value: "3:4", label: "Tall", width: 1080, height: 1440 }
4534
+ ];
4535
+ var IMAGE_QUALITY_OPTIONS = [
4536
+ { value: "standard", label: "Standard", description: "1024x1024, good quality", resolution: "1024x1024" },
4537
+ { value: "hd", label: "HD", description: "1920x1080, high quality", resolution: "1920x1080" },
4538
+ { value: "4k", label: "4K", description: "3840x2160, ultra quality", resolution: "3840x2160" }
4539
+ ];
4540
+ var VIDEO_QUALITY_OPTIONS = [
4541
+ { value: "720p", label: "720p HD", resolution: "1280x720" },
4542
+ { value: "1080p", label: "1080p Full HD", resolution: "1920x1080" },
4543
+ { value: "4k", label: "4K Ultra HD", resolution: "3840x2160" }
4544
+ ];
4545
+ var VIDEO_DURATION_OPTIONS_SECONDS = [2, 3, 4, 5, 6, 7, 8, 9, 10];
4546
+ var MOTION_OPTIONS = [
4547
+ { value: "slow", label: "Slow", description: "Gentle movement" },
4548
+ { value: "medium", label: "Medium", description: "Balanced motion" },
4549
+ { value: "fast", label: "Fast", description: "Dynamic movement" }
4550
+ ];
4551
+ var STYLE_PRESETS = [
4552
+ { value: "realistic", label: "Realistic", promptFragment: "realistic, photograph, detailed" },
4553
+ { value: "artistic", label: "Artistic", promptFragment: "artistic, painting, creative" },
4554
+ { value: "anime", label: "Anime", promptFragment: "anime style, manga, vibrant colors" },
4555
+ { value: "cinematic", label: "Cinematic", promptFragment: "cinematic, movie scene, dramatic lighting" },
4556
+ { value: "vintage", label: "Vintage", promptFragment: "vintage, retro, film grain" },
4557
+ { value: "fantasy", label: "Fantasy", promptFragment: "fantasy, magical, ethereal" },
4558
+ { value: "minimalist", label: "Minimalist", promptFragment: "minimalist, clean, simple" },
4559
+ { value: "abstract", label: "Abstract", promptFragment: "abstract, geometric, artistic" }
4560
+ ];
4561
+
4562
+ // src/content/domain/calculations/AspectRatioDimensions.ts
4563
+ var FALLBACK_DIMENSIONS = { width: 1024, height: 1024 };
4564
+ function resolveDimensionsForAspectRatio(ratio) {
4565
+ const match = ASPECT_RATIO_OPTIONS.find((option) => option.value === ratio);
4566
+ return match ? { width: match.width, height: match.height } : FALLBACK_DIMENSIONS;
4567
+ }
4568
+
4569
+ // src/content/domain/constants/GenerationConstants.ts
4570
+ var ASPECT_RATIO_VALUES = ASPECT_RATIO_OPTIONS.map((o) => o.value);
4571
+ var IMAGE_QUALITY_VALUES = IMAGE_QUALITY_OPTIONS.map((o) => o.value);
4572
+ var VIDEO_QUALITY_VALUES = VIDEO_QUALITY_OPTIONS.map((o) => o.value);
4573
+ var VIDEO_DURATION_OPTIONS = VIDEO_DURATION_OPTIONS_SECONDS;
4574
+ var getDimensionsForAspectRatio = resolveDimensionsForAspectRatio;
4575
+ var getRecommendedQuality = recommendQualityForAspectRatio;
4576
+
4577
+ // src/content/domain/limits/ValidationLimits.ts
4578
+ var ValidationLimits = {
4579
+ TOPIC: {
4580
+ MIN: 1,
4581
+ MAX: 500
4582
+ },
4583
+ CONTENT: {
4584
+ MIN: 1,
4585
+ MAX: 1e4
4586
+ },
4587
+ KEYWORD: {
4588
+ MAX_COUNT: 20,
4589
+ MAX_LENGTH: 50
4590
+ },
4591
+ DURATION_SECONDS: {
4592
+ MIN: 10,
4593
+ MAX: 600
4594
+ },
4595
+ CALENDAR_DAYS: {
4596
+ MIN: 1,
4597
+ MAX: 90
4598
+ },
4599
+ HASHTAG_COUNT: {
4600
+ MIN: 1,
4601
+ MAX: 50
4602
+ },
4603
+ SEO_CONTENT_PREVIEW: 500,
4604
+ BLOG_PROMPT_PREVIEW: 1e3,
4605
+ AB_VARIANT_CONTENT_PREVIEW: 200
4606
+ };
4607
+
4608
+ // src/content/domain/limits/SpeechPaceConfig.ts
4609
+ var SpeechPaceConfig = {
4610
+ WORDS_PER_SECOND: 2.5,
4611
+ TOKENS_PER_SECOND: 10,
4612
+ JSON_TOKEN_OVERHEAD: 500,
4613
+ TOKENS_PER_CALENDAR_DAY: 150
4614
+ };
4615
+
4616
+ // src/content/domain/calculations/SpeechPaceEstimate.ts
4617
+ function estimateWordCountForDuration(durationSeconds) {
4618
+ return Math.floor(durationSeconds * SpeechPaceConfig.WORDS_PER_SECOND);
4619
+ }
4620
+ function estimateTokenBudgetForDuration(durationSeconds) {
4621
+ return durationSeconds * SpeechPaceConfig.TOKENS_PER_SECOND + SpeechPaceConfig.JSON_TOKEN_OVERHEAD;
4622
+ }
4623
+ function estimateTokenBudgetForCalendarDays(days) {
4624
+ return days * SpeechPaceConfig.TOKENS_PER_CALENDAR_DAY + SpeechPaceConfig.JSON_TOKEN_OVERHEAD;
4625
+ }
4626
+
4627
+ // src/content/domain/calculations/SeoKeywordDensity.ts
4628
+ function countKeywordOccurrences(content, keyword) {
4629
+ if (!keyword) return 0;
4630
+ const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4631
+ const matches = content.match(new RegExp(escaped, "gi"));
4632
+ return matches ? matches.length : 0;
4633
+ }
4634
+ function countWordsInContent(content) {
4635
+ const tokens = content.split(/\s+/).filter(Boolean);
4636
+ return tokens.length || 1;
4637
+ }
4638
+ function calculateKeywordDensity(content, keyword) {
4639
+ const totalWords = countWordsInContent(content);
4640
+ const occurrences = countKeywordOccurrences(content, keyword);
4641
+ return occurrences / totalWords * 100;
4642
+ }
4643
+
4644
+ // src/content/domain/calculations/AbTestImprovementRatio.ts
4645
+ function calculateImprovementPercentage(scoreA, scoreB) {
4646
+ const higher = Math.max(scoreA, scoreB);
4647
+ const lower = Math.min(scoreA, scoreB);
4648
+ if (lower <= 0) {
4649
+ return "100";
4650
+ }
4651
+ const delta = (higher / lower - 1) * 100;
4652
+ return Math.abs(delta).toFixed(0);
4653
+ }
4654
+ function selectHigherScoreVariantId(scoreA, scoreB, idA = "A", idB = "B") {
4655
+ return scoreA > scoreB ? idA : idB;
4656
+ }
4657
+
4658
+ // src/content/domain/calculations/ContentCalendarDateOffset.ts
4659
+ var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
4660
+ function resolveCalendarEntryDate(dayIndex, baseTimestamp = Date.now()) {
4661
+ return new Date(baseTimestamp + (dayIndex - 1) * ONE_DAY_MS);
4662
+ }
4663
+
4664
+ // src/content/domain/validation/TopicTextValidator.ts
4665
+ function validateTopicText(topic) {
4666
+ if (typeof topic !== "string" || topic.trim().length < ValidationLimits.TOPIC.MIN) {
4667
+ throw new ValidationError("Topic cannot be empty");
4668
+ }
4669
+ const trimmed = topic.trim();
4670
+ if (trimmed.length > ValidationLimits.TOPIC.MAX) {
4671
+ throw new ValidationError(`Topic exceeds maximum length of ${ValidationLimits.TOPIC.MAX} characters`);
4672
+ }
4673
+ return trimmed;
4674
+ }
4675
+
4676
+ // src/content/domain/validation/ContentBodyValidator.ts
4677
+ function validateContentBody(content) {
4678
+ if (typeof content !== "string" || content.trim().length < ValidationLimits.CONTENT.MIN) {
4679
+ throw new ValidationError("Content cannot be empty");
4680
+ }
4681
+ const trimmed = content.trim();
4682
+ if (trimmed.length > ValidationLimits.CONTENT.MAX) {
4683
+ throw new ValidationError(`Content exceeds maximum length of ${ValidationLimits.CONTENT.MAX} characters`);
4684
+ }
4685
+ return trimmed;
4686
+ }
4687
+ function truncateForSeoPreview(content) {
4688
+ return content.substring(0, ValidationLimits.SEO_CONTENT_PREVIEW);
4689
+ }
4690
+ function truncateForBlogPromptPreview(content) {
4691
+ return content.substring(0, ValidationLimits.BLOG_PROMPT_PREVIEW);
4692
+ }
4693
+ function truncateForAbVariantPreview(content) {
4694
+ return content.substring(0, ValidationLimits.AB_VARIANT_CONTENT_PREVIEW);
4695
+ }
4696
+
4697
+ // src/content/domain/validation/KeywordListValidator.ts
4698
+ function validateKeywordList(keywords) {
4699
+ if (!Array.isArray(keywords)) {
4700
+ throw new ValidationError("Keywords must be an array");
4701
+ }
4702
+ if (keywords.length > ValidationLimits.KEYWORD.MAX_COUNT) {
4703
+ throw new ValidationError(`Cannot process more than ${ValidationLimits.KEYWORD.MAX_COUNT} keywords`);
4704
+ }
4705
+ return keywords.filter((k) => typeof k === "string" && k.trim().length > 0).map((keyword) => {
4706
+ const trimmed = keyword.trim();
4707
+ if (trimmed.length > ValidationLimits.KEYWORD.MAX_LENGTH) {
4708
+ const preview = trimmed.substring(0, 20);
4709
+ throw new ValidationError(
4710
+ `Keyword "${preview}..." exceeds maximum length of ${ValidationLimits.KEYWORD.MAX_LENGTH} characters`
4711
+ );
4712
+ }
4713
+ return trimmed;
4714
+ });
4715
+ }
4716
+
4717
+ // src/content/domain/validation/DurationSecondsValidator.ts
4718
+ function validateDurationSeconds(duration) {
4719
+ if (!Number.isFinite(duration)) {
4720
+ throw new ValidationError(
4721
+ `Duration must be between ${ValidationLimits.DURATION_SECONDS.MIN} and ${ValidationLimits.DURATION_SECONDS.MAX} seconds`
4722
+ );
4723
+ }
4724
+ if (duration < ValidationLimits.DURATION_SECONDS.MIN || duration > ValidationLimits.DURATION_SECONDS.MAX) {
4725
+ throw new ValidationError(
4726
+ `Duration must be between ${ValidationLimits.DURATION_SECONDS.MIN} and ${ValidationLimits.DURATION_SECONDS.MAX} seconds`
4727
+ );
4728
+ }
4729
+ return duration;
4730
+ }
4731
+
4732
+ // src/content/domain/validation/CalendarDaysValidator.ts
4733
+ function validateCalendarDays(days) {
4734
+ if (!Number.isFinite(days)) {
4735
+ throw new ValidationError(
4736
+ `Days must be between ${ValidationLimits.CALENDAR_DAYS.MIN} and ${ValidationLimits.CALENDAR_DAYS.MAX}`
4737
+ );
4738
+ }
4739
+ if (days < ValidationLimits.CALENDAR_DAYS.MIN || days > ValidationLimits.CALENDAR_DAYS.MAX) {
4740
+ throw new ValidationError(
4741
+ `Days must be between ${ValidationLimits.CALENDAR_DAYS.MIN} and ${ValidationLimits.CALENDAR_DAYS.MAX}`
4742
+ );
4743
+ }
4744
+ return days;
4745
+ }
4746
+
4747
+ // src/content/domain/validation/HashtagCountValidator.ts
4748
+ function validateHashtagCount(count) {
4749
+ if (!Number.isFinite(count)) {
4750
+ throw new ValidationError(
4751
+ `Hashtag count must be between ${ValidationLimits.HASHTAG_COUNT.MIN} and ${ValidationLimits.HASHTAG_COUNT.MAX}`
4752
+ );
4753
+ }
4754
+ if (count < ValidationLimits.HASHTAG_COUNT.MIN || count > ValidationLimits.HASHTAG_COUNT.MAX) {
4755
+ throw new ValidationError(
4756
+ `Hashtag count must be between ${ValidationLimits.HASHTAG_COUNT.MIN} and ${ValidationLimits.HASHTAG_COUNT.MAX}`
4757
+ );
4758
+ }
4759
+ return Math.floor(count);
4760
+ }
4761
+
4762
+ // src/content/domain/utils/IdGenerator.ts
4763
+ function generateId(prefix) {
4764
+ const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
4765
+ return prefix ? `${prefix}-${id}` : id;
4766
+ }
4767
+
4768
+ // src/content/application/prompts/PromptBuilder.ts
4769
+ var PromptBuilder = class {
4770
+ static blogPost(input) {
4771
+ return `
4772
+ As an expert SEO Content Strategist and Copywriter, generate a high-quality, comprehensive blog post.
4773
+
4774
+ TOPIC: ${input.topic}
4775
+ TYPE: ${input.blogType}
4776
+ KEYWORDS: ${input.keywords.join(", ")}
4777
+ TONE: ${input.tone}
4778
+ TARGET AUDIENCE: ${input.targetAudience}
4779
+ WORD COUNT: ${input.wordCount}
4780
+ LANGUAGE: ${input.language || "English"}
4781
+
4782
+ OPTIMIZATION REQUIREMENTS:
4783
+ - SEO Optimization: ${input.seoOptimization ? "ON (Include semantic keywords, LSI, and proper H1-H3 structure)" : "OFF"}
4784
+ - Include Schema Markup: ${input.includeSchema ? "ON (Generate JSON-LD for this blog type)" : "OFF"}
4785
+ - Image Suggestions: ${input.includeImages ? "ON (Provide DALL-E/Midjourney style prompts for contextually relevant images)" : "OFF"}
4786
+
4787
+ RESPONSE FORMAT (STRICT JSON ONLY):
4788
+ {
4789
+ "title": "Compelling, Click-worthy Title",
4790
+ "content": "Full markdown-formatted blog content with headers",
4791
+ "metaDescription": "SEO-optimized description (150-160 chars)",
4792
+ "seoScore": 0-100,
4793
+ "readabilityScore": 0-100,
4794
+ "keywords": ["list", "of", "optimized", "keywords"],
4795
+ "performance": {
4796
+ "readingTime": minutes (number),
4797
+ "shareability": 0-10 (number),
4798
+ "seoRank": 0-10 (number)
4799
+ },
4800
+ "schemaMarkup": "JSON-LD string (if requested)",
4801
+ "imagePrompts": ["Prompt 1", "Prompt 2"] (if requested)
4802
+ }
4803
+ `;
4804
+ }
4805
+ static socialPost(input) {
4806
+ return `
4807
+ Write a ${input.tone} social media post about: ${input.topic}
4808
+
4809
+ Platform: ${input.platform}
4810
+ Style: ${input.style}
4811
+ Maximum length: ${input.maxLength} characters
4812
+ ${input.includeHashtags ? "Include relevant hashtags" : "No hashtags"}
4813
+ ${input.includeCallToAction ? "Include a call-to-action" : ""}
4814
+
4815
+ Requirements:
4816
+ - Platform-optimized formatting
4817
+ - Engaging hook in first sentence
4818
+ - Natural, conversational tone
4819
+ - Platform-appropriate emoji usage
4820
+
4821
+ Generate the post in JSON format:
4822
+ {
4823
+ "content": "The post content",
4824
+ "hashtags": ["hashtag1", "hashtag2"],
4825
+ "emojis": ["emoji1", "emoji2"],
4826
+ "estimatedEngagement": 0-100
4827
+ }
4828
+ `;
4829
+ }
4830
+ static videoScript(input) {
4831
+ return `
4832
+ Create a ${input.tone} video script about: ${input.topic}
4833
+
4834
+ Duration: ${input.duration} seconds
4835
+ Target Audience: ${input.targetAudience}
4836
+ Include Visual Cues: ${input.includeVisuals ? "Yes" : "No"}
4837
+ Include CTA: ${input.includeCallToAction ? "Yes" : "No"}
4838
+
4839
+ Approximate word count: ${estimateWordCountForDuration(input.duration)} words
4840
+
4841
+ Generate in JSON format:
4842
+ {
4843
+ "script": "Full script with dialogue and narration",
4844
+ "visualCues": ["Visual cue 1", "Visual cue 2"],
4845
+ "callToAction": "Call to action text (if requested)",
4846
+ "estimatedEngagement": 0-100
4847
+ }
4848
+ `;
4849
+ }
4850
+ static voiceScript(input) {
4851
+ return `
4852
+ Create a ${input.emotion} voice script about: ${input.topic}
4853
+
4854
+ Duration: ${input.duration} seconds
4855
+ Emotion: ${input.emotion}
4856
+
4857
+ The script should:
4858
+ - Evoke the specified emotion
4859
+ - Be suitable for voice recording with emotion control
4860
+ - Include natural speech patterns
4861
+ - Have appropriate pacing for the emotion
4862
+ - Include emotional cues in brackets where needed
4863
+
4864
+ Approximate word count: ${estimateWordCountForDuration(input.duration)} words
4865
+
4866
+ Generate in JSON format:
4867
+ {
4868
+ "script": "Full script with emotional cues",
4869
+ "visualCues": ["Visual cue 1", "Visual cue 2"],
4870
+ "estimatedEngagement": 0-100
4871
+ }
4872
+ `;
4873
+ }
4874
+ static contentCalendar(niche, days) {
4875
+ return `
4876
+ Generate a ${days}-day content calendar for: ${niche}
4877
+
4878
+ For each day, provide:
4879
+ - Topic
4880
+ - Content type (blog, video, social, email, caption, script)
4881
+ - Best platform for this content
4882
+ - Brief description
4883
+ - Estimated engagement (0-100)
4884
+ - Priority level (high, medium, low)
4885
+
4886
+ Format as JSON array:
4887
+ [
4888
+ {
4889
+ "day": 1,
4890
+ "topic": "...",
4891
+ "contentType": "blog|video|social|email|caption|script",
4892
+ "platform": "twitter|linkedin|instagram|threads|tiktok|facebook",
4893
+ "description": "...",
4894
+ "estimatedEngagement": 0-100,
4895
+ "priority": "high|medium|low"
4896
+ }
4897
+ ]
4898
+
4899
+ Requirements:
4900
+ - Mix of content types across platforms
4901
+ - Platform-appropriate suggestions
4902
+ - Engaging, varied topics
4903
+ - Realistic posting schedule
4904
+ `;
4905
+ }
4906
+ static sentiment(content) {
4907
+ return `
4908
+ Analyze the sentiment of this social media post:
4909
+
4910
+ "${content}"
4911
+
4912
+ Provide analysis in JSON format:
4913
+ {
4914
+ "sentiment": "positive|neutral|negative",
4915
+ "confidence": 0-1,
4916
+ "emotions": [
4917
+ {"emotion": "happy|excited|calm|sad|angry|surprised|fearful|disgusted|neutral", "score": 0-1}
4918
+ ]
4919
+ }
4920
+
4921
+ Return only the JSON:
4922
+ `;
4923
+ }
4924
+ static contentAnalysis(content) {
4925
+ return `
4926
+ Analyze this content:
4927
+
4928
+ "${content}"
4929
+
4930
+ Provide analysis in JSON format:
4931
+ {
4932
+ "keywords": ["keyword1", "keyword2", ...],
4933
+ "entities": ["entity1", "entity2", ...],
4934
+ "suggestedImprovements": ["suggestion1", ...],
4935
+ "readabilityScore": 0-100,
4936
+ "estimatedEngagement": 0-100
4937
+ }
4938
+
4939
+ Return only the JSON:
4940
+ `;
4941
+ }
4942
+ static seoOptimization(content, keywords) {
4943
+ return `
4944
+ Optimize this content for SEO:
4945
+
4946
+ Content: "${content}"
4947
+ Keywords: ${keywords.join(", ")}
4948
+
4949
+ Tasks:
4950
+ 1. Incorporate keywords naturally
4951
+ 2. Improve engagement potential
4952
+ 3. Add relevant call-to-action if appropriate
4953
+ 4. Optimize for readability
4954
+ 5. Enhance heading structure
4955
+
4956
+ Return JSON:
4957
+ {
4958
+ "optimized": "...",
4959
+ "score": 0-100,
4960
+ "suggestions": ["suggestion1", ...],
4961
+ "addedKeywords": ["keyword1", ...],
4962
+ "removedFillerWords": number,
4963
+ "readabilityImprovements": ["improvement1", ...],
4964
+ "metaDescription": "SEO-optimized meta description",
4965
+ "titleSuggestions": ["title1", ...]
4966
+ }
4967
+
4968
+ Return only the JSON:
4969
+ `;
4970
+ }
4971
+ static seoScore(content, keywords) {
4972
+ return `
4973
+ Calculate SEO score for this content:
4974
+
4975
+ Content: "${content}"
4976
+ Keywords: ${keywords.join(", ")}
4977
+
4978
+ Provide breakdown in JSON:
4979
+ {
4980
+ "keywordDensity": 0-100,
4981
+ "readabilityScore": 0-100,
4982
+ "titleOptimization": 0-100,
4983
+ "metaDescription": 0-100,
4984
+ "headingStructure": 0-100,
4985
+ "internalLinking": 0-100,
4986
+ "overall": 0-100
4987
+ }
4988
+
4989
+ Return only the JSON:
4990
+ `;
4991
+ }
4992
+ static abTestVariant(input) {
4993
+ return `
4994
+ Predict performance for this content variant:
4995
+
4996
+ Content: "${input.content}"
4997
+ Content Type: ${input.contentType}
4998
+ Tone: ${input.tone}
4999
+ Target Audience: ${input.targetAudience}
5000
+ Platform: ${input.platform}
5001
+ Goals: ${input.goals.join(", ")}
5002
+
5003
+ Provide prediction in JSON:
5004
+ {
5005
+ "predictedEngagement": 0-100,
5006
+ "predictedCTR": 0-100,
5007
+ "predictedConversions": 0-100,
5008
+ "confidence": 0-1,
5009
+ "reasoning": "...",
5010
+ "strengths": ["strength1", ...],
5011
+ "weaknesses": ["weakness1", ...],
5012
+ "suggestions": ["suggestion1", ...]
5013
+ }
5014
+
5015
+ Return only the JSON:
5016
+ `;
5017
+ }
5018
+ static hashtags(content, count) {
5019
+ return `
5020
+ Generate ${count} relevant hashtags for this content:
5021
+
5022
+ "${content}"
5023
+
5024
+ Requirements:
5025
+ - Mix of popular and niche hashtags
5026
+ - Industry-specific when possible
5027
+ - Trending if relevant
5028
+ - No duplicated words
5029
+ - Format: #hashtag (one per line)
5030
+
5031
+ Return only the hashtags:
5032
+ `;
5033
+ }
5034
+ static imagePrompt(description, style) {
5035
+ return `
5036
+ Generate a detailed AI image generation prompt for:
5037
+
5038
+ Description: ${description}
5039
+ Style: ${style || "realistic"}
5040
+
5041
+ Create a detailed, descriptive prompt that includes:
5042
+ - Subject details
5043
+ - Mood and atmosphere
5044
+ - Lighting
5045
+ - Composition
5046
+ - Art style reference
5047
+ - Technical specifications (aspect ratio, quality)
5048
+
5049
+ The prompt should be optimized for models like Midjourney, DALL-E, or Stable Diffusion.
5050
+
5051
+ Generate the prompt:
5052
+ `;
5053
+ }
5054
+ static blogImagePrompts(blogContent) {
5055
+ return `
5056
+ Analyze this blog content and generate 3-5 detailed image prompts for illustrative images:
5057
+
5058
+ Blog Content: "${blogContent}"
5059
+
5060
+ For each image prompt, provide:
5061
+ - Subject description
5062
+ - Mood and atmosphere
5063
+ - Style reference
5064
+ - Composition details
5065
+ - Technical specs
5066
+
5067
+ Return as JSON array:
5068
+ ["prompt1", "prompt2", "prompt3", ...]
5069
+
5070
+ Return only the JSON:
5071
+ `;
5072
+ }
5073
+ static maxTokensForDuration(durationSeconds) {
5074
+ return estimateTokenBudgetForDuration(durationSeconds);
5075
+ }
5076
+ };
5077
+
5078
+ // src/content/application/services/BlogService.ts
5079
+ var BlogService = class {
5080
+ constructor(executor) {
5081
+ this.executor = executor;
5082
+ }
5083
+ executor;
5084
+ async generateBlogPost(request) {
5085
+ const topic = validateTopicText(request.topic);
5086
+ const keywords = validateKeywordList(request.targetKeywords);
5087
+ const prompt = PromptBuilder.blogPost({
5088
+ topic,
5089
+ blogType: request.blogType,
5090
+ keywords,
5091
+ tone: request.tone,
5092
+ targetAudience: request.targetAudience,
5093
+ wordCount: request.wordCount,
5094
+ language: request.language,
5095
+ seoOptimization: request.seoOptimization,
5096
+ includeSchema: request.includeSchema,
5097
+ includeImages: request.includeImages
5098
+ });
5099
+ const parsed = await this.executor.runJson(
5100
+ prompt,
5101
+ {},
5102
+ { maxTokens: request.wordCount * 2, temperature: 0.7 }
5103
+ );
5104
+ return {
5105
+ id: generateId("blog"),
5106
+ ...parsed,
5107
+ blogType: request.blogType,
5108
+ tone: request.tone,
5109
+ wordCount: request.wordCount,
5110
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5111
+ };
5112
+ }
5113
+ };
5114
+
5115
+ // src/content/infrastructure/utils/requestQueue.ts
5116
+ var RequestPriority = /* @__PURE__ */ ((RequestPriority2) => {
5117
+ RequestPriority2[RequestPriority2["LOW"] = 0] = "LOW";
5118
+ RequestPriority2[RequestPriority2["NORMAL"] = 1] = "NORMAL";
5119
+ RequestPriority2[RequestPriority2["HIGH"] = 2] = "HIGH";
5120
+ RequestPriority2[RequestPriority2["URGENT"] = 3] = "URGENT";
5121
+ return RequestPriority2;
5122
+ })(RequestPriority || {});
5123
+ async function batchRequest(requests, options = {}) {
5124
+ const {
5125
+ concurrency = 3,
5126
+ stopOnError = false
5127
+ } = options;
5128
+ const results = [];
5129
+ const errors = [];
5130
+ for (let i = 0; i < requests.length; i += concurrency) {
5131
+ const batch = requests.slice(i, i + concurrency);
5132
+ const batchResults = await Promise.allSettled(
5133
+ batch.map((fn) => fn())
5134
+ );
5135
+ for (const result of batchResults) {
5136
+ if (result.status === "fulfilled") {
5137
+ results.push(result.value);
5138
+ } else {
5139
+ errors.push(result.reason);
5140
+ if (stopOnError) {
5141
+ throw new Error(
5142
+ `Batch request failed: ${errors[0].message}`
5143
+ );
5144
+ }
5145
+ }
5146
+ }
5147
+ }
5148
+ if (errors.length > 0 && !stopOnError) {
5149
+ console.warn(`${errors.length} requests failed in batch`);
5150
+ }
5151
+ return results;
5152
+ }
5153
+
5154
+ // src/content/application/services/SocialService.ts
5155
+ var MULTI_PLATFORMS = [
5156
+ "twitter",
5157
+ "linkedin",
5158
+ "instagram",
5159
+ "threads",
5160
+ "tiktok"
5161
+ ];
5162
+ var DEFAULT_SOCIAL_RESULT = {
5163
+ content: "",
5164
+ platform: "twitter",
5165
+ hashtags: [],
5166
+ emojis: [],
5167
+ characterCount: 0,
5168
+ estimatedEngagement: 0
5169
+ };
5170
+ var SocialService = class {
5171
+ constructor(executor) {
5172
+ this.executor = executor;
5173
+ }
5174
+ executor;
5175
+ async generateSocialContent(request) {
5176
+ const topic = validateTopicText(request.topic);
5177
+ const spec = resolvePlatformSpec(request.platform);
5178
+ const maxLength = resolveMaxContentLength(request.platform, request.maxLength);
5179
+ const prompt = PromptBuilder.socialPost({
5180
+ topic,
5181
+ platform: request.platform,
5182
+ tone: request.tone,
5183
+ style: spec.style,
5184
+ maxLength,
5185
+ includeHashtags: request.hashtags !== false,
5186
+ includeCallToAction: Boolean(request.includeCallToAction)
5187
+ });
5188
+ const parsed = await this.executor.runJson(
5189
+ prompt,
5190
+ { ...DEFAULT_SOCIAL_RESULT, platform: request.platform },
5191
+ { maxTokens: 500, temperature: 0.8 }
5192
+ );
5193
+ return {
5194
+ ...parsed,
5195
+ platform: request.platform,
5196
+ characterCount: parsed.content?.length ?? 0
5197
+ };
5198
+ }
5199
+ async generateForAllPlatforms(topic, tone) {
5200
+ const requests = MULTI_PLATFORMS.map(
5201
+ (platform) => () => this.generateSocialContent({ topic, platform, tone })
5202
+ );
5203
+ try {
5204
+ return await batchRequest(requests, { concurrency: 2, stopOnError: false });
5205
+ } catch (error) {
5206
+ console.warn("Batch execution failed, falling back to sequential:", error);
5207
+ const results = [];
5208
+ for (const platform of MULTI_PLATFORMS) {
5209
+ try {
5210
+ results.push(await this.generateSocialContent({ topic, platform, tone }));
5211
+ } catch (err) {
5212
+ console.error(`Failed to generate for ${platform}:`, err);
5213
+ }
5214
+ }
5215
+ return results;
5216
+ }
5217
+ }
5218
+ async generateHashtags(content, count) {
5219
+ const validated = validateHashtagCount(count);
5220
+ const prompt = PromptBuilder.hashtags(content, validated);
5221
+ const response = await this.executor.runText(prompt, { maxTokens: 200, temperature: 0.7 });
5222
+ const hashtags = response.match(/#[\w-]+/g) || [];
5223
+ return hashtags.slice(0, validated);
5224
+ }
5225
+ optimizeHashtags(hashtags, platform) {
5226
+ const budget = resolveHashtagBudget(platform);
5227
+ return rankAndTrimHashtags(hashtags, budget);
5228
+ }
5229
+ };
5230
+
5231
+ // src/content/application/services/VideoScriptService.ts
5232
+ var buildScriptResult = (parsed, duration) => {
5233
+ const script = parsed.script ?? "";
5234
+ if (!script) {
5235
+ throw new Error("AI response missing script field");
5236
+ }
5237
+ const wordCount = script.trim().length === 0 ? 0 : script.trim().split(/\s+/).length;
5238
+ return {
5239
+ ...parsed,
5240
+ script,
5241
+ duration,
5242
+ wordCount
5243
+ };
5244
+ };
5245
+ var VideoScriptService = class {
5246
+ constructor(executor) {
5247
+ this.executor = executor;
5248
+ }
5249
+ executor;
5250
+ async generateVideoScript(request) {
5251
+ const topic = validateTopicText(request.topic);
5252
+ const duration = validateDurationSeconds(request.duration);
5253
+ const prompt = PromptBuilder.videoScript({
5254
+ topic,
5255
+ tone: request.tone,
5256
+ duration,
5257
+ targetAudience: request.targetAudience,
5258
+ includeVisuals: request.includeVisuals,
5259
+ includeCallToAction: request.includeCallToAction
5260
+ });
5261
+ const parsed = await this.executor.runJson(
5262
+ prompt,
5263
+ { script: "", visualCues: [], duration, wordCount: 0, estimatedEngagement: 0 },
5264
+ { maxTokens: estimateTokenBudgetForDuration(duration), temperature: 0.8 }
5265
+ );
5266
+ return buildScriptResult(parsed, duration);
5267
+ }
5268
+ async generateVoiceScript(topic, emotion, duration) {
5269
+ const validatedTopic = validateTopicText(topic);
5270
+ const validatedDuration = validateDurationSeconds(duration);
5271
+ const prompt = PromptBuilder.voiceScript({
5272
+ topic: validatedTopic,
5273
+ emotion,
5274
+ duration: validatedDuration
5275
+ });
5276
+ const parsed = await this.executor.runJson(
5277
+ prompt,
5278
+ { script: "", visualCues: [], duration: validatedDuration, wordCount: 0, estimatedEngagement: 0 },
5279
+ { maxTokens: estimateTokenBudgetForDuration(validatedDuration), temperature: 0.8 }
5280
+ );
5281
+ return buildScriptResult(parsed, validatedDuration);
5282
+ }
5283
+ };
5284
+
5285
+ // src/content/application/services/AnalysisService.ts
5286
+ var DEFAULT_SENTIMENT = {
5287
+ sentiment: "neutral",
5288
+ confidence: 0.5,
5289
+ emotions: []
5290
+ };
5291
+ var DEFAULT_ANALYSIS = {
5292
+ sentiment: DEFAULT_SENTIMENT,
5293
+ keywords: [],
5294
+ entities: [],
5295
+ suggestedImprovements: [],
5296
+ readabilityScore: 50,
5297
+ estimatedEngagement: 50
5298
+ };
5299
+ var AnalysisService = class {
5300
+ constructor(executor) {
5301
+ this.executor = executor;
5302
+ }
5303
+ executor;
5304
+ async analyzeSentiment(content) {
5305
+ const validated = validateContentBody(content);
5306
+ const prompt = PromptBuilder.sentiment(validated);
5307
+ const result = await this.executor.runJson(
5308
+ prompt,
5309
+ DEFAULT_SENTIMENT,
5310
+ { maxTokens: 200, temperature: 0.3 }
5311
+ );
5312
+ return {
5313
+ ...result,
5314
+ confidence: clampConfidenceToValidRange(result.confidence)
5315
+ };
5316
+ }
5317
+ async analyzeContent(request) {
5318
+ const validated = validateContentBody(request.content);
5319
+ const sentimentResult = await this.analyzeSentiment(validated);
5320
+ const prompt = PromptBuilder.contentAnalysis(validated);
5321
+ const parsed = await this.executor.runJson(
5322
+ prompt,
5323
+ { ...DEFAULT_ANALYSIS, sentiment: sentimentResult },
5324
+ { maxTokens: 300, temperature: 0.5 }
5325
+ );
5326
+ return {
5327
+ sentiment: sentimentResult,
5328
+ keywords: parsed.keywords ?? [],
5329
+ entities: parsed.entities ?? [],
5330
+ suggestedImprovements: parsed.suggestedImprovements ?? [],
5331
+ readabilityScore: clampScoreToValidRange(parsed.readabilityScore),
5332
+ estimatedEngagement: clampScoreToValidRange(parsed.estimatedEngagement)
5333
+ };
5334
+ }
5335
+ };
5336
+
5337
+ // src/content/application/services/SeoService.ts
5338
+ var DEFAULT_SEO_SCORE = {
5339
+ keywordDensity: 50,
5340
+ readabilityScore: 50,
5341
+ titleOptimization: 50,
5342
+ metaDescription: 50,
5343
+ headingStructure: 50,
5344
+ internalLinking: 50,
5345
+ overall: 50
5346
+ };
5347
+ var UNDERUTILIZED_SUGGESTION = "Consider using this keyword more frequently";
5348
+ var SeoService = class {
5349
+ constructor(executor) {
5350
+ this.executor = executor;
5351
+ }
5352
+ executor;
5353
+ async optimizeSEO(request) {
5354
+ const content = validateContentBody(request.content);
5355
+ const keywords = validateKeywordList(request.keywords);
5356
+ const prompt = PromptBuilder.seoOptimization(content, keywords);
5357
+ const result = await this.executor.runJson(
5358
+ prompt,
5359
+ {
5360
+ optimized: content,
5361
+ score: 50,
5362
+ suggestions: [],
5363
+ addedKeywords: [],
5364
+ removedFillerWords: 0,
5365
+ readabilityImprovements: []
5366
+ },
5367
+ { maxTokens: 1e3, temperature: 0.7 }
5368
+ );
5369
+ return {
5370
+ ...result,
5371
+ score: clampScoreToValidRange(result.score)
5372
+ };
5373
+ }
5374
+ async calculateSEOScore(content, keywords) {
5375
+ const validatedContent = validateContentBody(content);
5376
+ const validatedKeywords = validateKeywordList(keywords);
5377
+ const prompt = PromptBuilder.seoOptimization(
5378
+ truncateForSeoPreview(validatedContent),
5379
+ validatedKeywords
5380
+ );
5381
+ const result = await this.executor.runJson(
5382
+ prompt,
5383
+ DEFAULT_SEO_SCORE,
5384
+ { maxTokens: 200, temperature: 0.3 }
5385
+ );
5386
+ return {
5387
+ keywordDensity: clampScoreToValidRange(result.keywordDensity),
5388
+ readabilityScore: clampScoreToValidRange(result.readabilityScore),
5389
+ titleOptimization: clampScoreToValidRange(result.titleOptimization),
5390
+ metaDescription: clampScoreToValidRange(result.metaDescription),
5391
+ headingStructure: clampScoreToValidRange(result.headingStructure),
5392
+ internalLinking: clampScoreToValidRange(result.internalLinking),
5393
+ overall: clampScoreToValidRange(result.overall)
5394
+ };
5395
+ }
5396
+ analyzeKeywords(content, keywords) {
5397
+ const validatedContent = validateContentBody(content);
5398
+ const validatedKeywords = validateKeywordList(keywords);
5399
+ return validatedKeywords.map((keyword) => {
5400
+ const count = countKeywordOccurrences(validatedContent, keyword);
5401
+ const density = calculateKeywordDensity(validatedContent, keyword);
5402
+ return {
5403
+ keyword,
5404
+ count,
5405
+ density,
5406
+ prominence: classifyKeywordProminence(density),
5407
+ suggestions: isUnderutilizedKeyword(density) ? [UNDERUTILIZED_SUGGESTION] : []
5408
+ };
5409
+ });
5410
+ }
5411
+ };
5412
+
5413
+ // src/content/application/services/ABTestService.ts
5414
+ var DEFAULT_PREDICTION = {
5415
+ variantId: "",
5416
+ predictedEngagement: 0,
5417
+ predictedCTR: 0,
5418
+ predictedConversions: 0,
5419
+ confidence: 0,
5420
+ reasoning: "",
5421
+ strengths: [],
5422
+ weaknesses: [],
5423
+ suggestions: []
5424
+ };
5425
+ var VARIANT_A_ID = "A";
5426
+ var VARIANT_B_ID = "B";
5427
+ var FALLBACK_ENGAGEMENT = 50;
5428
+ var DEFAULT_RECOMMENDATIONS = [
5429
+ "Test with larger audience",
5430
+ "Monitor click-through rates"
5431
+ ];
5432
+ var ABTestService = class {
5433
+ constructor(executor) {
5434
+ this.executor = executor;
5435
+ }
5436
+ executor;
5437
+ async predictABTest(request) {
5438
+ const predictions = [];
5439
+ for (const variant of request.variants) {
5440
+ const prompt = PromptBuilder.abTestVariant({
5441
+ content: truncateForAbVariantPreview(variant.content),
5442
+ contentType: variant.contentType,
5443
+ tone: variant.tone,
5444
+ targetAudience: request.targetAudience,
5445
+ platform: request.platform,
5446
+ goals: request.goals
5447
+ });
5448
+ const parsed = await this.executor.runJson(
5449
+ prompt,
5450
+ { ...DEFAULT_PREDICTION, variantId: variant.id },
5451
+ { maxTokens: 300, temperature: 0.5 }
5452
+ );
5453
+ predictions.push({
5454
+ variantId: variant.id,
5455
+ predictedEngagement: clampScoreToValidRange(parsed.predictedEngagement),
5456
+ predictedCTR: clampScoreToValidRange(parsed.predictedCTR),
5457
+ predictedConversions: clampScoreToValidRange(parsed.predictedConversions),
5458
+ confidence: clampConfidenceToValidRange(parsed.confidence),
5459
+ reasoning: parsed.reasoning ?? "",
5460
+ strengths: parsed.strengths ?? [],
5461
+ weaknesses: parsed.weaknesses ?? [],
5462
+ suggestions: parsed.suggestions ?? []
5463
+ });
5464
+ }
5465
+ return predictions;
5466
+ }
5467
+ async compareVariants(variantA, variantB) {
5468
+ const predictions = await this.predictABTest({
5469
+ variants: [
5470
+ { id: VARIANT_A_ID, content: variantA, contentType: "social", tone: "casual" },
5471
+ { id: VARIANT_B_ID, content: variantB, contentType: "social", tone: "casual" }
5472
+ ],
5473
+ targetAudience: "general",
5474
+ platform: "twitter",
5475
+ goals: ["engagement", "clicks"]
5476
+ });
5477
+ const scoreA = predictions[0]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
5478
+ const scoreB = predictions[1]?.predictedEngagement ?? FALLBACK_ENGAGEMENT;
5479
+ const isTie = scoreA === scoreB;
5480
+ const winner = selectHigherScoreVariantId(scoreA, scoreB, VARIANT_A_ID, VARIANT_B_ID);
5481
+ const improvement = calculateImprovementPercentage(scoreA, scoreB);
5482
+ return {
5483
+ winner,
5484
+ confidence: AB_TEST_DEFAULT_CONFIDENCE,
5485
+ improvement: isTie ? "0%" : `+${improvement}% engagement`,
5486
+ reasoning: isTie ? "Both variants have equal predicted engagement" : winner === VARIANT_A_ID ? "Variant A has more engaging content" : "Variant B has more engaging content",
5487
+ recommendations: [...DEFAULT_RECOMMENDATIONS]
5488
+ };
5489
+ }
5490
+ };
5491
+
5492
+ // src/content/application/services/ImagePromptService.ts
5493
+ var ImagePromptService = class {
5494
+ constructor(executor) {
5495
+ this.executor = executor;
5496
+ }
5497
+ executor;
5498
+ async generateImagePrompt(description, style) {
5499
+ const validated = validateTopicText(description);
5500
+ const prompt = PromptBuilder.imagePrompt(validated, style);
5501
+ return this.executor.runText(prompt, { maxTokens: 300, temperature: 0.8 });
5502
+ }
5503
+ async generateImagePromptsForBlog(blogContent) {
5504
+ const truncated = truncateForBlogPromptPreview(validateContentBody(blogContent));
5505
+ const prompt = PromptBuilder.blogImagePrompts(truncated);
5506
+ const prompts = await this.executor.runJson(prompt, [], {
5507
+ maxTokens: 500,
5508
+ temperature: 0.8
5509
+ });
5510
+ return Array.isArray(prompts) ? prompts : [];
5511
+ }
5512
+ };
5513
+
5514
+ // src/content/application/services/ContentCalendarService.ts
5515
+ var toCalendarEntry = (item) => ({
5516
+ date: resolveCalendarEntryDate(item.day),
5517
+ topic: item.topic,
5518
+ contentType: item.contentType,
5519
+ platform: item.platform,
5520
+ description: item.description,
5521
+ estimatedEngagement: item.estimatedEngagement,
5522
+ priority: item.priority
5523
+ });
5524
+ var ContentCalendarService = class {
5525
+ constructor(executor) {
5526
+ this.executor = executor;
5527
+ }
5528
+ executor;
5529
+ async generateContentCalendar(niche, days) {
5530
+ const validatedNiche = validateTopicText(niche);
5531
+ const validatedDays = validateCalendarDays(days);
5532
+ const prompt = PromptBuilder.contentCalendar(validatedNiche, validatedDays);
5533
+ const data = await this.executor.runJson(
5534
+ prompt,
5535
+ [],
5536
+ {
5537
+ maxTokens: estimateTokenBudgetForCalendarDays(validatedDays),
5538
+ temperature: 0.8
5539
+ }
5540
+ );
5541
+ const items = Array.isArray(data) ? data : Array.isArray(data?.calendar) ? data.calendar : Array.isArray(data?.entries) ? data.entries : [];
5542
+ return items.map(toCalendarEntry);
5543
+ }
5544
+ };
5545
+
5546
+ // src/content/application/services/ProviderServices.ts
5547
+ var wrapProviderError = (operation, error) => {
5548
+ const message = error instanceof Error ? error.message : "Unknown error";
5549
+ return new AIError(`${operation} failed: ${message}`, "PROVIDER_ERROR", error);
5550
+ };
5551
+ var ensureFactory = (factory, operation) => {
5552
+ if (factory.getAllProviders().length === 0) {
5553
+ throw new AIError(
5554
+ `${operation} failed: No providers are registered. Provide a ProviderConfig with at least one enabled provider when constructing AIContentService.`,
5555
+ "PROVIDER_UNAVAILABLE"
5556
+ );
5557
+ }
5558
+ };
5559
+ var MediaGenerationService = class {
5560
+ constructor(providerFactory) {
5561
+ this.providerFactory = providerFactory;
5562
+ }
5563
+ providerFactory;
5564
+ generateImage(request) {
5565
+ ensureFactory(this.providerFactory, "Image generation");
5566
+ return this.providerFactory.generateImage(request).catch((err) => {
5567
+ throw wrapProviderError("Image generation", err);
5568
+ });
5569
+ }
5570
+ generateVideo(request) {
5571
+ ensureFactory(this.providerFactory, "Video generation");
5572
+ return this.providerFactory.generateVideo(request).catch((err) => {
5573
+ throw wrapProviderError("Video generation", err);
5574
+ });
5575
+ }
5576
+ convertImageToVideo(request) {
5577
+ ensureFactory(this.providerFactory, "Image-to-video conversion");
5578
+ return this.providerFactory.imageToVideo(request).catch((err) => {
5579
+ throw wrapProviderError("Image-to-video conversion", err);
5580
+ });
5581
+ }
5582
+ transformVideo(request) {
5583
+ ensureFactory(this.providerFactory, "Video-to-video transformation");
5584
+ return this.providerFactory.videoToVideo(request).catch((err) => {
5585
+ throw wrapProviderError("Video-to-video transformation", err);
5586
+ });
5587
+ }
5588
+ };
5589
+
5590
+ // src/content/infrastructure/utils/jsonParser.ts
5591
+ function repairCommonJsonDefects2(text) {
5592
+ return text.replace(/(:\s*-?\d+(?:\.\d+)?)\s*"/g, "$1").replace(/(:\s*"[^"]*")"/g, "$1");
5593
+ }
5594
+ function safeJSONParse(text, fallback, onError) {
5595
+ try {
5596
+ return JSON.parse(text);
5597
+ } catch (error) {
5598
+ try {
5599
+ return JSON.parse(repairCommonJsonDefects2(text));
5600
+ } catch {
5601
+ }
5602
+ if (error instanceof SyntaxError) {
5603
+ const positionMatch = error.message.match(/position (\d+)/);
5604
+ const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
5605
+ if (position > 0) {
5606
+ const context = text.substring(Math.max(0, position - 50), position + 50);
5607
+ onError?.(`JSON parse error at position ${position}: "${context}"`);
5608
+ } else {
5609
+ onError?.(`JSON parse error: ${error.message}`);
5610
+ }
5611
+ }
5612
+ return fallback;
5613
+ }
5614
+ }
5615
+ function extractJSON(text) {
5616
+ const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
5617
+ if (codeBlockMatch && codeBlockMatch[1]) {
5618
+ const candidate = codeBlockMatch[1].trim();
5619
+ if (candidate.startsWith("{") || candidate.startsWith("[")) {
5620
+ return candidate;
5621
+ }
5622
+ }
5623
+ const objectMatch = text.match(/\{[\s\S]*\}/);
5624
+ if (objectMatch) {
5625
+ return objectMatch[0];
5626
+ }
5627
+ const arrayMatch = text.match(/\[[\s\S]*\]/);
5628
+ if (arrayMatch) {
5629
+ return arrayMatch[0];
5630
+ }
5631
+ return null;
5632
+ }
5633
+ function parseAIResponse(response, fallback, onError) {
5634
+ const json = extractJSON(response);
5635
+ if (!json) {
5636
+ onError?.("No JSON payload found in response");
5637
+ return fallback;
5638
+ }
5639
+ return safeJSONParse(json, fallback, onError);
5640
+ }
5641
+ function isValidJSON(text) {
5642
+ const trimmed = text.trim();
5643
+ if (trimmed.length === 0) return false;
5644
+ if (!(trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]"))) {
5645
+ return false;
5646
+ }
5647
+ try {
5648
+ JSON.parse(trimmed);
5649
+ return true;
5650
+ } catch {
5651
+ return false;
5652
+ }
5653
+ }
5654
+
5655
+ // src/content/application/services/GenerationExecutor.ts
5656
+ var GenerationExecutor = class {
5657
+ constructor(generator) {
5658
+ this.generator = generator;
5659
+ }
5660
+ generator;
5661
+ async runJson(prompt, fallback, generationOptions = {}) {
5662
+ const response = await this.generator.generateText(prompt, generationOptions);
5663
+ return parseAIResponse(response, fallback);
5664
+ }
5665
+ runText(prompt, options = {}) {
5666
+ return this.generator.generateText(prompt, options);
5667
+ }
5668
+ /** Usage-complete pass-through; requires a ProviderFactory engine. */
5669
+ runChat(request) {
5670
+ if (!(this.generator instanceof ProviderFactory)) {
5671
+ throw new AIError(
5672
+ "runChat requires a ProviderFactory engine \u2014 construct AIContentService with a provider config, not a custom ITextGenerator",
5673
+ "PROVIDER_UNAVAILABLE"
5674
+ );
5675
+ }
5676
+ return this.generator.chat(request);
5677
+ }
5678
+ };
5679
+
5680
+ // src/content/application/services/AIContentService.ts
5681
+ var isTextGenerator = (value) => typeof value === "object" && value !== null && typeof value.generateText === "function";
5682
+ function toAIProviderConfig(config) {
5683
+ const legacy = config;
5684
+ const modern = config;
5685
+ const anthropic = modern.anthropic ?? (legacy.anthropic?.enabled ? {
5686
+ apiKey: legacy.anthropic.apiKey,
5687
+ baseUrl: legacy.anthropic.baseUrl,
5688
+ timeoutMs: legacy.anthropic.timeout,
5689
+ models: legacy.anthropic.models,
5690
+ effort: legacy.anthropic.effort
5691
+ } : void 0);
5692
+ const groq = modern.groq ?? (legacy.groq?.enabled ? {
5693
+ apiKey: legacy.groq.apiKey,
5694
+ baseUrl: legacy.groq.baseUrl,
5695
+ timeoutMs: legacy.groq.timeout,
5696
+ // legacy {text, chat} model pins map onto the neutral tier slots
5697
+ ...legacy.groq.models?.text ? { models: { balanced: legacy.groq.models.text } } : {}
5698
+ } : void 0);
5699
+ const gemini = modern.gemini ?? (legacy.gemini?.enabled ? { ...legacy.gemini } : void 0);
5700
+ const fal = modern.fal ?? (legacy.fal?.enabled ? { ...legacy.fal } : void 0);
5701
+ const pruna = modern.pruna ?? (legacy.pruna?.enabled ? { ...legacy.pruna } : void 0);
5702
+ return {
5703
+ priority: modern.priority,
5704
+ fallbackEnabled: modern.fallbackEnabled,
5705
+ defaultTier: modern.defaultTier,
5706
+ ...anthropic ? { anthropic } : {},
5707
+ ...groq ? { groq } : {},
5708
+ ...gemini ? { gemini } : {},
5709
+ ...fal ? { fal } : {},
5710
+ ...pruna ? { pruna } : {}
5711
+ };
5712
+ }
5713
+ var buildServices = (configOrGenerator, model, customGenerator) => {
5714
+ if (customGenerator) {
5715
+ const executor2 = new GenerationExecutor(customGenerator);
5716
+ const emptyFactory = new ProviderFactory({
5717
+ priority: [],
5718
+ fallbackEnabled: false
5719
+ });
5720
+ return {
5721
+ providerFactory: emptyFactory,
5722
+ blogService: new BlogService(executor2),
5723
+ socialService: new SocialService(executor2),
5724
+ videoScriptService: new VideoScriptService(executor2),
5725
+ analysisService: new AnalysisService(executor2),
5726
+ seoService: new SeoService(executor2),
5727
+ abTestService: new ABTestService(executor2),
5728
+ imagePromptService: new ImagePromptService(executor2),
5729
+ contentCalendarService: new ContentCalendarService(executor2),
5730
+ mediaGenerationService: new MediaGenerationService(emptyFactory)
5731
+ };
5732
+ }
5733
+ if (typeof configOrGenerator === "string") {
5734
+ const aiConfig = {
5735
+ anthropic: {
5736
+ apiKey: configOrGenerator,
5737
+ ...model ? { models: { balanced: model } } : {}
5738
+ }
5739
+ };
5740
+ const factory2 = createAI(aiConfig);
5741
+ const executor2 = new GenerationExecutor(factory2);
5742
+ return {
5743
+ providerFactory: factory2,
5744
+ blogService: new BlogService(executor2),
5745
+ socialService: new SocialService(executor2),
5746
+ videoScriptService: new VideoScriptService(executor2),
5747
+ analysisService: new AnalysisService(executor2),
5748
+ seoService: new SeoService(executor2),
5749
+ abTestService: new ABTestService(executor2),
5750
+ imagePromptService: new ImagePromptService(executor2),
5751
+ contentCalendarService: new ContentCalendarService(executor2),
5752
+ mediaGenerationService: new MediaGenerationService(factory2)
5753
+ };
5754
+ }
5755
+ if (isTextGenerator(configOrGenerator)) {
5756
+ const executor2 = new GenerationExecutor(configOrGenerator);
5757
+ const emptyFactory = new ProviderFactory({
5758
+ priority: [],
5759
+ fallbackEnabled: false
5760
+ });
5761
+ return {
5762
+ providerFactory: emptyFactory,
5763
+ blogService: new BlogService(executor2),
5764
+ socialService: new SocialService(executor2),
5765
+ videoScriptService: new VideoScriptService(executor2),
5766
+ analysisService: new AnalysisService(executor2),
5767
+ seoService: new SeoService(executor2),
5768
+ abTestService: new ABTestService(executor2),
5769
+ imagePromptService: new ImagePromptService(executor2),
5770
+ contentCalendarService: new ContentCalendarService(executor2),
5771
+ mediaGenerationService: new MediaGenerationService(emptyFactory)
5772
+ };
5773
+ }
5774
+ const factory = createAI(toAIProviderConfig(configOrGenerator));
5775
+ const executor = new GenerationExecutor(factory);
5776
+ return {
5777
+ providerFactory: factory,
5778
+ blogService: new BlogService(executor),
5779
+ socialService: new SocialService(executor),
5780
+ videoScriptService: new VideoScriptService(executor),
5781
+ analysisService: new AnalysisService(executor),
5782
+ seoService: new SeoService(executor),
5783
+ abTestService: new ABTestService(executor),
5784
+ imagePromptService: new ImagePromptService(executor),
5785
+ contentCalendarService: new ContentCalendarService(executor),
5786
+ mediaGenerationService: new MediaGenerationService(factory)
5787
+ };
5788
+ };
5789
+ var AIContentService = class {
5790
+ providerFactory;
5791
+ blogService;
5792
+ socialService;
5793
+ videoScriptService;
5794
+ analysisService;
5795
+ seoService;
5796
+ abTestService;
5797
+ imagePromptService;
5798
+ contentCalendarService;
5799
+ mediaGenerationService;
5800
+ constructor(configOrGenerator, model, textGenerator) {
5801
+ const bundle = buildServices(configOrGenerator, model, textGenerator);
5802
+ this.providerFactory = bundle.providerFactory;
5803
+ this.blogService = bundle.blogService;
5804
+ this.socialService = bundle.socialService;
5805
+ this.videoScriptService = bundle.videoScriptService;
5806
+ this.analysisService = bundle.analysisService;
5807
+ this.seoService = bundle.seoService;
5808
+ this.abTestService = bundle.abTestService;
5809
+ this.imagePromptService = bundle.imagePromptService;
5810
+ this.contentCalendarService = bundle.contentCalendarService;
5811
+ this.mediaGenerationService = bundle.mediaGenerationService;
5812
+ }
5813
+ /** The composed factory — chat()/chatStream() with usage, health, fallback. */
5814
+ get providers() {
5815
+ return this.providerFactory;
5816
+ }
5817
+ generateBlogPost(request) {
5818
+ return this.blogService.generateBlogPost(request);
5819
+ }
5820
+ generateSocialContent(request) {
5821
+ return this.socialService.generateSocialContent(request);
5822
+ }
5823
+ generateForAllPlatforms(topic, tone) {
5824
+ return this.socialService.generateForAllPlatforms(topic, tone);
5825
+ }
5826
+ generateHashtags(content, count) {
5827
+ return this.socialService.generateHashtags(content, count);
5828
+ }
5829
+ optimizeHashtags(hashtags, platform) {
5830
+ return Promise.resolve(this.socialService.optimizeHashtags(hashtags, platform));
5831
+ }
5832
+ generateVideoScript(request) {
5833
+ return this.videoScriptService.generateVideoScript(request);
5834
+ }
5835
+ generateVoiceScript(topic, emotion, duration) {
5836
+ return this.videoScriptService.generateVoiceScript(topic, emotion, duration);
5837
+ }
5838
+ generateContentCalendar(niche, days) {
5839
+ return this.contentCalendarService.generateContentCalendar(niche, days);
5840
+ }
5841
+ analyzeSentiment(content) {
5842
+ return this.analysisService.analyzeSentiment(content);
5843
+ }
5844
+ analyzeContent(request) {
5845
+ return this.analysisService.analyzeContent(request);
5846
+ }
5847
+ optimizeSEO(request) {
5848
+ return this.seoService.optimizeSEO(request);
5849
+ }
5850
+ calculateSEOScore(content, keywords) {
5851
+ return this.seoService.calculateSEOScore(content, keywords);
5852
+ }
5853
+ analyzeKeywords(content, keywords) {
5854
+ return Promise.resolve(this.seoService.analyzeKeywords(content, keywords));
5855
+ }
5856
+ predictABTest(request) {
5857
+ return this.abTestService.predictABTest(request);
5858
+ }
5859
+ compareVariants(variantA, variantB) {
5860
+ return this.abTestService.compareVariants(variantA, variantB);
5861
+ }
5862
+ generateImagePrompt(description, style) {
5863
+ return this.imagePromptService.generateImagePrompt(description, style);
5864
+ }
5865
+ generateImagePromptsForBlog(blogContent) {
5866
+ return this.imagePromptService.generateImagePromptsForBlog(blogContent);
5867
+ }
5868
+ generateImage(request) {
5869
+ return this.mediaGenerationService.generateImage(request);
5870
+ }
5871
+ generateVideo(request) {
5872
+ return this.mediaGenerationService.generateVideo(request);
5873
+ }
5874
+ convertImageToVideo(request) {
5875
+ return this.mediaGenerationService.convertImageToVideo(request);
5876
+ }
5877
+ transformVideo(request) {
5878
+ return this.mediaGenerationService.transformVideo(request);
5879
+ }
5880
+ };
5881
+
5882
+ // src/wizard/content.wizard.ts
5883
+ var CONTENT_WIZARD_STEPS = [
5884
+ {
5885
+ id: "select-type",
5886
+ label: "Select Type",
5887
+ icon: "Layers",
5888
+ description: "Choose the type of content you want to generate",
5889
+ validate: (data) => Boolean(data.contentType)
5890
+ },
5891
+ {
5892
+ id: "input-topic",
5893
+ label: "Topic",
5894
+ icon: "Lightbulb",
5895
+ description: "Enter your topic or idea",
5896
+ validate: (data) => {
5897
+ const topic = data.topic;
5898
+ return typeof topic === "string" && topic.length > 0;
5899
+ }
5900
+ },
5901
+ {
5902
+ id: "configure",
5903
+ label: "Configure",
5904
+ icon: "Settings",
5905
+ description: "Set tone, audience, and platform",
5906
+ validate: (data) => {
5907
+ const d = data;
5908
+ return Boolean(d.tone && d.targetAudience);
5909
+ }
5910
+ },
5911
+ {
5912
+ id: "advanced",
5913
+ label: "Advanced",
5914
+ icon: "Sliders",
5915
+ description: "Additional options and optimizations",
5916
+ optional: true
5917
+ },
5918
+ {
5919
+ id: "preview",
5920
+ label: "Preview",
5921
+ icon: "Eye",
5922
+ description: "Review your settings before generation"
5923
+ },
5924
+ {
5925
+ id: "generating",
5926
+ label: "Generating",
5927
+ icon: "Sparkles",
5928
+ description: "AI is generating your content"
5929
+ },
5930
+ {
5931
+ id: "results",
5932
+ label: "Results",
5933
+ icon: "CheckCircle",
5934
+ description: "Your generated content is ready"
5935
+ }
5936
+ ];
5937
+ function buildContentRequest(data) {
5938
+ switch (data.contentType) {
5939
+ case "blog":
5940
+ return {
5941
+ topic: data.topic ?? "",
5942
+ blogType: "tutorial",
5943
+ targetKeywords: data.keywords ?? [],
5944
+ tone: data.tone ?? "professional",
5945
+ wordCount: data.wordCount ?? 1e3,
5946
+ targetAudience: data.targetAudience ?? "",
5947
+ seoOptimization: data.seoOptimization ?? false,
5948
+ includeImages: data.includeImages ?? false,
5949
+ includeSchema: false
5950
+ };
5951
+ case "social":
5952
+ return {
5953
+ topic: data.topic ?? "",
5954
+ platform: data.platform ?? "twitter",
5955
+ tone: data.tone ?? "casual",
5956
+ hashtags: true,
5957
+ targetAudience: data.targetAudience,
5958
+ includeCallToAction: data.includeCallToAction
5959
+ };
5960
+ case "script":
5961
+ return {
5962
+ topic: data.topic ?? "",
5963
+ tone: data.tone ?? "professional",
5964
+ duration: data.duration ?? 60,
5965
+ targetAudience: data.targetAudience ?? "",
5966
+ includeVisuals: true,
5967
+ includeCallToAction: data.includeCallToAction ?? false
5968
+ };
5969
+ default:
5970
+ throw new Error(`Unsupported content type: ${data.contentType}`);
5971
+ }
5972
+ }
5973
+
5974
+ // src/wizard/image.wizard.ts
5975
+ var IMAGE_WIZARD_STEPS = [
5976
+ {
5977
+ id: "input-prompt",
5978
+ label: "Describe Image",
5979
+ icon: "Image",
5980
+ description: "Describe the image you want to create",
5981
+ validate: (data) => {
5982
+ const prompt = data.prompt;
5983
+ return typeof prompt === "string" && prompt.length > 0;
5984
+ }
5985
+ },
5986
+ {
5987
+ id: "select-style",
5988
+ label: "Style",
5989
+ icon: "Palette",
5990
+ description: "Choose the artistic style",
5991
+ validate: (data) => Boolean(data.style)
5992
+ },
5993
+ {
5994
+ id: "configure",
5995
+ label: "Configure",
5996
+ icon: "Settings",
5997
+ description: "Set format, quality, and quantity",
5998
+ validate: (data) => {
5999
+ const d = data;
6000
+ return Boolean(d.format && d.quality);
6001
+ }
6002
+ },
6003
+ {
6004
+ id: "advanced",
6005
+ label: "Advanced",
6006
+ icon: "Sliders",
6007
+ description: "Color palette and platform optimization",
6008
+ optional: true
6009
+ },
6010
+ { id: "preview", label: "Preview", icon: "Eye", description: "Review your settings" },
6011
+ { id: "generating", label: "Generating", icon: "Sparkles", description: "AI is creating your image" },
6012
+ { id: "results", label: "Results", icon: "Image", description: "Your generated image is ready" }
6013
+ ];
6014
+ function buildImageRequest(data) {
6015
+ return {
6016
+ type: "image",
6017
+ prompt: data.prompt ?? "",
6018
+ style: data.style,
6019
+ format: data.format,
6020
+ quality: data.quality,
6021
+ quantity: data.quantity ?? 1
6022
+ };
6023
+ }
6024
+
6025
+ // src/wizard/video.wizard.ts
6026
+ var validateSource = (data) => {
6027
+ const d = data;
6028
+ if (d.generationType === "text-to-video") {
6029
+ return typeof d.prompt === "string" && d.prompt.length > 0;
6030
+ }
6031
+ if (d.generationType === "image-to-video") {
6032
+ return typeof d.imageUrl === "string" && d.imageUrl.length > 0;
6033
+ }
6034
+ if (d.generationType === "video-to-video") {
6035
+ return typeof d.sourceVideoUrl === "string" && d.sourceVideoUrl.length > 0 && typeof d.prompt === "string" && d.prompt.length > 0;
6036
+ }
6037
+ return false;
6038
+ };
6039
+ var VIDEO_WIZARD_STEPS = [
6040
+ {
6041
+ id: "select-type",
6042
+ label: "Select Type",
6043
+ icon: "Video",
6044
+ description: "Choose video generation type",
6045
+ validate: (data) => Boolean(data.generationType)
6046
+ },
6047
+ {
6048
+ id: "input-source",
6049
+ label: "Source",
6050
+ icon: "FileInput",
6051
+ description: "Provide your source (prompt, image, or video)",
6052
+ validate: validateSource
6053
+ },
6054
+ {
6055
+ id: "configure",
6056
+ label: "Configure",
6057
+ icon: "Settings",
6058
+ description: "Set duration, aspect ratio, and style",
6059
+ validate: (data) => {
6060
+ const d = data;
6061
+ return Boolean(d.duration && d.aspectRatio);
6062
+ }
6063
+ },
6064
+ { id: "advanced", label: "Advanced", icon: "Sliders", description: "Additional video options", optional: true },
6065
+ { id: "preview", label: "Preview", icon: "Eye", description: "Review your settings" },
6066
+ { id: "generating", label: "Generating", icon: "Sparkles", description: "AI is creating your video" },
6067
+ { id: "results", label: "Results", icon: "Video", description: "Your generated video is ready" }
6068
+ ];
6069
+ function buildVideoRequest(data) {
6070
+ switch (data.generationType) {
6071
+ case "text-to-video":
6072
+ return {
6073
+ type: "video",
6074
+ prompt: data.prompt ?? "",
6075
+ duration: data.duration ?? 5,
6076
+ aspectRatio: data.aspectRatio ?? "16:9",
6077
+ style: data.style,
6078
+ quality: data.quality
6079
+ };
6080
+ case "image-to-video":
6081
+ return {
6082
+ type: "video",
6083
+ imageUrl: data.imageUrl ?? "",
6084
+ prompt: data.prompt,
6085
+ duration: data.duration ?? 5,
6086
+ motion: data.motion ?? "medium",
6087
+ camera: data.camera ?? "static"
6088
+ };
6089
+ case "video-to-video":
6090
+ return {
6091
+ type: "video",
6092
+ sourceVideoUrl: data.sourceVideoUrl ?? "",
6093
+ prompt: data.prompt ?? "",
6094
+ styleTransfer: data.styleTransfer,
6095
+ enhanceQuality: data.enhanceQuality
6096
+ };
6097
+ default:
6098
+ throw new Error(`Unsupported generation type: ${data.generationType}`);
6099
+ }
6100
+ }
6101
+
6102
+ exports.ABTestService = ABTestService;
6103
+ exports.AIContentService = AIContentService;
4360
6104
  exports.AIError = AIError;
4361
6105
  exports.AIErrorType = AIErrorType;
4362
6106
  exports.ANTHROPIC_API_VERSION = ANTHROPIC_API_VERSION;
@@ -4364,16 +6108,23 @@ exports.ANTHROPIC_DEFAULT_MAX_TOKENS = ANTHROPIC_DEFAULT_MAX_TOKENS;
4364
6108
  exports.ANTHROPIC_MODELS = ANTHROPIC_MODELS;
4365
6109
  exports.ANTHROPIC_TIERS = ANTHROPIC_TIERS;
4366
6110
  exports.API_ENDPOINTS = API_ENDPOINTS;
6111
+ exports.ASPECT_RATIO_OPTIONS = ASPECT_RATIO_OPTIONS;
6112
+ exports.ASPECT_RATIO_VALUES = ASPECT_RATIO_VALUES;
6113
+ exports.AnalysisService = AnalysisService;
4367
6114
  exports.AnthropicHttpTransport = AnthropicHttpTransport;
4368
6115
  exports.AnthropicProvider = AnthropicProvider;
4369
6116
  exports.AnthropicRequestBuilder = AnthropicRequestBuilder;
4370
6117
  exports.AuthenticationError = AuthenticationError;
6118
+ exports.BaseAIProvider = BaseProvider;
4371
6119
  exports.BaseProvider = BaseProvider;
6120
+ exports.BlogService = BlogService;
6121
+ exports.CONTENT_WIZARD_STEPS = CONTENT_WIZARD_STEPS;
4372
6122
  exports.CacheManager = CacheManager;
4373
6123
  exports.CancellationError = CancellationError;
4374
6124
  exports.ChatIdGenerator = ChatIdGenerator;
4375
6125
  exports.ChatService = ChatService;
4376
6126
  exports.CompositeExecutor = CompositeExecutor;
6127
+ exports.ContentCalendarService = ContentCalendarService;
4377
6128
  exports.DEFAULT_API_KEY_RULE = DEFAULT_API_KEY_RULE;
4378
6129
  exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
4379
6130
  exports.DEFAULT_ERROR_CLASSIFICATION_RULE = DEFAULT_ERROR_CLASSIFICATION_RULE;
@@ -4390,9 +6141,11 @@ exports.DebounceManager = DebounceManager;
4390
6141
  exports.DedupeExecutor = DedupeExecutor;
4391
6142
  exports.FalProvider = FalProvider;
4392
6143
  exports.GEMINI_TIERS = GEMINI_TIERS;
6144
+ exports.GENERATION_COSTS = CREDIT_COSTS;
4393
6145
  exports.GROQ_MODELS = GROQ_MODELS;
4394
6146
  exports.GROQ_TIERS = GROQ_TIERS;
4395
6147
  exports.GeminiProvider = GeminiProvider;
6148
+ exports.GenerationExecutor = GenerationExecutor;
4396
6149
  exports.GenerationFailedError = GenerationFailedError;
4397
6150
  exports.GroqChatRequestBuilder = GroqChatRequestBuilder;
4398
6151
  exports.GroqConfigStore = GroqConfigStore;
@@ -4401,20 +6154,33 @@ exports.GroqErrorType = AIErrorType;
4401
6154
  exports.GroqHttpClient = GroqHttpClient;
4402
6155
  exports.GroqHttpTransport = GroqHttpTransport;
4403
6156
  exports.GroqProvider = GroqProvider;
6157
+ exports.IMAGE_QUALITY_OPTIONS = IMAGE_QUALITY_OPTIONS;
6158
+ exports.IMAGE_QUALITY_VALUES = IMAGE_QUALITY_VALUES;
6159
+ exports.IMAGE_WIZARD_STEPS = IMAGE_WIZARD_STEPS;
6160
+ exports.ImagePromptService = ImagePromptService;
4404
6161
  exports.JSON_CODE_BLOCK_PATTERN = JSON_CODE_BLOCK_PATTERN;
4405
6162
  exports.JSON_OBJECT_PATTERN = JSON_OBJECT_PATTERN;
4406
6163
  exports.JsonResponseParser = JsonResponseParser;
6164
+ exports.MOTION_OPTIONS = MOTION_OPTIONS;
6165
+ exports.MediaGenerationService = MediaGenerationService;
6166
+ exports.PromptBuilder = PromptBuilder;
4407
6167
  exports.ProviderError = ProviderError;
4408
6168
  exports.ProviderFactory = ProviderFactory;
6169
+ exports.ProviderQuotaExhaustedError = ProviderQuotaExhaustedError;
6170
+ exports.ProviderUnavailableError = ProviderUnavailableError;
4409
6171
  exports.PrunaProvider = PrunaProvider;
4410
6172
  exports.QueueExecutor = QueueExecutor;
4411
6173
  exports.RequestDeduplicator = RequestDeduplicator;
6174
+ exports.RequestPriority = RequestPriority;
4412
6175
  exports.RequestQueue = RequestQueue;
4413
6176
  exports.RetryExecutor = RetryExecutor;
4414
6177
  exports.RetryManager = RetryManager;
4415
6178
  exports.SSE_BRIDGE_EVENTS = SSE_BRIDGE_EVENTS;
4416
6179
  exports.SSE_DONE_MARKER = SSE_DONE_MARKER;
4417
6180
  exports.SSE_PREFIX = SSE_PREFIX;
6181
+ exports.STYLE_PRESETS = STYLE_PRESETS;
6182
+ exports.SeoService = SeoService;
6183
+ exports.SocialService = SocialService;
4418
6184
  exports.StreamingResponseParser = StreamingResponseParser;
4419
6185
  exports.StructuredPromptBuilder = StructuredPromptBuilder;
4420
6186
  exports.TEXT_TIERS = TEXT_TIERS;
@@ -4423,14 +6189,25 @@ exports.TextGenerationService = TextGenerationService;
4423
6189
  exports.ThrottleManager = ThrottleManager;
4424
6190
  exports.TimeoutError = TimeoutError;
4425
6191
  exports.USER_FRIENDLY_ERROR_MESSAGES = USER_FRIENDLY_ERROR_MESSAGES;
4426
- exports.ValidationError = ValidationError;
6192
+ exports.VIDEO_DURATION_OPTIONS = VIDEO_DURATION_OPTIONS;
6193
+ exports.VIDEO_DURATION_OPTIONS_SECONDS = VIDEO_DURATION_OPTIONS_SECONDS;
6194
+ exports.VIDEO_QUALITY_OPTIONS = VIDEO_QUALITY_OPTIONS;
6195
+ exports.VIDEO_QUALITY_VALUES = VIDEO_QUALITY_VALUES;
6196
+ exports.VIDEO_WIZARD_STEPS = VIDEO_WIZARD_STEPS;
6197
+ exports.ValidationError = ValidationError2;
6198
+ exports.VideoScriptService = VideoScriptService;
4427
6199
  exports.asObjectArray = asObjectArray;
4428
6200
  exports.asRecord = asRecord;
4429
6201
  exports.asStringArray = asStringArray;
4430
6202
  exports.asTrimmedOptionalString = asTrimmedOptionalString;
6203
+ exports.batchRequest = batchRequest;
4431
6204
  exports.buildCacheKey = buildCacheKey;
6205
+ exports.buildContentRequest = buildContentRequest;
4432
6206
  exports.buildHttpStatusFallback = buildHttpStatusFallback;
6207
+ exports.buildImageRequest = buildImageRequest;
6208
+ exports.buildVideoRequest = buildVideoRequest;
4433
6209
  exports.cacheManager = cacheManager;
6210
+ exports.calculateCreditCost = calculateContentCreditCost;
4434
6211
  exports.chatIdGenerator = chatIdGenerator;
4435
6212
  exports.clampScore = clampScore;
4436
6213
  exports.classifyHttpStatus = classifyHttpStatus;
@@ -4441,7 +6218,9 @@ exports.createAI = createAI;
4441
6218
  exports.createAssistantMessage = createAssistantMessage;
4442
6219
  exports.createFalProvider = createFalProvider;
4443
6220
  exports.createGeminiProvider = createGeminiProvider;
6221
+ exports.createGroqProvider = createGroqProvider;
4444
6222
  exports.createMessage = createMessage;
6223
+ exports.createProviderFactory = createProviderFactory;
4445
6224
  exports.createPrunaProvider = createPrunaProvider;
4446
6225
  exports.createSseResponse = createSseResponse;
4447
6226
  exports.createSystemMessage = createSystemMessage;
@@ -4453,6 +6232,7 @@ exports.describeAIErrorType = describeAIErrorType;
4453
6232
  exports.extractContentFromSsePayload = extractContentFromSsePayload;
4454
6233
  exports.extractErrorMessageFromJsonBody = extractErrorMessageFromJsonBody;
4455
6234
  exports.extractErrorMessageFromResponse = extractErrorMessageFromResponse;
6235
+ exports.extractJSON = extractJSON;
4456
6236
  exports.extractJsonArray = extractJsonArray;
4457
6237
  exports.extractJsonCandidate = extractJsonCandidate;
4458
6238
  exports.extractJsonFromCodeFence = extractJsonFromCodeFence;
@@ -4465,7 +6245,9 @@ exports.formatMessagesForDisplay = formatMessagesForDisplay;
4465
6245
  exports.generateImageThenVideo = generateImageThenVideo;
4466
6246
  exports.generatePruna = generate;
4467
6247
  exports.generateWithPruna = generateWithPruna;
6248
+ exports.getDimensionsForAspectRatio = getDimensionsForAspectRatio;
4468
6249
  exports.getProvider = getProvider;
6250
+ exports.getRecommendedQuality = getRecommendedQuality;
4469
6251
  exports.isAuthErrorType = isAuthErrorType;
4470
6252
  exports.isMediaProvider = isMediaProvider;
4471
6253
  exports.isRetryableErrorType = isRetryableErrorType;
@@ -4474,11 +6256,13 @@ exports.isSseDoneMarker = isSseDoneMarker;
4474
6256
  exports.isTextProvider = isTextProvider;
4475
6257
  exports.isTextTier = isTextTier;
4476
6258
  exports.isValidApiKey = isValidApiKey;
6259
+ exports.isValidJSON = isValidJSON;
4477
6260
  exports.mapAnthropicStopReason = mapAnthropicStopReason;
4478
6261
  exports.mergeTierOverrides = mergeTierOverrides;
4479
6262
  exports.messageFormatter = messageFormatter;
4480
6263
  exports.normalizeSseChunk = normalizeSseChunk;
4481
6264
  exports.oneOfCatalog = oneOfCatalog;
6265
+ exports.parseAIResponse = parseAIResponse;
4482
6266
  exports.pollForResult = pollForResult;
4483
6267
  exports.promptToMessages = promptToMessages;
4484
6268
  exports.repairCommonJsonDefects = repairCommonJsonDefects;
@@ -4490,6 +6274,7 @@ exports.resolveModel = resolveModel;
4490
6274
  exports.resolveUserFriendlyErrorMessage = resolveUserFriendlyErrorMessage;
4491
6275
  exports.resolveUserFriendlyMessage = resolveUserFriendlyMessage;
4492
6276
  exports.retryManager = retryManager;
6277
+ exports.safeJSONParse = safeJSONParse;
4493
6278
  exports.splitSseLines = splitSseLines;
4494
6279
  exports.submitPrediction = submitPrediction;
4495
6280
  exports.throttle = throttle;