@umituz/web-ai 1.0.2 → 1.0.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 (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/PromptBuilder-B5Lm1znm.d.ts +215 -0
  4. package/dist/PromptBuilder-DIUZlfV6.d.cts +215 -0
  5. package/dist/{text-generation.service-BQO3Tvz2.d.cts → cancellation-error-Cj7fJyCg.d.cts} +66 -3
  6. package/dist/{text-generation.service-CcYQM6Ch.d.ts → cancellation-error-CkMn5Kft.d.ts} +66 -3
  7. package/dist/chat/index.cjs.map +1 -1
  8. package/dist/chat/index.d.cts +3 -3
  9. package/dist/chat/index.d.ts +3 -3
  10. package/dist/chat/index.js.map +1 -1
  11. package/dist/{chat.service-CL1qtYxe.d.cts → chat.service-9M-KpLo-.d.cts} +1 -1
  12. package/dist/{chat.service-q05GnhEm.d.ts → chat.service-B4FLFxR1.d.ts} +1 -1
  13. package/dist/content/index.cjs +1582 -452
  14. package/dist/content/index.cjs.map +1 -1
  15. package/dist/content/index.d.cts +165 -219
  16. package/dist/content/index.d.ts +165 -219
  17. package/dist/content/index.js +1525 -453
  18. package/dist/content/index.js.map +1 -1
  19. package/dist/{create-ai-9PW752z8.d.ts → create-ai-0eZFFqTw.d.ts} +1 -1
  20. package/dist/{create-ai-CXYKPK6G.d.cts → create-ai-BrKR_5fQ.d.cts} +1 -1
  21. package/dist/hooks/index.cjs +924 -31
  22. package/dist/hooks/index.cjs.map +1 -1
  23. package/dist/hooks/index.d.cts +44 -8
  24. package/dist/hooks/index.d.ts +44 -8
  25. package/dist/hooks/index.js +919 -32
  26. package/dist/hooks/index.js.map +1 -1
  27. package/dist/index-BUVULo3h.d.ts +177 -0
  28. package/dist/index-BulSkB2L.d.cts +177 -0
  29. package/dist/index.cjs +3057 -563
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +8 -5
  32. package/dist/index.d.ts +8 -5
  33. package/dist/index.js +3011 -564
  34. package/dist/index.js.map +1 -1
  35. package/dist/{provider.factory-DfDfk0dK.d.ts → provider.factory-BwPnqSEU.d.ts} +3 -1
  36. package/dist/{provider.factory-BxsfK4WW.d.cts → provider.factory-DyfJtR8M.d.cts} +3 -1
  37. package/dist/providers/index.cjs +818 -27
  38. package/dist/providers/index.cjs.map +1 -1
  39. package/dist/providers/index.d.cts +50 -8
  40. package/dist/providers/index.d.ts +50 -8
  41. package/dist/providers/index.js +806 -28
  42. package/dist/providers/index.js.map +1 -1
  43. package/dist/wizard/index.d.cts +2 -86
  44. package/dist/wizard/index.d.ts +2 -86
  45. package/package.json +21 -1
  46. package/dist/ContentGeneration-B6iK2B33.d.cts +0 -93
  47. package/dist/ContentGeneration-B6iK2B33.d.ts +0 -93
@@ -1,3 +1,61 @@
1
+ // src/content/domain/types/index.ts
2
+ var PLATFORM_SPECS = {
3
+ twitter: {
4
+ maxLength: 280,
5
+ style: "concise, engaging, thread-friendly",
6
+ emojiSupport: true,
7
+ hashtagSupport: true,
8
+ imageSupport: true,
9
+ videoSupport: true,
10
+ maxHashtags: 5
11
+ },
12
+ linkedin: {
13
+ maxLength: 3e3,
14
+ style: "professional, insightful, industry-focused",
15
+ emojiSupport: true,
16
+ hashtagSupport: true,
17
+ imageSupport: true,
18
+ videoSupport: true,
19
+ maxHashtags: 5
20
+ },
21
+ instagram: {
22
+ maxLength: 2200,
23
+ style: "visual, engaging, emoji-rich",
24
+ emojiSupport: true,
25
+ hashtagSupport: true,
26
+ imageSupport: true,
27
+ videoSupport: true,
28
+ maxHashtags: 30
29
+ },
30
+ threads: {
31
+ maxLength: 500,
32
+ style: "conversational, community-driven",
33
+ emojiSupport: true,
34
+ hashtagSupport: true,
35
+ imageSupport: true,
36
+ videoSupport: true,
37
+ maxHashtags: 5
38
+ },
39
+ tiktok: {
40
+ maxLength: 150,
41
+ style: "catchy, trending, short-form",
42
+ emojiSupport: true,
43
+ hashtagSupport: true,
44
+ imageSupport: false,
45
+ videoSupport: true,
46
+ maxHashtags: 5
47
+ },
48
+ facebook: {
49
+ maxLength: 63206,
50
+ style: "conversational, engaging, shareable",
51
+ emojiSupport: true,
52
+ hashtagSupport: true,
53
+ imageSupport: true,
54
+ videoSupport: true,
55
+ maxHashtags: 5
56
+ }
57
+ };
58
+
1
59
  // src/content/domain/calculations/ContentCreditCost.ts
2
60
  var CREDIT_COSTS = {
3
61
  "text-to-image": { standard: 1, hd: 2, "4k": 4 },
@@ -153,48 +211,428 @@ var AIError = class _AIError extends Error {
153
211
  this.cause = cause;
154
212
  this.metadata = metadata;
155
213
  }
156
- static from(error, fallbackCode = "UNKNOWN") {
157
- if (error instanceof _AIError) return error;
158
- if (error instanceof Error) {
159
- const code = mapMessageToCode(error.message, fallbackCode);
160
- return new _AIError(error.message, code, error);
161
- }
162
- return new _AIError(String(error), fallbackCode, error);
214
+ static from(error, fallbackCode = "UNKNOWN") {
215
+ if (error instanceof _AIError) return error;
216
+ if (error instanceof Error) {
217
+ const code = mapMessageToCode(error.message, fallbackCode);
218
+ return new _AIError(error.message, code, error);
219
+ }
220
+ return new _AIError(String(error), fallbackCode, error);
221
+ }
222
+ toString() {
223
+ const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
224
+ return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
225
+ }
226
+ toJSON() {
227
+ return {
228
+ name: this.name,
229
+ type: this.type,
230
+ code: this.code,
231
+ message: this.message,
232
+ metadata: this.metadata
233
+ };
234
+ }
235
+ };
236
+
237
+ // src/content/domain/errors/AIErrors.ts
238
+ var ValidationError = class extends AIError {
239
+ constructor(message) {
240
+ super(message, "VALIDATION_ERROR");
241
+ this.name = "ValidationError";
242
+ }
243
+ };
244
+ var ProviderUnavailableError = class extends AIError {
245
+ constructor(providerId) {
246
+ super(`Provider ${providerId} is not available`, "PROVIDER_UNAVAILABLE");
247
+ this.name = "ProviderUnavailableError";
248
+ }
249
+ };
250
+ var ProviderQuotaExhaustedError = class extends AIError {
251
+ constructor(providerId) {
252
+ super(`Provider ${providerId} quota exhausted`, "PROVIDER_QUOTA_EXHAUSTED");
253
+ this.name = "ProviderQuotaExhaustedError";
254
+ }
255
+ };
256
+
257
+ // src/content/domain/limits/ValidationLimits.ts
258
+ var ValidationLimits = {
259
+ TOPIC: {
260
+ MIN: 1,
261
+ MAX: 500
262
+ },
263
+ CONTENT: {
264
+ MIN: 1,
265
+ MAX: 1e4
266
+ },
267
+ KEYWORD: {
268
+ MAX_COUNT: 20,
269
+ MAX_LENGTH: 50
270
+ },
271
+ DURATION_SECONDS: {
272
+ MIN: 10,
273
+ MAX: 600
274
+ },
275
+ CALENDAR_DAYS: {
276
+ MIN: 1,
277
+ MAX: 90
278
+ },
279
+ HASHTAG_COUNT: {
280
+ MIN: 1,
281
+ MAX: 50
282
+ },
283
+ SEO_CONTENT_PREVIEW: 500,
284
+ BLOG_PROMPT_PREVIEW: 1e3,
285
+ AB_VARIANT_CONTENT_PREVIEW: 200
286
+ };
287
+
288
+ // src/content/domain/limits/SpeechPaceConfig.ts
289
+ var SpeechPaceConfig = {
290
+ WORDS_PER_SECOND: 2.5,
291
+ TOKENS_PER_SECOND: 10,
292
+ JSON_TOKEN_OVERHEAD: 500,
293
+ TOKENS_PER_CALENDAR_DAY: 150
294
+ };
295
+
296
+ // src/content/domain/limits/ProviderTimingConfig.ts
297
+ var ProviderTimingConfig = {
298
+ DEFAULT_TIMEOUT_MS: 3e4,
299
+ DEFAULT_RETRY_ATTEMPTS: 3,
300
+ FAL_TIMEOUT_MS: 12e4,
301
+ FAL_RETRY_ATTEMPTS: 2,
302
+ GEMINI_TIMEOUT_MS: 6e4,
303
+ GEMINI_RETRY_ATTEMPTS: 3,
304
+ GROQ_TIMEOUT_MS: 3e4,
305
+ GROQ_RETRY_ATTEMPTS: 3,
306
+ HEALTH_CHECK_TTL_MS: 5 * 60 * 1e3,
307
+ RETRY_BACKOFF_BASE_MS: 1e3
308
+ };
309
+
310
+ // src/content/domain/limits/ModelDefaults.ts
311
+ var ModelDefaults = {
312
+ GROQ_TEXT: "llama-3.1-8b-instant",
313
+ GROQ_CHAT: "llama-3.1-8b-instant",
314
+ FAL_IMAGE: "fal-ai/flux/schnell",
315
+ FAL_VIDEO: "fal-ai/hunyuan-video/1",
316
+ FAL_IMAGE_TO_VIDEO: "fal-ai/fast-animatediff/image-to-video",
317
+ GEMINI_TEXT: "gemini-2.0-flash",
318
+ GEMINI_IMAGE: "imagen-4.0-generate-001",
319
+ PRUNA_IMAGE: "p-image",
320
+ PRUNA_IMAGE_EDIT: "p-image-edit",
321
+ PRUNA_VIDEO: "p-video"
322
+ };
323
+ var TextModelDefaults = {
324
+ TEXT_MODEL: "claude-sonnet-4-20250514",
325
+ MAX_TOKENS: 4096,
326
+ DEFAULT_TEMPERATURE: 0.7,
327
+ DEFAULT_TOP_P: 0.9
328
+ };
329
+
330
+ // src/content/domain/limits/RequestQueueConfig.ts
331
+ var RequestQueueConfig = {
332
+ MAX_CONCURRENT: 3,
333
+ MAX_QUEUE_SIZE: 50,
334
+ DEFAULT_TIMEOUT_MS: 3e4,
335
+ RETRY_DELAY_MS: 1e3,
336
+ RATE_LIMIT: {
337
+ ENABLED: true,
338
+ WINDOW_MS: 6e4,
339
+ MAX_REQUESTS_PER_WINDOW: 100
340
+ },
341
+ POLL_INTERVAL_MS: 100
342
+ };
343
+
344
+ // src/content/domain/limits/MemoryConfig.ts
345
+ var MemoryConfig = {
346
+ MANAGER: {
347
+ MAX_SIZE_BYTES: 50 * 1024 * 1024,
348
+ CLEANUP_THRESHOLD_RATIO: 0.8,
349
+ RETENTION_RATIO: 0.5
350
+ },
351
+ POOL: {
352
+ DEFAULT_MAX_SIZE: 100
353
+ },
354
+ EXPIRING_CACHE: {
355
+ DEFAULT_TTL_MS: 6e4,
356
+ CLEANUP_INTERVAL_MS: 6e4
357
+ }
358
+ };
359
+
360
+ // src/content/domain/calculations/SpeechPaceEstimate.ts
361
+ function estimateWordCountForDuration(durationSeconds) {
362
+ return Math.floor(durationSeconds * SpeechPaceConfig.WORDS_PER_SECOND);
363
+ }
364
+ function estimateTokenBudgetForDuration(durationSeconds) {
365
+ return durationSeconds * SpeechPaceConfig.TOKENS_PER_SECOND + SpeechPaceConfig.JSON_TOKEN_OVERHEAD;
366
+ }
367
+ function estimateTokenBudgetForCalendarDays(days) {
368
+ return days * SpeechPaceConfig.TOKENS_PER_CALENDAR_DAY + SpeechPaceConfig.JSON_TOKEN_OVERHEAD;
369
+ }
370
+
371
+ // src/content/domain/calculations/SeoKeywordDensity.ts
372
+ function countKeywordOccurrences(content, keyword) {
373
+ if (!keyword) return 0;
374
+ const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
375
+ const matches = content.match(new RegExp(escaped, "gi"));
376
+ return matches ? matches.length : 0;
377
+ }
378
+ function countWordsInContent(content) {
379
+ const tokens = content.split(/\s+/).filter(Boolean);
380
+ return tokens.length || 1;
381
+ }
382
+ function calculateKeywordDensity(content, keyword) {
383
+ const totalWords = countWordsInContent(content);
384
+ const occurrences = countKeywordOccurrences(content, keyword);
385
+ return occurrences / totalWords * 100;
386
+ }
387
+
388
+ // src/content/domain/calculations/AbTestImprovementRatio.ts
389
+ function calculateImprovementPercentage(scoreA, scoreB) {
390
+ const higher = Math.max(scoreA, scoreB);
391
+ const lower = Math.min(scoreA, scoreB);
392
+ if (lower <= 0) {
393
+ return "100";
394
+ }
395
+ const delta = (higher / lower - 1) * 100;
396
+ return Math.abs(delta).toFixed(0);
397
+ }
398
+ function selectHigherScoreVariantId(scoreA, scoreB, idA = "A", idB = "B") {
399
+ return scoreA > scoreB ? idA : idB;
400
+ }
401
+
402
+ // src/content/domain/calculations/RetryBackoffDelay.ts
403
+ function calculateRetryBackoffDelayMs(attemptIndex) {
404
+ return ProviderTimingConfig.RETRY_BACKOFF_BASE_MS * Math.pow(2, attemptIndex);
405
+ }
406
+
407
+ // src/content/domain/calculations/ContentCalendarDateOffset.ts
408
+ var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
409
+ function resolveCalendarEntryDate(dayIndex, baseTimestamp = Date.now()) {
410
+ return new Date(baseTimestamp + (dayIndex - 1) * ONE_DAY_MS);
411
+ }
412
+
413
+ // src/content/domain/calculations/DefaultModelSelection.ts
414
+ function resolveDefaultPrunaModel(generationType) {
415
+ switch (generationType) {
416
+ case "image-to-image":
417
+ return ModelDefaults.PRUNA_IMAGE_EDIT;
418
+ case "image-to-video":
419
+ return ModelDefaults.PRUNA_VIDEO;
420
+ case "text-to-image":
421
+ return ModelDefaults.PRUNA_IMAGE;
422
+ case "text-to-video":
423
+ return ModelDefaults.PRUNA_VIDEO;
424
+ }
425
+ }
426
+
427
+ // src/content/domain/predicates/KeywordProminenceClassifier.ts
428
+ var HIGH_DENSITY_THRESHOLD = 2;
429
+ var MEDIUM_DENSITY_THRESHOLD = 1;
430
+ function classifyKeywordProminence(densityPercent) {
431
+ if (densityPercent > HIGH_DENSITY_THRESHOLD) return "high";
432
+ if (densityPercent > MEDIUM_DENSITY_THRESHOLD) return "medium";
433
+ return "low";
434
+ }
435
+ function isUnderutilizedKeyword(densityPercent) {
436
+ return densityPercent < 1;
437
+ }
438
+
439
+ // src/content/domain/predicates/HashtagPriorityRanker.ts
440
+ var DEFAULT_HASHTAG_BUDGET = Number.MAX_SAFE_INTEGER;
441
+ function resolveHashtagBudget(platform) {
442
+ return PLATFORM_SPECS[platform]?.maxHashtags ?? DEFAULT_HASHTAG_BUDGET;
443
+ }
444
+ function rankAndTrimHashtags(hashtags, budget) {
445
+ if (hashtags.length <= budget) return [...hashtags];
446
+ return [...hashtags].sort((a, b) => b.length - a.length).slice(0, budget);
447
+ }
448
+
449
+ // src/content/domain/predicates/PlatformSpecResolver.ts
450
+ var FALLBACK_SPEC = {
451
+ maxLength: 280,
452
+ style: "concise, engaging, audience-friendly",
453
+ emojiSupport: true,
454
+ hashtagSupport: true,
455
+ imageSupport: true,
456
+ videoSupport: true,
457
+ maxHashtags: 5
458
+ };
459
+ function resolvePlatformSpec(platform) {
460
+ return PLATFORM_SPECS[platform] ?? FALLBACK_SPEC;
461
+ }
462
+ function resolveMaxContentLength(platform, override) {
463
+ return override ?? resolvePlatformSpec(platform).maxLength;
464
+ }
465
+
466
+ // src/content/domain/predicates/ScoreRangeGuard.ts
467
+ var SCORE_MIN = 0;
468
+ var SCORE_MAX = 100;
469
+ var SCORE_FALLBACK = 50;
470
+ var CONFIDENCE_MIN = 0;
471
+ var CONFIDENCE_MAX = 1;
472
+ var CONFIDENCE_FALLBACK = 0.5;
473
+ function clampScoreToValidRange(score, fallback = SCORE_FALLBACK) {
474
+ if (!Number.isFinite(score)) return fallback;
475
+ return Math.max(SCORE_MIN, Math.min(SCORE_MAX, score));
476
+ }
477
+ function clampConfidenceToValidRange(confidence, fallback = CONFIDENCE_FALLBACK) {
478
+ if (!Number.isFinite(confidence)) return fallback;
479
+ return Math.max(CONFIDENCE_MIN, Math.min(CONFIDENCE_MAX, confidence));
480
+ }
481
+
482
+ // src/content/domain/predicates/AbTestConfidence.ts
483
+ var AB_TEST_DEFAULT_CONFIDENCE = 0.7;
484
+
485
+ // src/content/domain/predicates/ProviderCostEstimator.ts
486
+ var FAL_FIXED_COST = 1;
487
+ var GROQ_FREE_TIER_COST = 0;
488
+ var GEMINI_IMAGE_COST = 0.02;
489
+ var GEMINI_TEXT_PER_1K_CHARS_COST = 1e-4;
490
+ var PRUNA_FIXED_COST = 1;
491
+ var UNKNOWN_PROVIDER_COST = 0;
492
+ function estimateGroqGenerationCost(_request) {
493
+ return GROQ_FREE_TIER_COST;
494
+ }
495
+ function estimateFalGenerationCost(_request) {
496
+ return FAL_FIXED_COST;
497
+ }
498
+ function estimateGeminiGenerationCost(request) {
499
+ if (request.type === "image") return GEMINI_IMAGE_COST;
500
+ return GEMINI_TEXT_PER_1K_CHARS_COST;
501
+ }
502
+ function estimatePrunaGenerationCost() {
503
+ return PRUNA_FIXED_COST;
504
+ }
505
+ function estimateUnknownProviderCost() {
506
+ return UNKNOWN_PROVIDER_COST;
507
+ }
508
+
509
+ // src/content/domain/predicates/FalImageRenderingOptions.ts
510
+ var FAL_FORMAT_TO_IMAGE_SIZE = {
511
+ square: "square_hd",
512
+ landscape: "landscape_16_9",
513
+ portrait: "portrait_9_16",
514
+ story: "portrait_9_16",
515
+ banner: "landscape_16_9"
516
+ };
517
+ var FAL_DEFAULT_IMAGE_SIZE = "square_hd";
518
+ var FAL_QUALITY_TO_STEPS = {
519
+ standard: 4,
520
+ high: 6,
521
+ ultra: 8
522
+ };
523
+ var FAL_DEFAULT_QUALITY_STEPS = 6;
524
+ function resolveFalImageSize(format) {
525
+ if (!format) return FAL_DEFAULT_IMAGE_SIZE;
526
+ return FAL_FORMAT_TO_IMAGE_SIZE[format] ?? FAL_DEFAULT_IMAGE_SIZE;
527
+ }
528
+ function resolveFalInferenceSteps(quality) {
529
+ if (!quality) return FAL_DEFAULT_QUALITY_STEPS;
530
+ return FAL_QUALITY_TO_STEPS[quality] ?? FAL_DEFAULT_QUALITY_STEPS;
531
+ }
532
+
533
+ // src/content/domain/validation/TopicTextValidator.ts
534
+ function validateTopicText(topic) {
535
+ if (typeof topic !== "string" || topic.trim().length < ValidationLimits.TOPIC.MIN) {
536
+ throw new ValidationError("Topic cannot be empty");
537
+ }
538
+ const trimmed = topic.trim();
539
+ if (trimmed.length > ValidationLimits.TOPIC.MAX) {
540
+ throw new ValidationError(`Topic exceeds maximum length of ${ValidationLimits.TOPIC.MAX} characters`);
541
+ }
542
+ return trimmed;
543
+ }
544
+
545
+ // src/content/domain/validation/ContentBodyValidator.ts
546
+ function validateContentBody(content) {
547
+ if (typeof content !== "string" || content.trim().length < ValidationLimits.CONTENT.MIN) {
548
+ throw new ValidationError("Content cannot be empty");
549
+ }
550
+ const trimmed = content.trim();
551
+ if (trimmed.length > ValidationLimits.CONTENT.MAX) {
552
+ throw new ValidationError(`Content exceeds maximum length of ${ValidationLimits.CONTENT.MAX} characters`);
553
+ }
554
+ return trimmed;
555
+ }
556
+ function truncateForSeoPreview(content) {
557
+ return content.substring(0, ValidationLimits.SEO_CONTENT_PREVIEW);
558
+ }
559
+ function truncateForBlogPromptPreview(content) {
560
+ return content.substring(0, ValidationLimits.BLOG_PROMPT_PREVIEW);
561
+ }
562
+ function truncateForAbVariantPreview(content) {
563
+ return content.substring(0, ValidationLimits.AB_VARIANT_CONTENT_PREVIEW);
564
+ }
565
+
566
+ // src/content/domain/validation/KeywordListValidator.ts
567
+ function validateKeywordList(keywords) {
568
+ if (!Array.isArray(keywords)) {
569
+ throw new ValidationError("Keywords must be an array");
570
+ }
571
+ if (keywords.length > ValidationLimits.KEYWORD.MAX_COUNT) {
572
+ throw new ValidationError(`Cannot process more than ${ValidationLimits.KEYWORD.MAX_COUNT} keywords`);
573
+ }
574
+ return keywords.filter((k) => typeof k === "string" && k.trim().length > 0).map((keyword) => {
575
+ const trimmed = keyword.trim();
576
+ if (trimmed.length > ValidationLimits.KEYWORD.MAX_LENGTH) {
577
+ const preview = trimmed.substring(0, 20);
578
+ throw new ValidationError(
579
+ `Keyword "${preview}..." exceeds maximum length of ${ValidationLimits.KEYWORD.MAX_LENGTH} characters`
580
+ );
581
+ }
582
+ return trimmed;
583
+ });
584
+ }
585
+
586
+ // src/content/domain/validation/DurationSecondsValidator.ts
587
+ function validateDurationSeconds(duration) {
588
+ if (!Number.isFinite(duration)) {
589
+ throw new ValidationError(
590
+ `Duration must be between ${ValidationLimits.DURATION_SECONDS.MIN} and ${ValidationLimits.DURATION_SECONDS.MAX} seconds`
591
+ );
592
+ }
593
+ if (duration < ValidationLimits.DURATION_SECONDS.MIN || duration > ValidationLimits.DURATION_SECONDS.MAX) {
594
+ throw new ValidationError(
595
+ `Duration must be between ${ValidationLimits.DURATION_SECONDS.MIN} and ${ValidationLimits.DURATION_SECONDS.MAX} seconds`
596
+ );
163
597
  }
164
- toString() {
165
- const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
166
- return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
598
+ return duration;
599
+ }
600
+
601
+ // src/content/domain/validation/CalendarDaysValidator.ts
602
+ function validateCalendarDays(days) {
603
+ if (!Number.isFinite(days)) {
604
+ throw new ValidationError(
605
+ `Days must be between ${ValidationLimits.CALENDAR_DAYS.MIN} and ${ValidationLimits.CALENDAR_DAYS.MAX}`
606
+ );
167
607
  }
168
- toJSON() {
169
- return {
170
- name: this.name,
171
- type: this.type,
172
- code: this.code,
173
- message: this.message,
174
- metadata: this.metadata
175
- };
608
+ if (days < ValidationLimits.CALENDAR_DAYS.MIN || days > ValidationLimits.CALENDAR_DAYS.MAX) {
609
+ throw new ValidationError(
610
+ `Days must be between ${ValidationLimits.CALENDAR_DAYS.MIN} and ${ValidationLimits.CALENDAR_DAYS.MAX}`
611
+ );
176
612
  }
177
- };
613
+ return days;
614
+ }
178
615
 
179
- // src/content/domain/errors/AIErrors.ts
180
- var ValidationError = class extends AIError {
181
- constructor(message) {
182
- super(message, "VALIDATION_ERROR");
183
- this.name = "ValidationError";
184
- }
185
- };
186
- var ProviderUnavailableError = class extends AIError {
187
- constructor(providerId) {
188
- super(`Provider ${providerId} is not available`, "PROVIDER_UNAVAILABLE");
189
- this.name = "ProviderUnavailableError";
616
+ // src/content/domain/validation/HashtagCountValidator.ts
617
+ function validateHashtagCount(count) {
618
+ if (!Number.isFinite(count)) {
619
+ throw new ValidationError(
620
+ `Hashtag count must be between ${ValidationLimits.HASHTAG_COUNT.MIN} and ${ValidationLimits.HASHTAG_COUNT.MAX}`
621
+ );
190
622
  }
191
- };
192
- var ProviderQuotaExhaustedError = class extends AIError {
193
- constructor(providerId) {
194
- super(`Provider ${providerId} quota exhausted`, "PROVIDER_QUOTA_EXHAUSTED");
195
- this.name = "ProviderQuotaExhaustedError";
623
+ if (count < ValidationLimits.HASHTAG_COUNT.MIN || count > ValidationLimits.HASHTAG_COUNT.MAX) {
624
+ throw new ValidationError(
625
+ `Hashtag count must be between ${ValidationLimits.HASHTAG_COUNT.MIN} and ${ValidationLimits.HASHTAG_COUNT.MAX}`
626
+ );
196
627
  }
197
- };
628
+ return Math.floor(count);
629
+ }
630
+
631
+ // src/content/domain/utils/IdGenerator.ts
632
+ function generateId(prefix) {
633
+ const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
634
+ return prefix ? `${prefix}-${id}` : id;
635
+ }
198
636
 
199
637
  // src/core/errors/groq-error.ts
200
638
  var GroqError = class extends AIError {
@@ -263,18 +701,6 @@ function isTextProvider(provider) {
263
701
  return provider.capabilities.includes("text");
264
702
  }
265
703
 
266
- // src/content/domain/limits/ProviderTimingConfig.ts
267
- var ProviderTimingConfig = {
268
- DEFAULT_TIMEOUT_MS: 3e4,
269
- DEFAULT_RETRY_ATTEMPTS: 3,
270
- FAL_TIMEOUT_MS: 12e4,
271
- FAL_RETRY_ATTEMPTS: 2,
272
- GEMINI_TIMEOUT_MS: 6e4,
273
- GEMINI_RETRY_ATTEMPTS: 3,
274
- HEALTH_CHECK_TTL_MS: 5 * 60 * 1e3,
275
- RETRY_BACKOFF_BASE_MS: 1e3
276
- };
277
-
278
704
  // src/providers/factory/provider.factory.ts
279
705
  var ProviderFactory = class {
280
706
  providers = /* @__PURE__ */ new Map();
@@ -2048,11 +2474,6 @@ function findLastIndex2(items, predicate) {
2048
2474
  return -1;
2049
2475
  }
2050
2476
 
2051
- // src/content/domain/calculations/RetryBackoffDelay.ts
2052
- function calculateRetryBackoffDelayMs(attemptIndex) {
2053
- return ProviderTimingConfig.RETRY_BACKOFF_BASE_MS * Math.pow(2, attemptIndex);
2054
- }
2055
-
2056
2477
  // src/providers/base/base.provider.ts
2057
2478
  var BaseProvider = class {
2058
2479
  config;
@@ -2101,26 +2522,6 @@ var BaseProvider = class {
2101
2522
  }
2102
2523
  };
2103
2524
 
2104
- // src/content/domain/predicates/ProviderCostEstimator.ts
2105
- var FAL_FIXED_COST = 1;
2106
- var GEMINI_IMAGE_COST = 0.02;
2107
- var GEMINI_TEXT_PER_1K_CHARS_COST = 1e-4;
2108
- function estimateFalGenerationCost(_request) {
2109
- return FAL_FIXED_COST;
2110
- }
2111
- function estimateGeminiGenerationCost(request) {
2112
- if (request.type === "image") return GEMINI_IMAGE_COST;
2113
- return GEMINI_TEXT_PER_1K_CHARS_COST;
2114
- }
2115
-
2116
- // src/content/domain/limits/ModelDefaults.ts
2117
- var ModelDefaults = {
2118
- FAL_IMAGE: "fal-ai/flux/schnell",
2119
- FAL_VIDEO: "fal-ai/hunyuan-video/1",
2120
- FAL_IMAGE_TO_VIDEO: "fal-ai/fast-animatediff/image-to-video",
2121
- GEMINI_TEXT: "gemini-2.0-flash",
2122
- GEMINI_IMAGE: "imagen-4.0-generate-001"};
2123
-
2124
2525
  // src/providers/gemini/gemini.provider.ts
2125
2526
  var GEMINI_TIERS = {
2126
2527
  fast: "gemini-2.0-flash-lite",
@@ -2350,152 +2751,12 @@ function mapGeminiFinishReason(reason) {
2350
2751
  if (reason === "SAFETY" || reason === "PROHIBITED_CONTENT") return "content_filter";
2351
2752
  return void 0;
2352
2753
  }
2353
- function findLastIndex3(items, predicate) {
2354
- for (let i = items.length - 1; i >= 0; i--) {
2355
- if (predicate(items[i])) return i;
2356
- }
2357
- return -1;
2358
- }
2359
-
2360
- // src/content/domain/predicates/KeywordProminenceClassifier.ts
2361
- var HIGH_DENSITY_THRESHOLD = 2;
2362
- var MEDIUM_DENSITY_THRESHOLD = 1;
2363
- function classifyKeywordProminence(densityPercent) {
2364
- if (densityPercent > HIGH_DENSITY_THRESHOLD) return "high";
2365
- if (densityPercent > MEDIUM_DENSITY_THRESHOLD) return "medium";
2366
- return "low";
2367
- }
2368
- function isUnderutilizedKeyword(densityPercent) {
2369
- return densityPercent < 1;
2370
- }
2371
-
2372
- // src/content/domain/types/index.ts
2373
- var PLATFORM_SPECS = {
2374
- twitter: {
2375
- maxLength: 280,
2376
- style: "concise, engaging, thread-friendly",
2377
- emojiSupport: true,
2378
- hashtagSupport: true,
2379
- imageSupport: true,
2380
- videoSupport: true,
2381
- maxHashtags: 5
2382
- },
2383
- linkedin: {
2384
- maxLength: 3e3,
2385
- style: "professional, insightful, industry-focused",
2386
- emojiSupport: true,
2387
- hashtagSupport: true,
2388
- imageSupport: true,
2389
- videoSupport: true,
2390
- maxHashtags: 5
2391
- },
2392
- instagram: {
2393
- maxLength: 2200,
2394
- style: "visual, engaging, emoji-rich",
2395
- emojiSupport: true,
2396
- hashtagSupport: true,
2397
- imageSupport: true,
2398
- videoSupport: true,
2399
- maxHashtags: 30
2400
- },
2401
- threads: {
2402
- maxLength: 500,
2403
- style: "conversational, community-driven",
2404
- emojiSupport: true,
2405
- hashtagSupport: true,
2406
- imageSupport: true,
2407
- videoSupport: true,
2408
- maxHashtags: 5
2409
- },
2410
- tiktok: {
2411
- maxLength: 150,
2412
- style: "catchy, trending, short-form",
2413
- emojiSupport: true,
2414
- hashtagSupport: true,
2415
- imageSupport: false,
2416
- videoSupport: true,
2417
- maxHashtags: 5
2418
- },
2419
- facebook: {
2420
- maxLength: 63206,
2421
- style: "conversational, engaging, shareable",
2422
- emojiSupport: true,
2423
- hashtagSupport: true,
2424
- imageSupport: true,
2425
- videoSupport: true,
2426
- maxHashtags: 5
2427
- }
2428
- };
2429
-
2430
- // src/content/domain/predicates/HashtagPriorityRanker.ts
2431
- var DEFAULT_HASHTAG_BUDGET = Number.MAX_SAFE_INTEGER;
2432
- function resolveHashtagBudget(platform) {
2433
- return PLATFORM_SPECS[platform]?.maxHashtags ?? DEFAULT_HASHTAG_BUDGET;
2434
- }
2435
- function rankAndTrimHashtags(hashtags, budget) {
2436
- if (hashtags.length <= budget) return [...hashtags];
2437
- return [...hashtags].sort((a, b) => b.length - a.length).slice(0, budget);
2438
- }
2439
-
2440
- // src/content/domain/predicates/PlatformSpecResolver.ts
2441
- var FALLBACK_SPEC = {
2442
- maxLength: 280,
2443
- style: "concise, engaging, audience-friendly",
2444
- emojiSupport: true,
2445
- hashtagSupport: true,
2446
- imageSupport: true,
2447
- videoSupport: true,
2448
- maxHashtags: 5
2449
- };
2450
- function resolvePlatformSpec(platform) {
2451
- return PLATFORM_SPECS[platform] ?? FALLBACK_SPEC;
2452
- }
2453
- function resolveMaxContentLength(platform, override) {
2454
- return override ?? resolvePlatformSpec(platform).maxLength;
2455
- }
2456
-
2457
- // src/content/domain/predicates/ScoreRangeGuard.ts
2458
- var SCORE_MIN = 0;
2459
- var SCORE_MAX = 100;
2460
- var SCORE_FALLBACK = 50;
2461
- var CONFIDENCE_MIN = 0;
2462
- var CONFIDENCE_MAX = 1;
2463
- var CONFIDENCE_FALLBACK = 0.5;
2464
- function clampScoreToValidRange(score, fallback = SCORE_FALLBACK) {
2465
- if (!Number.isFinite(score)) return fallback;
2466
- return Math.max(SCORE_MIN, Math.min(SCORE_MAX, score));
2467
- }
2468
- function clampConfidenceToValidRange(confidence, fallback = CONFIDENCE_FALLBACK) {
2469
- if (!Number.isFinite(confidence)) return fallback;
2470
- return Math.max(CONFIDENCE_MIN, Math.min(CONFIDENCE_MAX, confidence));
2471
- }
2472
-
2473
- // src/content/domain/predicates/AbTestConfidence.ts
2474
- var AB_TEST_DEFAULT_CONFIDENCE = 0.7;
2475
-
2476
- // src/content/domain/predicates/FalImageRenderingOptions.ts
2477
- var FAL_FORMAT_TO_IMAGE_SIZE = {
2478
- square: "square_hd",
2479
- landscape: "landscape_16_9",
2480
- portrait: "portrait_9_16",
2481
- story: "portrait_9_16",
2482
- banner: "landscape_16_9"
2483
- };
2484
- var FAL_DEFAULT_IMAGE_SIZE = "square_hd";
2485
- var FAL_QUALITY_TO_STEPS = {
2486
- standard: 4,
2487
- high: 6,
2488
- ultra: 8
2489
- };
2490
- var FAL_DEFAULT_QUALITY_STEPS = 6;
2491
- function resolveFalImageSize(format) {
2492
- if (!format) return FAL_DEFAULT_IMAGE_SIZE;
2493
- return FAL_FORMAT_TO_IMAGE_SIZE[format] ?? FAL_DEFAULT_IMAGE_SIZE;
2494
- }
2495
- function resolveFalInferenceSteps(quality) {
2496
- if (!quality) return FAL_DEFAULT_QUALITY_STEPS;
2497
- return FAL_QUALITY_TO_STEPS[quality] ?? FAL_DEFAULT_QUALITY_STEPS;
2498
- }
2754
+ function findLastIndex3(items, predicate) {
2755
+ for (let i = items.length - 1; i >= 0; i--) {
2756
+ if (predicate(items[i])) return i;
2757
+ }
2758
+ return -1;
2759
+ }
2499
2760
 
2500
2761
  // src/providers/fal/fal.provider.ts
2501
2762
  var FalProvider = class extends BaseProvider {
@@ -2666,39 +2927,810 @@ var FalProvider = class extends BaseProvider {
2666
2927
  }
2667
2928
  };
2668
2929
 
2669
- // src/providers/pruna/pruna-generate.ts
2670
- var prunaCorePromise = null;
2671
- async function loadPrunaCore() {
2672
- if (!prunaCorePromise) {
2673
- const specifier = "@umituz/pruna-provider/core";
2674
- prunaCorePromise = import(specifier);
2930
+ // src/providers/pruna/client/core/constants/limits.ts
2931
+ var DEFAULT_ASPECT_RATIO = "16:9";
2932
+ var P_VIDEO_DEFAULTS = {
2933
+ duration: 5,
2934
+ resolution: "720p",
2935
+ fps: 24,
2936
+ draft: false,
2937
+ promptUpsampling: true
2938
+ };
2939
+ var POLL_DEFAULTS = {
2940
+ intervalMs: 3e3,
2941
+ maxAttempts: 120,
2942
+ maxIntervalMs: 1e4,
2943
+ backoffBase: 1.5,
2944
+ jitterPercent: 0.1
2945
+ };
2946
+ var DEFAULT_DEDUPLICATOR_TTL_MS = 3e4;
2947
+ var DEDUPLICATOR_CLEANUP_GRACE_MS = 100;
2948
+
2949
+ // src/providers/pruna/client/core/constants/network.ts
2950
+ var HTTPS_PREFIX = "https://";
2951
+ var HTTP_PREFIX = "http://";
2952
+ var BASE64_MARKER = "base64,";
2953
+ var PRUNA_BASE_URL = "https://api.pruna.ai";
2954
+ var PRUNA_PREDICTIONS_URL = `${PRUNA_BASE_URL}/v1/predictions`;
2955
+ var PRUNA_FILES_URL = `${PRUNA_BASE_URL}/v1/files`;
2956
+ var AUTH_FAILURE_STATUSES = [401, 403];
2957
+ var MIN_API_KEY_LENGTH = 1;
2958
+ var MAX_API_KEY_LENGTH = 256;
2959
+
2960
+ // src/providers/pruna/client/core/constants/mime-types.ts
2961
+ var MIME_PNG = "image/png";
2962
+ var MIME_JPEG = "image/jpeg";
2963
+ var MIME_WEBP = "image/webp";
2964
+ var DEFAULT_IMAGE_MIME = MIME_PNG;
2965
+ var MIME_FILE_EXTENSION = {
2966
+ [MIME_PNG]: "png",
2967
+ [MIME_JPEG]: "jpg",
2968
+ [MIME_WEBP]: "webp"
2969
+ };
2970
+ var IMAGE_SIGNATURES = {
2971
+ PNG: [137, 80],
2972
+ JPEG: [255, 216],
2973
+ WEBP_RIFF: [82, 73]
2974
+ };
2975
+
2976
+ // src/providers/pruna/client/core/errors/cancellation-error.ts
2977
+ var CANCELLATION_MESSAGE = "Request cancelled by user";
2978
+ var CancellationError = class extends Error {
2979
+ constructor(message = CANCELLATION_MESSAGE) {
2980
+ super(message);
2981
+ this.name = "CancellationError";
2982
+ }
2983
+ };
2984
+
2985
+ // src/providers/pruna/client/core/errors/authentication-error.ts
2986
+ var AUTHENTICATION_MESSAGE = "Authentication failed. Please check your API key.";
2987
+ var AuthenticationError = class extends Error {
2988
+ constructor(message = AUTHENTICATION_MESSAGE) {
2989
+ super(message);
2990
+ this.name = "AuthenticationError";
2991
+ }
2992
+ };
2993
+
2994
+ // src/providers/pruna/client/core/errors/timeout-error.ts
2995
+ var TIMEOUT_MESSAGE = "Generation timed out. Maximum polling attempts reached.";
2996
+ var TimeoutError = class extends Error {
2997
+ constructor(message = TIMEOUT_MESSAGE) {
2998
+ super(message);
2999
+ this.name = "TimeoutError";
3000
+ }
3001
+ };
3002
+
3003
+ // src/providers/pruna/client/core/errors/validation-error.ts
3004
+ var ValidationError2 = class extends Error {
3005
+ field;
3006
+ reason;
3007
+ receivedValue;
3008
+ constructor(context) {
3009
+ super(`Invalid ${context.field}: ${context.reason}`);
3010
+ this.name = "ValidationError";
3011
+ this.field = context.field;
3012
+ this.reason = context.reason;
3013
+ this.receivedValue = context.receivedValue;
3014
+ }
3015
+ };
3016
+
3017
+ // src/providers/pruna/client/core/utils/base64.ts
3018
+ var globalScope = globalThis;
3019
+ var hasBuffer = typeof globalScope.Buffer?.from === "function";
3020
+ function base64ToBytes(raw) {
3021
+ if (hasBuffer) {
3022
+ const bufferFactory = globalScope.Buffer;
3023
+ if (!bufferFactory) {
3024
+ throw new Error("Buffer unavailable despite detection flag");
3025
+ }
3026
+ const buf = bufferFactory.from(raw, "base64");
3027
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
2675
3028
  }
2676
- return prunaCorePromise;
3029
+ const decoded = atob(raw);
3030
+ return Uint8Array.from(decoded, (char) => char.charCodeAt(0));
2677
3031
  }
2678
- async function* generateWithPruna(request, apiKey, signal, onProgress) {
2679
- const prunaCore = await loadPrunaCore();
2680
- if (prunaCore.generateWithPruna) {
2681
- yield* prunaCore.generateWithPruna(request, apiKey, signal, onProgress);
2682
- return;
2683
- }
2684
- if (!prunaCore.submitPrediction || !prunaCore.pollForResult) {
2685
- throw new Error("@umituz/pruna-provider/core does not export required functions");
2686
- }
2687
- const modelId = request.model || (request.image ? "p-image-edit" : "p-image");
2688
- const prediction = await prunaCore.submitPrediction(modelId, request, apiKey, signal, onProgress);
2689
- if (prediction.output && typeof prediction.output === "object" && "url" in prediction.output) {
2690
- yield {
2691
- stage: "predicting",
2692
- result: { url: prediction.output.url, model: modelId }
3032
+
3033
+ // src/providers/pruna/client/core/utils/mime-detector.ts
3034
+ var MIN_SIGNATURE_BYTES = 2;
3035
+ function matchesSignature(bytes, signature) {
3036
+ if (bytes.length < signature.length) return false;
3037
+ return signature.every((value, index) => bytes[index] === value);
3038
+ }
3039
+ function detectImageMime(bytes) {
3040
+ if (bytes.length < MIN_SIGNATURE_BYTES) return DEFAULT_IMAGE_MIME;
3041
+ if (matchesSignature(bytes, IMAGE_SIGNATURES.PNG)) return MIME_PNG;
3042
+ if (matchesSignature(bytes, IMAGE_SIGNATURES.JPEG)) return MIME_JPEG;
3043
+ if (matchesSignature(bytes, IMAGE_SIGNATURES.WEBP_RIFF)) return MIME_WEBP;
3044
+ return DEFAULT_IMAGE_MIME;
3045
+ }
3046
+
3047
+ // src/providers/pruna/client/core/utils/api-error-parser.ts
3048
+ async function parseApiError(response, fallback) {
3049
+ const payload = await response.json().catch(() => ({ message: response.statusText }));
3050
+ const message = payload.message ?? `${fallback}: ${response.status}`;
3051
+ const error = new Error(message);
3052
+ error.statusCode = response.status;
3053
+ return error;
3054
+ }
3055
+
3056
+ // src/providers/pruna/client/core/protocol/http-status.ts
3057
+ function isAuthFailureStatus(status) {
3058
+ return AUTH_FAILURE_STATUSES.includes(status);
3059
+ }
3060
+
3061
+ // src/providers/pruna/client/core/protocol/job-status.ts
3062
+ var TERMINAL_SUCCESS_STATUSES = ["succeeded", "completed"];
3063
+ var TERMINAL_FAILURE_STATUSES = ["failed"];
3064
+ function isTerminalSuccessStatus(status) {
3065
+ if (!status) return false;
3066
+ return TERMINAL_SUCCESS_STATUSES.includes(status);
3067
+ }
3068
+ function isTerminalFailureStatus(status) {
3069
+ if (!status) return false;
3070
+ return TERMINAL_FAILURE_STATUSES.includes(status);
3071
+ }
3072
+
3073
+ // src/providers/pruna/client/core/protocol/abort-signal-classifier.ts
3074
+ function isAbortError(err) {
3075
+ if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true;
3076
+ if (err instanceof Error && err.name === "AbortError") return true;
3077
+ return false;
3078
+ }
3079
+
3080
+ // src/providers/pruna/client/core/protocol/pruna-url-builder.ts
3081
+ function isAbsoluteHttpUrl(value) {
3082
+ return value.startsWith(HTTPS_PREFIX) || value.startsWith(HTTP_PREFIX);
3083
+ }
3084
+ function isHttpsUrl(value) {
3085
+ return value.startsWith(HTTPS_PREFIX);
3086
+ }
3087
+ function buildPrunaApiUrl(pathOrUrl) {
3088
+ if (isAbsoluteHttpUrl(pathOrUrl)) return pathOrUrl;
3089
+ return `${PRUNA_BASE_URL}${pathOrUrl}`;
3090
+ }
3091
+ function buildFileUrlById(fileId) {
3092
+ return `${PRUNA_FILES_URL}/${fileId}`;
3093
+ }
3094
+
3095
+ // src/providers/pruna/client/core/math/exponential-backoff.ts
3096
+ function calculateExponentialBackoff2(inputs) {
3097
+ const {
3098
+ attempt,
3099
+ baseIntervalMs,
3100
+ maxIntervalMs = POLL_DEFAULTS.maxIntervalMs,
3101
+ jitterPercent = POLL_DEFAULTS.jitterPercent,
3102
+ growthBase = POLL_DEFAULTS.backoffBase
3103
+ } = inputs;
3104
+ const safeAttempt = Math.max(0, attempt);
3105
+ const safeBase = Math.max(0, baseIntervalMs);
3106
+ const exponentialDelay = Math.min(safeBase * Math.pow(growthBase, safeAttempt), maxIntervalMs);
3107
+ const jitter = exponentialDelay * jitterPercent * Math.random();
3108
+ return exponentialDelay + jitter;
3109
+ }
3110
+
3111
+ // src/providers/pruna/client/core/utils/uri-resolver.ts
3112
+ var uriCache = /* @__PURE__ */ new WeakMap();
3113
+ function stripBase64Prefix(image) {
3114
+ if (isHttpsUrl(image)) return image;
3115
+ const base64Index = image.indexOf(BASE64_MARKER);
3116
+ return base64Index !== -1 ? image.slice(base64Index + BASE64_MARKER.length) : image;
3117
+ }
3118
+ function readOutput(output) {
3119
+ if (typeof output === "string") return output;
3120
+ if (Array.isArray(output) && output.length > 0) return output[0] ?? null;
3121
+ if (output !== null && typeof output === "object" && "url" in output) {
3122
+ return output.url;
3123
+ }
3124
+ return null;
3125
+ }
3126
+ function extractUri(data) {
3127
+ const cached = uriCache.get(data);
3128
+ if (cached !== void 0) return cached;
3129
+ if (!isTerminalSuccessStatus(data.status)) {
3130
+ uriCache.set(data, null);
3131
+ return null;
3132
+ }
3133
+ const result = data.generation_url ?? readOutput(data.output) ?? data.video_url ?? data.data ?? null;
3134
+ uriCache.set(data, result);
3135
+ return result;
3136
+ }
3137
+ function resolveUri(uri) {
3138
+ return uri.startsWith("/") ? `${PRUNA_BASE_URL}${uri}` : uri;
3139
+ }
3140
+
3141
+ // src/providers/pruna/client/core/services/pruna-client.service.ts
3142
+ function buildFileExtension(mime) {
3143
+ return MIME_FILE_EXTENSION[mime] ?? "bin";
3144
+ }
3145
+ async function uploadImage(base64OrUrl, apiKey, onProgress) {
3146
+ if (isHttpsUrl(base64OrUrl)) return base64OrUrl;
3147
+ if (base64OrUrl.startsWith(HTTP_PREFIX)) {
3148
+ throw new Error("Only HTTPS URLs are supported for security.");
3149
+ }
3150
+ onProgress?.("uploading");
3151
+ const raw = stripBase64Prefix(base64OrUrl);
3152
+ let bytes;
3153
+ try {
3154
+ bytes = base64ToBytes(raw);
3155
+ } catch {
3156
+ throw new Error("Invalid image format. Provide base64 or a valid HTTPS URL.");
3157
+ }
3158
+ const mime = detectImageMime(bytes);
3159
+ const buffer = bytes.buffer.slice(
3160
+ bytes.byteOffset,
3161
+ bytes.byteOffset + bytes.byteLength
3162
+ );
3163
+ const blob = new Blob([buffer], { type: mime });
3164
+ const formData = new FormData();
3165
+ formData.append("content", blob, `upload.${buildFileExtension(mime)}`);
3166
+ const res = await fetch(PRUNA_FILES_URL, {
3167
+ method: "POST",
3168
+ headers: { apikey: apiKey },
3169
+ body: formData
3170
+ });
3171
+ if (!res.ok) {
3172
+ throw await parseApiError(res, "File upload error");
3173
+ }
3174
+ const data = await res.json();
3175
+ return data.urls?.get ?? buildFileUrlById(data.id ?? "");
3176
+ }
3177
+ async function submitPrediction(model, input, apiKey, signal, onProgress) {
3178
+ onProgress?.("predicting");
3179
+ const res = await fetch(PRUNA_PREDICTIONS_URL, {
3180
+ method: "POST",
3181
+ headers: {
3182
+ apikey: apiKey,
3183
+ Model: model,
3184
+ "Try-Sync": "true",
3185
+ "Content-Type": "application/json"
3186
+ },
3187
+ body: JSON.stringify({ input }),
3188
+ signal
3189
+ });
3190
+ if (!res.ok) {
3191
+ throw await parseApiError(res, "API error");
3192
+ }
3193
+ return res.json();
3194
+ }
3195
+ var GenerationFailedError = class extends Error {
3196
+ constructor(message) {
3197
+ super(message);
3198
+ this.name = "GenerationFailedError";
3199
+ }
3200
+ };
3201
+ async function attemptPoll(fullUrl, apiKey, signal) {
3202
+ let res;
3203
+ try {
3204
+ res = await fetch(fullUrl, { headers: { apikey: apiKey }, signal });
3205
+ } catch (err) {
3206
+ if (isAbortError(err)) throw new CancellationError();
3207
+ return { outcome: "transient-failure" };
3208
+ }
3209
+ if (!res.ok) {
3210
+ if (isAuthFailureStatus(res.status)) throw new AuthenticationError();
3211
+ return { outcome: "transient-failure" };
3212
+ }
3213
+ let data;
3214
+ try {
3215
+ data = await res.json();
3216
+ } catch {
3217
+ return { outcome: "transient-failure" };
3218
+ }
3219
+ if (isTerminalSuccessStatus(data.status)) {
3220
+ const uri = extractUri(data);
3221
+ if (uri) return { outcome: "succeeded", uri: resolveUri(uri) };
3222
+ return { outcome: "transient-failure" };
3223
+ }
3224
+ if (isTerminalFailureStatus(data.status)) {
3225
+ throw new GenerationFailedError(data.error ?? "Generation failed during processing.");
3226
+ }
3227
+ return { outcome: "continue" };
3228
+ }
3229
+ async function pollForResult(pollUrl, apiKey, maxAttempts = POLL_DEFAULTS.maxAttempts, intervalMs = POLL_DEFAULTS.intervalMs, signal, onProgress) {
3230
+ const fullUrl = isAbsoluteHttpUrl(pollUrl) ? pollUrl : buildPrunaApiUrl(pollUrl);
3231
+ const controller = new AbortController();
3232
+ const onExternalAbort = () => controller.abort();
3233
+ signal?.addEventListener("abort", onExternalAbort);
3234
+ try {
3235
+ for (let i = 0; i < maxAttempts; i++) {
3236
+ if (signal?.aborted) throw new CancellationError();
3237
+ const delay = calculateExponentialBackoff2({ attempt: i, baseIntervalMs: intervalMs });
3238
+ await new Promise((resolve) => setTimeout(resolve, delay));
3239
+ if (signal?.aborted) throw new CancellationError();
3240
+ onProgress?.("polling", i + 1);
3241
+ const result = await attemptPoll(fullUrl, apiKey, controller.signal);
3242
+ if (result.outcome === "succeeded" && result.uri) {
3243
+ return result.uri;
3244
+ }
3245
+ }
3246
+ throw new TimeoutError();
3247
+ } finally {
3248
+ signal?.removeEventListener("abort", onExternalAbort);
3249
+ controller.abort();
3250
+ }
3251
+ }
3252
+
3253
+ // src/providers/pruna/client/core/math/comparison-hash.ts
3254
+ function sortKeysDeep(value) {
3255
+ if (Array.isArray(value)) {
3256
+ return value.map(sortKeysDeep);
3257
+ }
3258
+ if (value !== null && typeof value === "object") {
3259
+ const record = value;
3260
+ return Object.keys(record).sort().reduce((acc, key) => {
3261
+ acc[key] = sortKeysDeep(record[key]);
3262
+ return acc;
3263
+ }, {});
3264
+ }
3265
+ return value;
3266
+ }
3267
+ function hashValue(value) {
3268
+ if (value === null || typeof value !== "object") {
3269
+ return String(value);
3270
+ }
3271
+ try {
3272
+ return JSON.stringify(sortKeysDeep(value));
3273
+ } catch {
3274
+ return String(value);
3275
+ }
3276
+ }
3277
+ function compositeHash(...values) {
3278
+ return values.map(hashValue).join("|");
3279
+ }
3280
+
3281
+ // src/providers/pruna/client/core/math/cache-key-derivation.ts
3282
+ function deriveGenerationCacheKey(apiKey, input) {
3283
+ return `${apiKey}:${compositeHash(input)}`;
3284
+ }
3285
+
3286
+ // src/providers/pruna/client/core/validation/api-key-validator.ts
3287
+ function assertValidApiKey(apiKey) {
3288
+ if (typeof apiKey !== "string") {
3289
+ throw new ValidationError2({
3290
+ field: "apiKey",
3291
+ reason: "must be a string",
3292
+ receivedValue: typeof apiKey
3293
+ });
3294
+ }
3295
+ if (apiKey.length < MIN_API_KEY_LENGTH) {
3296
+ throw new ValidationError2({
3297
+ field: "apiKey",
3298
+ reason: "must not be empty",
3299
+ receivedValue: apiKey
3300
+ });
3301
+ }
3302
+ if (apiKey.length > MAX_API_KEY_LENGTH) {
3303
+ throw new ValidationError2({
3304
+ field: "apiKey",
3305
+ reason: `exceeds maximum length of ${MAX_API_KEY_LENGTH}`,
3306
+ receivedValue: apiKey.length
3307
+ });
3308
+ }
3309
+ }
3310
+
3311
+ // src/providers/pruna/client/core/validation/image-source-validator.ts
3312
+ function classifyImageSource(source) {
3313
+ if (source.startsWith(HTTPS_PREFIX)) return "https-url";
3314
+ if (source.startsWith(HTTP_PREFIX)) return "http-url";
3315
+ if (source.includes(BASE64_MARKER) || /^[A-Za-z0-9+/=]+$/.test(source)) return "base64";
3316
+ return "unknown";
3317
+ }
3318
+ function assertSecureImageSource(image, fieldName = "image") {
3319
+ if (typeof image !== "string") {
3320
+ throw new ValidationError2({
3321
+ field: fieldName,
3322
+ reason: "must be a string",
3323
+ receivedValue: typeof image
3324
+ });
3325
+ }
3326
+ const kind = classifyImageSource(image);
3327
+ if (kind === "http-url") {
3328
+ throw new ValidationError2({
3329
+ field: fieldName,
3330
+ reason: "HTTP URLs are not supported; use HTTPS or a base64 data URI",
3331
+ receivedValue: image
3332
+ });
3333
+ }
3334
+ if (kind === "unknown") {
3335
+ throw new ValidationError2({
3336
+ field: fieldName,
3337
+ reason: "must be an HTTPS URL or a base64 data URI",
3338
+ receivedValue: typeof image
3339
+ });
3340
+ }
3341
+ }
3342
+
3343
+ // src/providers/pruna/client/core/validation/aspect-ratio-validator.ts
3344
+ var SUPPORTED_ASPECT_RATIOS = [
3345
+ "16:9",
3346
+ "9:16",
3347
+ "1:1",
3348
+ "4:3",
3349
+ "3:4",
3350
+ "3:2",
3351
+ "2:3"
3352
+ ];
3353
+ function isSupportedAspectRatio(value) {
3354
+ return typeof value === "string" && SUPPORTED_ASPECT_RATIOS.includes(value);
3355
+ }
3356
+ function assertValidAspectRatio(value, fieldName = "aspect_ratio") {
3357
+ if (value === void 0 || value === null) return;
3358
+ if (!isSupportedAspectRatio(value)) {
3359
+ throw new ValidationError2({
3360
+ field: fieldName,
3361
+ reason: `must be one of ${SUPPORTED_ASPECT_RATIOS.join(", ")}`,
3362
+ receivedValue: value
3363
+ });
3364
+ }
3365
+ }
3366
+
3367
+ // src/providers/pruna/client/core/validation/resolution-validator.ts
3368
+ var SUPPORTED_RESOLUTIONS = ["720p", "1080p"];
3369
+ function isSupportedResolution(value) {
3370
+ return typeof value === "string" && SUPPORTED_RESOLUTIONS.includes(value);
3371
+ }
3372
+ function assertValidResolution(value, fieldName = "resolution") {
3373
+ if (value === void 0 || value === null) return;
3374
+ if (!isSupportedResolution(value)) {
3375
+ throw new ValidationError2({
3376
+ field: fieldName,
3377
+ reason: `must be one of ${SUPPORTED_RESOLUTIONS.join(", ")}`,
3378
+ receivedValue: value
3379
+ });
3380
+ }
3381
+ }
3382
+
3383
+ // src/providers/pruna/client/core/validation/duration-validator.ts
3384
+ var MIN_VIDEO_DURATION_SECONDS = 1;
3385
+ var MAX_VIDEO_DURATION_SECONDS = 30;
3386
+ function isValidDuration(value) {
3387
+ return typeof value === "number" && Number.isFinite(value) && value >= MIN_VIDEO_DURATION_SECONDS && value <= MAX_VIDEO_DURATION_SECONDS && Number.isInteger(value);
3388
+ }
3389
+ function assertValidDuration(value, fieldName = "duration") {
3390
+ if (value === void 0 || value === null) return;
3391
+ if (!isValidDuration(value)) {
3392
+ throw new ValidationError2({
3393
+ field: fieldName,
3394
+ reason: `must be an integer between ${MIN_VIDEO_DURATION_SECONDS} and ${MAX_VIDEO_DURATION_SECONDS} seconds`,
3395
+ receivedValue: value
3396
+ });
3397
+ }
3398
+ }
3399
+
3400
+ // src/providers/pruna/client/core/validation/prompt-validator.ts
3401
+ var MIN_PROMPT_LENGTH = 1;
3402
+ var MAX_PROMPT_LENGTH = 4e3;
3403
+ function assertValidPrompt(value, fieldName = "prompt") {
3404
+ if (typeof value !== "string") {
3405
+ throw new ValidationError2({
3406
+ field: fieldName,
3407
+ reason: "must be a string",
3408
+ receivedValue: typeof value
3409
+ });
3410
+ }
3411
+ if (value.trim().length < MIN_PROMPT_LENGTH) {
3412
+ throw new ValidationError2({
3413
+ field: fieldName,
3414
+ reason: "must not be empty or whitespace-only",
3415
+ receivedValue: value
3416
+ });
3417
+ }
3418
+ if (value.length > MAX_PROMPT_LENGTH) {
3419
+ throw new ValidationError2({
3420
+ field: fieldName,
3421
+ reason: `exceeds maximum length of ${MAX_PROMPT_LENGTH} characters`,
3422
+ receivedValue: value.length
3423
+ });
3424
+ }
3425
+ }
3426
+
3427
+ // src/providers/pruna/client/core/validation/seed-validator.ts
3428
+ var MIN_SEED = -2147483648;
3429
+ var MAX_SEED = 2147483647;
3430
+ function isValidSeed(value) {
3431
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= MIN_SEED && value <= MAX_SEED;
3432
+ }
3433
+ function assertValidSeed(value, fieldName = "seed") {
3434
+ if (value === void 0 || value === null) return;
3435
+ if (!isValidSeed(value)) {
3436
+ throw new ValidationError2({
3437
+ field: fieldName,
3438
+ reason: `must be an integer in [${MIN_SEED}, ${MAX_SEED}]`,
3439
+ receivedValue: value
3440
+ });
3441
+ }
3442
+ }
3443
+
3444
+ // src/providers/pruna/client/core/protocol/input-type-guard.ts
3445
+ function isTextToImageInput(input) {
3446
+ return input.model === "p-image";
3447
+ }
3448
+ function isImageToImageInput(input) {
3449
+ return input.model === "p-image-edit";
3450
+ }
3451
+ function isImageToVideoInput(input) {
3452
+ return input.model === "p-video";
3453
+ }
3454
+
3455
+ // src/providers/pruna/client/core/utils/request-deduplicator.ts
3456
+ var RequestDeduplicator2 = class {
3457
+ pendingRequests = /* @__PURE__ */ new Map();
3458
+ ttl;
3459
+ constructor(ttl = DEFAULT_DEDUPLICATOR_TTL_MS) {
3460
+ this.ttl = ttl;
3461
+ }
3462
+ execute(key, fn) {
3463
+ const existing = this.pendingRequests.get(key);
3464
+ if (existing) {
3465
+ if (Date.now() - existing.timestamp < this.ttl) {
3466
+ return existing.promise;
3467
+ }
3468
+ this.pendingRequests.delete(key);
3469
+ }
3470
+ const promise = fn().finally(() => {
3471
+ setTimeout(() => {
3472
+ this.pendingRequests.delete(key);
3473
+ }, DEDUPLICATOR_CLEANUP_GRACE_MS);
3474
+ });
3475
+ this.pendingRequests.set(key, { promise, timestamp: Date.now() });
3476
+ return promise;
3477
+ }
3478
+ clear() {
3479
+ this.pendingRequests.clear();
3480
+ }
3481
+ size() {
3482
+ this.purgeExpired();
3483
+ return this.pendingRequests.size;
3484
+ }
3485
+ purgeExpired() {
3486
+ const now = Date.now();
3487
+ for (const [key, value] of this.pendingRequests) {
3488
+ if (now - value.timestamp >= this.ttl) {
3489
+ this.pendingRequests.delete(key);
3490
+ }
3491
+ }
3492
+ }
3493
+ };
3494
+ var globalDeduplicator = new RequestDeduplicator2();
3495
+
3496
+ // src/providers/pruna/client/generation/builders/text-to-image.builder.ts
3497
+ var TextToImageInputBuilder = class {
3498
+ model = "p-image";
3499
+ build(input, _context) {
3500
+ const payload = {
3501
+ prompt: input.prompt,
3502
+ aspect_ratio: input.aspect_ratio ?? DEFAULT_ASPECT_RATIO
3503
+ };
3504
+ if (input.seed !== void 0) {
3505
+ payload.seed = input.seed;
3506
+ }
3507
+ return Promise.resolve(payload);
3508
+ }
3509
+ };
3510
+
3511
+ // src/providers/pruna/client/generation/builders/image-to-image.builder.ts
3512
+ var ImageToImageInputBuilder = class {
3513
+ model = "p-image-edit";
3514
+ build(input, _context) {
3515
+ const payload = {
3516
+ images: [stripBase64Prefix(input.image)],
3517
+ prompt: input.prompt,
3518
+ aspect_ratio: input.aspect_ratio ?? DEFAULT_ASPECT_RATIO
3519
+ };
3520
+ if (input.seed !== void 0) {
3521
+ payload.seed = input.seed;
3522
+ }
3523
+ return Promise.resolve(payload);
3524
+ }
3525
+ };
3526
+
3527
+ // src/providers/pruna/client/generation/builders/image-to-video.builder.ts
3528
+ var ImageToVideoInputBuilder = class {
3529
+ model = "p-video";
3530
+ async build(input, context) {
3531
+ if (context.signal?.aborted) {
3532
+ throw new CancellationError();
3533
+ }
3534
+ const fileUrl = await uploadImage(
3535
+ input.image,
3536
+ context.apiKey,
3537
+ context.onProgress
3538
+ );
3539
+ return {
3540
+ image: fileUrl,
3541
+ prompt: input.prompt,
3542
+ duration: input.duration ?? P_VIDEO_DEFAULTS.duration,
3543
+ resolution: input.resolution ?? P_VIDEO_DEFAULTS.resolution,
3544
+ fps: P_VIDEO_DEFAULTS.fps,
3545
+ draft: input.draft ?? P_VIDEO_DEFAULTS.draft,
3546
+ aspect_ratio: input.aspect_ratio ?? DEFAULT_ASPECT_RATIO,
3547
+ prompt_upsampling: P_VIDEO_DEFAULTS.promptUpsampling
2693
3548
  };
2694
- return;
2695
3549
  }
2696
- const pollUrl = prediction.status_url || prediction.generation_url;
3550
+ };
3551
+
3552
+ // src/providers/pruna/client/generation/builders/model-input-builder.ts
3553
+ var ModelInputBuilderRegistry = class {
3554
+ builders = /* @__PURE__ */ new Map();
3555
+ register(builder) {
3556
+ this.builders.set(builder.model, builder);
3557
+ return this;
3558
+ }
3559
+ resolve(model) {
3560
+ const builder = this.builders.get(model);
3561
+ if (!builder) {
3562
+ throw new Error(`No input builder registered for model: ${model}`);
3563
+ }
3564
+ return builder;
3565
+ }
3566
+ };
3567
+
3568
+ // src/providers/pruna/client/generation/builders/builder-factory.ts
3569
+ function createDefaultBuilderRegistry() {
3570
+ return new ModelInputBuilderRegistry().register(new TextToImageInputBuilder()).register(new ImageToImageInputBuilder()).register(new ImageToVideoInputBuilder());
3571
+ }
3572
+
3573
+ // src/providers/pruna/client/generation/services/generation.service.ts
3574
+ var builderRegistry = createDefaultBuilderRegistry();
3575
+ function validateGenerateInput(input) {
3576
+ assertValidPrompt(input.prompt, `${input.model}.prompt`);
3577
+ assertValidAspectRatio(input.aspect_ratio, `${input.model}.aspect_ratio`);
3578
+ if (isTextToImageInput(input)) {
3579
+ assertValidSeed(input.seed, `${input.model}.seed`);
3580
+ } else if (isImageToImageInput(input)) {
3581
+ assertSecureImageSource(input.image, `${input.model}.image`);
3582
+ assertValidSeed(input.seed, `${input.model}.seed`);
3583
+ } else if (isImageToVideoInput(input)) {
3584
+ assertSecureImageSource(input.image, `${input.model}.image`);
3585
+ assertValidDuration(input.duration, `${input.model}.duration`);
3586
+ assertValidResolution(input.resolution, `${input.model}.resolution`);
3587
+ }
3588
+ }
3589
+ async function generate(apiKey, input, options = {}) {
3590
+ assertValidApiKey(apiKey);
3591
+ validateGenerateInput(input);
3592
+ const { signal, onProgress, useCache = true } = options;
3593
+ if (useCache) {
3594
+ const cacheKey = deriveGenerationCacheKey(apiKey, input);
3595
+ return globalDeduplicator.execute(
3596
+ cacheKey,
3597
+ () => generateInternal(apiKey, input, { signal, onProgress })
3598
+ );
3599
+ }
3600
+ return generateInternal(apiKey, input, { signal, onProgress });
3601
+ }
3602
+ async function generateInternal(apiKey, input, options) {
3603
+ const { signal, onProgress } = options;
3604
+ const builder = builderRegistry.resolve(input.model);
3605
+ const modelInput = await builder.build(input, { apiKey, signal, onProgress });
3606
+ const response = await submitPrediction(input.model, modelInput, apiKey, signal, onProgress);
3607
+ const syncUri = extractUri(response);
3608
+ if (syncUri) {
3609
+ return { url: resolveUri(syncUri), model: input.model };
3610
+ }
3611
+ const pollUrl = response.get_url ?? response.status_url;
2697
3612
  if (!pollUrl) {
2698
- throw new Error("Pruna prediction response missing polling URL");
3613
+ throw new Error("Pruna API returned no result and no polling URL.");
3614
+ }
3615
+ const url = await pollForResult(
3616
+ pollUrl,
3617
+ apiKey,
3618
+ POLL_DEFAULTS.maxAttempts,
3619
+ POLL_DEFAULTS.intervalMs,
3620
+ signal,
3621
+ onProgress
3622
+ );
3623
+ return { url, model: input.model };
3624
+ }
3625
+ async function generateImageThenVideo(apiKey, input, options = {}) {
3626
+ assertValidApiKey(apiKey);
3627
+ assertValidPrompt(input.prompt, "text-to-video.prompt");
3628
+ assertValidAspectRatio(input.aspect_ratio, "text-to-video.aspect_ratio");
3629
+ assertValidDuration(input.duration, "text-to-video.duration");
3630
+ assertValidResolution(input.resolution, "text-to-video.resolution");
3631
+ const { signal, onProgress } = options;
3632
+ const aspectRatio = input.aspect_ratio ?? DEFAULT_ASPECT_RATIO;
3633
+ onProgress?.("predicting", 1);
3634
+ const imageResult = await generate(
3635
+ apiKey,
3636
+ { model: "p-image", prompt: input.prompt, aspect_ratio: aspectRatio },
3637
+ { signal }
3638
+ );
3639
+ onProgress?.("predicting", 2);
3640
+ return generate(
3641
+ apiKey,
3642
+ {
3643
+ model: "p-video",
3644
+ prompt: input.prompt,
3645
+ image: imageResult.url,
3646
+ duration: input.duration ?? P_VIDEO_DEFAULTS.duration,
3647
+ resolution: input.resolution ?? P_VIDEO_DEFAULTS.resolution,
3648
+ aspect_ratio: aspectRatio,
3649
+ draft: input.draft ?? P_VIDEO_DEFAULTS.draft
3650
+ },
3651
+ { signal, onProgress }
3652
+ );
3653
+ }
3654
+
3655
+ // src/providers/pruna/pruna-generate.ts
3656
+ async function* generateWithPruna(request, apiKey, signal, onProgress) {
3657
+ const stages = [];
3658
+ let notify = null;
3659
+ let done = false;
3660
+ const wake = () => {
3661
+ const fn = notify;
3662
+ if (fn) {
3663
+ notify = null;
3664
+ fn();
3665
+ }
3666
+ };
3667
+ const handleProgress = (stage) => {
3668
+ stages.push({ stage });
3669
+ wake();
3670
+ };
3671
+ const promise = (async () => {
3672
+ try {
3673
+ const model = request.model ?? (request.image ? "p-image-edit" : "p-image");
3674
+ if (model === "p-video" && !request.image && request.prompt) {
3675
+ const videoRes = await generateImageThenVideo(
3676
+ apiKey,
3677
+ {
3678
+ prompt: request.prompt,
3679
+ duration: request.duration ?? 5,
3680
+ aspect_ratio: request.aspectRatio ?? request.aspect_ratio ?? "16:9"
3681
+ },
3682
+ { signal, onProgress: handleProgress }
3683
+ );
3684
+ stages.push({
3685
+ stage: "polling",
3686
+ result: { url: videoRes.url, model: "p-video" }
3687
+ });
3688
+ return;
3689
+ }
3690
+ const input = {
3691
+ model,
3692
+ prompt: request.prompt ?? "",
3693
+ aspect_ratio: request.aspectRatio ?? request.aspect_ratio ?? "1:1",
3694
+ ...request.image ? { image: request.image } : {},
3695
+ ...request.duration ? { duration: request.duration } : {},
3696
+ ...request.resolution ? { resolution: request.resolution } : {},
3697
+ ...request.seed !== void 0 ? { seed: request.seed } : {}
3698
+ };
3699
+ const res = await generate(apiKey, input, {
3700
+ signal,
3701
+ onProgress: handleProgress
3702
+ });
3703
+ stages.push({
3704
+ stage: "polling",
3705
+ result: { url: res.url, model: res.model }
3706
+ });
3707
+ } catch (err) {
3708
+ stages.push({
3709
+ stage: "processing",
3710
+ error: err instanceof Error ? err : new Error(String(err))
3711
+ });
3712
+ } finally {
3713
+ done = true;
3714
+ wake();
3715
+ }
3716
+ })();
3717
+ while (!done || stages.length > 0) {
3718
+ if (stages.length === 0 && !done) {
3719
+ await new Promise((resolve) => {
3720
+ notify = resolve;
3721
+ });
3722
+ notify = null;
3723
+ }
3724
+ while (stages.length > 0) {
3725
+ const nextStage = stages.shift();
3726
+ if (!nextStage) continue;
3727
+ yield nextStage;
3728
+ if (nextStage.error) {
3729
+ throw nextStage.error;
3730
+ }
3731
+ }
2699
3732
  }
2700
- const resultUrl = await prunaCore.pollForResult(pollUrl, apiKey, 60, 2e3, signal, onProgress);
2701
- yield { stage: "polling", result: { url: resultUrl, model: modelId } };
3733
+ await promise;
2702
3734
  }
2703
3735
 
2704
3736
  // src/providers/pruna/pruna.provider.ts
@@ -2791,40 +3823,21 @@ function createAI(config = {}) {
2791
3823
  defaultTier: config.defaultTier
2792
3824
  });
2793
3825
  if (config.anthropic?.apiKey) {
2794
- factory.register(new AnthropicProvider(config.anthropic));
2795
- }
2796
- if (config.groq?.apiKey) {
2797
- factory.register(new GroqProvider(config.groq));
2798
- }
2799
- if (config.gemini?.apiKey) {
2800
- factory.register(new GeminiProvider({ ...config.gemini, enabled: true }));
2801
- }
2802
- if (config.fal?.apiKey) {
2803
- factory.register(new FalProvider({ ...config.fal, enabled: true }));
2804
- }
2805
- if (config.pruna?.apiKey) {
2806
- factory.register(new PrunaProvider({ ...config.pruna, enabled: true }));
2807
- }
2808
- return factory;
2809
- }
2810
-
2811
- // src/content/domain/limits/SpeechPaceConfig.ts
2812
- var SpeechPaceConfig = {
2813
- WORDS_PER_SECOND: 2.5,
2814
- TOKENS_PER_SECOND: 10,
2815
- JSON_TOKEN_OVERHEAD: 500,
2816
- TOKENS_PER_CALENDAR_DAY: 150
2817
- };
2818
-
2819
- // src/content/domain/calculations/SpeechPaceEstimate.ts
2820
- function estimateWordCountForDuration(durationSeconds) {
2821
- return Math.floor(durationSeconds * SpeechPaceConfig.WORDS_PER_SECOND);
2822
- }
2823
- function estimateTokenBudgetForDuration(durationSeconds) {
2824
- return durationSeconds * SpeechPaceConfig.TOKENS_PER_SECOND + SpeechPaceConfig.JSON_TOKEN_OVERHEAD;
2825
- }
2826
- function estimateTokenBudgetForCalendarDays(days) {
2827
- return days * SpeechPaceConfig.TOKENS_PER_CALENDAR_DAY + SpeechPaceConfig.JSON_TOKEN_OVERHEAD;
3826
+ factory.register(new AnthropicProvider(config.anthropic));
3827
+ }
3828
+ if (config.groq?.apiKey) {
3829
+ factory.register(new GroqProvider(config.groq));
3830
+ }
3831
+ if (config.gemini?.apiKey) {
3832
+ factory.register(new GeminiProvider({ ...config.gemini, enabled: true }));
3833
+ }
3834
+ if (config.fal?.apiKey) {
3835
+ factory.register(new FalProvider({ ...config.fal, enabled: true }));
3836
+ }
3837
+ if (config.pruna?.apiKey) {
3838
+ factory.register(new PrunaProvider({ ...config.pruna, enabled: true }));
3839
+ }
3840
+ return factory;
2828
3841
  }
2829
3842
 
2830
3843
  // src/content/application/prompts/PromptBuilder.ts
@@ -3137,75 +4150,6 @@ Return only the JSON:
3137
4150
  }
3138
4151
  };
3139
4152
 
3140
- // src/content/domain/limits/ValidationLimits.ts
3141
- var ValidationLimits = {
3142
- TOPIC: {
3143
- MIN: 1,
3144
- MAX: 500
3145
- },
3146
- CONTENT: {
3147
- MIN: 1,
3148
- MAX: 1e4
3149
- },
3150
- KEYWORD: {
3151
- MAX_COUNT: 20,
3152
- MAX_LENGTH: 50
3153
- },
3154
- DURATION_SECONDS: {
3155
- MIN: 10,
3156
- MAX: 600
3157
- },
3158
- CALENDAR_DAYS: {
3159
- MIN: 1,
3160
- MAX: 90
3161
- },
3162
- HASHTAG_COUNT: {
3163
- MIN: 1,
3164
- MAX: 50
3165
- },
3166
- SEO_CONTENT_PREVIEW: 500,
3167
- BLOG_PROMPT_PREVIEW: 1e3,
3168
- AB_VARIANT_CONTENT_PREVIEW: 200
3169
- };
3170
-
3171
- // src/content/domain/validation/TopicTextValidator.ts
3172
- function validateTopicText(topic) {
3173
- if (typeof topic !== "string" || topic.trim().length < ValidationLimits.TOPIC.MIN) {
3174
- throw new ValidationError("Topic cannot be empty");
3175
- }
3176
- const trimmed = topic.trim();
3177
- if (trimmed.length > ValidationLimits.TOPIC.MAX) {
3178
- throw new ValidationError(`Topic exceeds maximum length of ${ValidationLimits.TOPIC.MAX} characters`);
3179
- }
3180
- return trimmed;
3181
- }
3182
-
3183
- // src/content/domain/validation/KeywordListValidator.ts
3184
- function validateKeywordList(keywords) {
3185
- if (!Array.isArray(keywords)) {
3186
- throw new ValidationError("Keywords must be an array");
3187
- }
3188
- if (keywords.length > ValidationLimits.KEYWORD.MAX_COUNT) {
3189
- throw new ValidationError(`Cannot process more than ${ValidationLimits.KEYWORD.MAX_COUNT} keywords`);
3190
- }
3191
- return keywords.filter((k) => typeof k === "string" && k.trim().length > 0).map((keyword) => {
3192
- const trimmed = keyword.trim();
3193
- if (trimmed.length > ValidationLimits.KEYWORD.MAX_LENGTH) {
3194
- const preview = trimmed.substring(0, 20);
3195
- throw new ValidationError(
3196
- `Keyword "${preview}..." exceeds maximum length of ${ValidationLimits.KEYWORD.MAX_LENGTH} characters`
3197
- );
3198
- }
3199
- return trimmed;
3200
- });
3201
- }
3202
-
3203
- // src/content/domain/utils/IdGenerator.ts
3204
- function generateId(prefix) {
3205
- const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
3206
- return prefix ? `${prefix}-${id}` : id;
3207
- }
3208
-
3209
4153
  // src/content/application/services/BlogService.ts
3210
4154
  var BlogService = class {
3211
4155
  constructor(executor) {
@@ -3242,6 +4186,17 @@ var BlogService = class {
3242
4186
  };
3243
4187
  }
3244
4188
  };
4189
+
4190
+ // src/content/infrastructure/utils/requestQueue.ts
4191
+ ({
4192
+ maxConcurrent: RequestQueueConfig.MAX_CONCURRENT,
4193
+ maxQueueSize: RequestQueueConfig.MAX_QUEUE_SIZE,
4194
+ defaultTimeout: RequestQueueConfig.DEFAULT_TIMEOUT_MS,
4195
+ retryDelay: RequestQueueConfig.RETRY_DELAY_MS,
4196
+ enableRateLimit: RequestQueueConfig.RATE_LIMIT.ENABLED,
4197
+ rateLimitWindow: RequestQueueConfig.RATE_LIMIT.WINDOW_MS,
4198
+ rateLimitMaxRequests: RequestQueueConfig.RATE_LIMIT.MAX_REQUESTS_PER_WINDOW
4199
+ });
3245
4200
  async function batchRequest(requests, options = {}) {
3246
4201
  const {
3247
4202
  concurrency = 3,
@@ -3273,21 +4228,6 @@ async function batchRequest(requests, options = {}) {
3273
4228
  return results;
3274
4229
  }
3275
4230
 
3276
- // src/content/domain/validation/HashtagCountValidator.ts
3277
- function validateHashtagCount(count) {
3278
- if (!Number.isFinite(count)) {
3279
- throw new ValidationError(
3280
- `Hashtag count must be between ${ValidationLimits.HASHTAG_COUNT.MIN} and ${ValidationLimits.HASHTAG_COUNT.MAX}`
3281
- );
3282
- }
3283
- if (count < ValidationLimits.HASHTAG_COUNT.MIN || count > ValidationLimits.HASHTAG_COUNT.MAX) {
3284
- throw new ValidationError(
3285
- `Hashtag count must be between ${ValidationLimits.HASHTAG_COUNT.MIN} and ${ValidationLimits.HASHTAG_COUNT.MAX}`
3286
- );
3287
- }
3288
- return Math.floor(count);
3289
- }
3290
-
3291
4231
  // src/content/application/services/SocialService.ts
3292
4232
  var MULTI_PLATFORMS = [
3293
4233
  "twitter",
@@ -3365,21 +4305,6 @@ var SocialService = class {
3365
4305
  }
3366
4306
  };
3367
4307
 
3368
- // src/content/domain/validation/DurationSecondsValidator.ts
3369
- function validateDurationSeconds(duration) {
3370
- if (!Number.isFinite(duration)) {
3371
- throw new ValidationError(
3372
- `Duration must be between ${ValidationLimits.DURATION_SECONDS.MIN} and ${ValidationLimits.DURATION_SECONDS.MAX} seconds`
3373
- );
3374
- }
3375
- if (duration < ValidationLimits.DURATION_SECONDS.MIN || duration > ValidationLimits.DURATION_SECONDS.MAX) {
3376
- throw new ValidationError(
3377
- `Duration must be between ${ValidationLimits.DURATION_SECONDS.MIN} and ${ValidationLimits.DURATION_SECONDS.MAX} seconds`
3378
- );
3379
- }
3380
- return duration;
3381
- }
3382
-
3383
4308
  // src/content/application/services/VideoScriptService.ts
3384
4309
  var buildScriptResult = (parsed, duration) => {
3385
4310
  const script = parsed.script ?? "";
@@ -3434,27 +4359,6 @@ var VideoScriptService = class {
3434
4359
  }
3435
4360
  };
3436
4361
 
3437
- // src/content/domain/validation/ContentBodyValidator.ts
3438
- function validateContentBody(content) {
3439
- if (typeof content !== "string" || content.trim().length < ValidationLimits.CONTENT.MIN) {
3440
- throw new ValidationError("Content cannot be empty");
3441
- }
3442
- const trimmed = content.trim();
3443
- if (trimmed.length > ValidationLimits.CONTENT.MAX) {
3444
- throw new ValidationError(`Content exceeds maximum length of ${ValidationLimits.CONTENT.MAX} characters`);
3445
- }
3446
- return trimmed;
3447
- }
3448
- function truncateForSeoPreview(content) {
3449
- return content.substring(0, ValidationLimits.SEO_CONTENT_PREVIEW);
3450
- }
3451
- function truncateForBlogPromptPreview(content) {
3452
- return content.substring(0, ValidationLimits.BLOG_PROMPT_PREVIEW);
3453
- }
3454
- function truncateForAbVariantPreview(content) {
3455
- return content.substring(0, ValidationLimits.AB_VARIANT_CONTENT_PREVIEW);
3456
- }
3457
-
3458
4362
  // src/content/application/services/AnalysisService.ts
3459
4363
  var DEFAULT_SENTIMENT = {
3460
4364
  sentiment: "neutral",
@@ -3507,23 +4411,6 @@ var AnalysisService = class {
3507
4411
  }
3508
4412
  };
3509
4413
 
3510
- // src/content/domain/calculations/SeoKeywordDensity.ts
3511
- function countKeywordOccurrences(content, keyword) {
3512
- if (!keyword) return 0;
3513
- const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3514
- const matches = content.match(new RegExp(escaped, "gi"));
3515
- return matches ? matches.length : 0;
3516
- }
3517
- function countWordsInContent(content) {
3518
- const tokens = content.split(/\s+/).filter(Boolean);
3519
- return tokens.length || 1;
3520
- }
3521
- function calculateKeywordDensity(content, keyword) {
3522
- const totalWords = countWordsInContent(content);
3523
- const occurrences = countKeywordOccurrences(content, keyword);
3524
- return occurrences / totalWords * 100;
3525
- }
3526
-
3527
4414
  // src/content/application/services/SeoService.ts
3528
4415
  var DEFAULT_SEO_SCORE = {
3529
4416
  keywordDensity: 50,
@@ -3600,20 +4487,6 @@ var SeoService = class {
3600
4487
  }
3601
4488
  };
3602
4489
 
3603
- // src/content/domain/calculations/AbTestImprovementRatio.ts
3604
- function calculateImprovementPercentage(scoreA, scoreB) {
3605
- const higher = Math.max(scoreA, scoreB);
3606
- const lower = Math.min(scoreA, scoreB);
3607
- if (lower <= 0) {
3608
- return "100";
3609
- }
3610
- const delta = (higher / lower - 1) * 100;
3611
- return Math.abs(delta).toFixed(0);
3612
- }
3613
- function selectHigherScoreVariantId(scoreA, scoreB, idA = "A", idB = "B") {
3614
- return scoreA > scoreB ? idA : idB;
3615
- }
3616
-
3617
4490
  // src/content/application/services/ABTestService.ts
3618
4491
  var DEFAULT_PREDICTION = {
3619
4492
  variantId: "",
@@ -3715,27 +4588,6 @@ var ImagePromptService = class {
3715
4588
  }
3716
4589
  };
3717
4590
 
3718
- // src/content/domain/validation/CalendarDaysValidator.ts
3719
- function validateCalendarDays(days) {
3720
- if (!Number.isFinite(days)) {
3721
- throw new ValidationError(
3722
- `Days must be between ${ValidationLimits.CALENDAR_DAYS.MIN} and ${ValidationLimits.CALENDAR_DAYS.MAX}`
3723
- );
3724
- }
3725
- if (days < ValidationLimits.CALENDAR_DAYS.MIN || days > ValidationLimits.CALENDAR_DAYS.MAX) {
3726
- throw new ValidationError(
3727
- `Days must be between ${ValidationLimits.CALENDAR_DAYS.MIN} and ${ValidationLimits.CALENDAR_DAYS.MAX}`
3728
- );
3729
- }
3730
- return days;
3731
- }
3732
-
3733
- // src/content/domain/calculations/ContentCalendarDateOffset.ts
3734
- var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
3735
- function resolveCalendarEntryDate(dayIndex, baseTimestamp = Date.now()) {
3736
- return new Date(baseTimestamp + (dayIndex - 1) * ONE_DAY_MS);
3737
- }
3738
-
3739
4591
  // src/content/application/services/ContentCalendarService.ts
3740
4592
  var toCalendarEntry = (item) => ({
3741
4593
  date: resolveCalendarEntryDate(item.day),
@@ -4046,6 +4898,226 @@ var AIContentService = class {
4046
4898
  }
4047
4899
  };
4048
4900
 
4049
- export { ABTestService, AIContentService, AIError, ASPECT_RATIO_OPTIONS, ASPECT_RATIO_VALUES, AnalysisService, BlogService, ContentCalendarService, CREDIT_COSTS as GENERATION_COSTS, GenerationExecutor, IMAGE_QUALITY_OPTIONS, IMAGE_QUALITY_VALUES, ImagePromptService, MOTION_OPTIONS, MediaGenerationService, PromptBuilder, ProviderQuotaExhaustedError, ProviderUnavailableError, STYLE_PRESETS, SeoService, SocialService, VIDEO_DURATION_OPTIONS, VIDEO_DURATION_OPTIONS_SECONDS, VIDEO_QUALITY_OPTIONS, VIDEO_QUALITY_VALUES, ValidationError, VideoScriptService, calculateContentCreditCost as calculateCreditCost, getDimensionsForAspectRatio, getRecommendedQuality };
4901
+ // src/wizard/content.wizard.ts
4902
+ var CONTENT_WIZARD_STEPS = [
4903
+ {
4904
+ id: "select-type",
4905
+ label: "Select Type",
4906
+ icon: "Layers",
4907
+ description: "Choose the type of content you want to generate",
4908
+ validate: (data) => Boolean(data.contentType)
4909
+ },
4910
+ {
4911
+ id: "input-topic",
4912
+ label: "Topic",
4913
+ icon: "Lightbulb",
4914
+ description: "Enter your topic or idea",
4915
+ validate: (data) => {
4916
+ const topic = data.topic;
4917
+ return typeof topic === "string" && topic.length > 0;
4918
+ }
4919
+ },
4920
+ {
4921
+ id: "configure",
4922
+ label: "Configure",
4923
+ icon: "Settings",
4924
+ description: "Set tone, audience, and platform",
4925
+ validate: (data) => {
4926
+ const d = data;
4927
+ return Boolean(d.tone && d.targetAudience);
4928
+ }
4929
+ },
4930
+ {
4931
+ id: "advanced",
4932
+ label: "Advanced",
4933
+ icon: "Sliders",
4934
+ description: "Additional options and optimizations",
4935
+ optional: true
4936
+ },
4937
+ {
4938
+ id: "preview",
4939
+ label: "Preview",
4940
+ icon: "Eye",
4941
+ description: "Review your settings before generation"
4942
+ },
4943
+ {
4944
+ id: "generating",
4945
+ label: "Generating",
4946
+ icon: "Sparkles",
4947
+ description: "AI is generating your content"
4948
+ },
4949
+ {
4950
+ id: "results",
4951
+ label: "Results",
4952
+ icon: "CheckCircle",
4953
+ description: "Your generated content is ready"
4954
+ }
4955
+ ];
4956
+ function buildContentRequest(data) {
4957
+ switch (data.contentType) {
4958
+ case "blog":
4959
+ return {
4960
+ topic: data.topic ?? "",
4961
+ blogType: "tutorial",
4962
+ targetKeywords: data.keywords ?? [],
4963
+ tone: data.tone ?? "professional",
4964
+ wordCount: data.wordCount ?? 1e3,
4965
+ targetAudience: data.targetAudience ?? "",
4966
+ seoOptimization: data.seoOptimization ?? false,
4967
+ includeImages: data.includeImages ?? false,
4968
+ includeSchema: false
4969
+ };
4970
+ case "social":
4971
+ return {
4972
+ topic: data.topic ?? "",
4973
+ platform: data.platform ?? "twitter",
4974
+ tone: data.tone ?? "casual",
4975
+ hashtags: true,
4976
+ targetAudience: data.targetAudience,
4977
+ includeCallToAction: data.includeCallToAction
4978
+ };
4979
+ case "script":
4980
+ return {
4981
+ topic: data.topic ?? "",
4982
+ tone: data.tone ?? "professional",
4983
+ duration: data.duration ?? 60,
4984
+ targetAudience: data.targetAudience ?? "",
4985
+ includeVisuals: true,
4986
+ includeCallToAction: data.includeCallToAction ?? false
4987
+ };
4988
+ default:
4989
+ throw new Error(`Unsupported content type: ${data.contentType}`);
4990
+ }
4991
+ }
4992
+
4993
+ // src/wizard/image.wizard.ts
4994
+ var IMAGE_WIZARD_STEPS = [
4995
+ {
4996
+ id: "input-prompt",
4997
+ label: "Describe Image",
4998
+ icon: "Image",
4999
+ description: "Describe the image you want to create",
5000
+ validate: (data) => {
5001
+ const prompt = data.prompt;
5002
+ return typeof prompt === "string" && prompt.length > 0;
5003
+ }
5004
+ },
5005
+ {
5006
+ id: "select-style",
5007
+ label: "Style",
5008
+ icon: "Palette",
5009
+ description: "Choose the artistic style",
5010
+ validate: (data) => Boolean(data.style)
5011
+ },
5012
+ {
5013
+ id: "configure",
5014
+ label: "Configure",
5015
+ icon: "Settings",
5016
+ description: "Set format, quality, and quantity",
5017
+ validate: (data) => {
5018
+ const d = data;
5019
+ return Boolean(d.format && d.quality);
5020
+ }
5021
+ },
5022
+ {
5023
+ id: "advanced",
5024
+ label: "Advanced",
5025
+ icon: "Sliders",
5026
+ description: "Color palette and platform optimization",
5027
+ optional: true
5028
+ },
5029
+ { id: "preview", label: "Preview", icon: "Eye", description: "Review your settings" },
5030
+ { id: "generating", label: "Generating", icon: "Sparkles", description: "AI is creating your image" },
5031
+ { id: "results", label: "Results", icon: "Image", description: "Your generated image is ready" }
5032
+ ];
5033
+ function buildImageRequest(data) {
5034
+ return {
5035
+ type: "image",
5036
+ prompt: data.prompt ?? "",
5037
+ style: data.style,
5038
+ format: data.format,
5039
+ quality: data.quality,
5040
+ quantity: data.quantity ?? 1
5041
+ };
5042
+ }
5043
+
5044
+ // src/wizard/video.wizard.ts
5045
+ var validateSource = (data) => {
5046
+ const d = data;
5047
+ if (d.generationType === "text-to-video") {
5048
+ return typeof d.prompt === "string" && d.prompt.length > 0;
5049
+ }
5050
+ if (d.generationType === "image-to-video") {
5051
+ return typeof d.imageUrl === "string" && d.imageUrl.length > 0;
5052
+ }
5053
+ if (d.generationType === "video-to-video") {
5054
+ return typeof d.sourceVideoUrl === "string" && d.sourceVideoUrl.length > 0 && typeof d.prompt === "string" && d.prompt.length > 0;
5055
+ }
5056
+ return false;
5057
+ };
5058
+ var VIDEO_WIZARD_STEPS = [
5059
+ {
5060
+ id: "select-type",
5061
+ label: "Select Type",
5062
+ icon: "Video",
5063
+ description: "Choose video generation type",
5064
+ validate: (data) => Boolean(data.generationType)
5065
+ },
5066
+ {
5067
+ id: "input-source",
5068
+ label: "Source",
5069
+ icon: "FileInput",
5070
+ description: "Provide your source (prompt, image, or video)",
5071
+ validate: validateSource
5072
+ },
5073
+ {
5074
+ id: "configure",
5075
+ label: "Configure",
5076
+ icon: "Settings",
5077
+ description: "Set duration, aspect ratio, and style",
5078
+ validate: (data) => {
5079
+ const d = data;
5080
+ return Boolean(d.duration && d.aspectRatio);
5081
+ }
5082
+ },
5083
+ { id: "advanced", label: "Advanced", icon: "Sliders", description: "Additional video options", optional: true },
5084
+ { id: "preview", label: "Preview", icon: "Eye", description: "Review your settings" },
5085
+ { id: "generating", label: "Generating", icon: "Sparkles", description: "AI is creating your video" },
5086
+ { id: "results", label: "Results", icon: "Video", description: "Your generated video is ready" }
5087
+ ];
5088
+ function buildVideoRequest(data) {
5089
+ switch (data.generationType) {
5090
+ case "text-to-video":
5091
+ return {
5092
+ type: "video",
5093
+ prompt: data.prompt ?? "",
5094
+ duration: data.duration ?? 5,
5095
+ aspectRatio: data.aspectRatio ?? "16:9",
5096
+ style: data.style,
5097
+ quality: data.quality
5098
+ };
5099
+ case "image-to-video":
5100
+ return {
5101
+ type: "video",
5102
+ imageUrl: data.imageUrl ?? "",
5103
+ prompt: data.prompt,
5104
+ duration: data.duration ?? 5,
5105
+ motion: data.motion ?? "medium",
5106
+ camera: data.camera ?? "static"
5107
+ };
5108
+ case "video-to-video":
5109
+ return {
5110
+ type: "video",
5111
+ sourceVideoUrl: data.sourceVideoUrl ?? "",
5112
+ prompt: data.prompt ?? "",
5113
+ styleTransfer: data.styleTransfer,
5114
+ enhanceQuality: data.enhanceQuality
5115
+ };
5116
+ default:
5117
+ throw new Error(`Unsupported generation type: ${data.generationType}`);
5118
+ }
5119
+ }
5120
+
5121
+ export { ABTestService, AB_TEST_DEFAULT_CONFIDENCE, AIContentService, AIError, ASPECT_RATIO_OPTIONS, ASPECT_RATIO_VALUES, AnalysisService, BlogService, CONTENT_WIZARD_STEPS, CREDIT_COSTS, ContentCalendarService, DEFAULT_CREDIT_COST, DEFAULT_HASHTAG_BUDGET, CREDIT_COSTS as GENERATION_COSTS, GenerationExecutor, IMAGE_QUALITY_OPTIONS, IMAGE_QUALITY_VALUES, IMAGE_WIZARD_STEPS, ImagePromptService, MOTION_OPTIONS, MediaGenerationService, MemoryConfig, ModelDefaults, PLATFORM_SPECS, PromptBuilder, ProviderQuotaExhaustedError, ProviderTimingConfig, ProviderUnavailableError, RequestQueueConfig, STYLE_PRESETS, SeoService, SocialService, SpeechPaceConfig, TextModelDefaults, VIDEO_DURATION_OPTIONS, VIDEO_DURATION_OPTIONS_SECONDS, VIDEO_QUALITY_OPTIONS, VIDEO_QUALITY_VALUES, VIDEO_WIZARD_STEPS, ValidationError, ValidationLimits, VideoScriptService, buildContentRequest, buildImageRequest, buildVideoRequest, calculateContentCreditCost, calculateContentCreditCost as calculateCreditCost, calculateImprovementPercentage, calculateKeywordDensity, calculateRetryBackoffDelayMs, clampConfidenceToValidRange, clampScoreToValidRange, classifyKeywordProminence, countKeywordOccurrences, countWordsInContent, estimateFalGenerationCost, estimateGeminiGenerationCost, estimateGroqGenerationCost, estimatePrunaGenerationCost, estimateTokenBudgetForCalendarDays, estimateTokenBudgetForDuration, estimateUnknownProviderCost, estimateWordCountForDuration, generateId, getDimensionsForAspectRatio, getRecommendedQuality, isUnderutilizedKeyword, rankAndTrimHashtags, recommendQualityForAspectRatio, resolveCalendarEntryDate, resolveDefaultPrunaModel, resolveDimensionsForAspectRatio, resolveFalImageSize, resolveFalInferenceSteps, resolveHashtagBudget, resolveMaxContentLength, resolvePlatformSpec, selectHigherScoreVariantId, toQualityTier, truncateForAbVariantPreview, truncateForBlogPromptPreview, truncateForSeoPreview, validateCalendarDays, validateContentBody, validateDurationSeconds, validateHashtagCount, validateKeywordList, validateTopicText };
4050
5122
  //# sourceMappingURL=index.js.map
4051
5123
  //# sourceMappingURL=index.js.map