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