@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.
- package/dist/{AIErrors-B2ogd1PB.d.ts → AIErrors-CLjXRty-.d.ts} +99 -92
- package/dist/{AIErrors-C5EasV_c.d.cts → AIErrors-DjZXUiLT.d.cts} +99 -92
- package/dist/PromptBuilder-B5Lm1znm.d.ts +215 -0
- package/dist/PromptBuilder-DIUZlfV6.d.cts +215 -0
- package/dist/{text-generation.service-BQO3Tvz2.d.cts → cancellation-error-Cj7fJyCg.d.cts} +66 -3
- package/dist/{text-generation.service-CcYQM6Ch.d.ts → cancellation-error-CkMn5Kft.d.ts} +66 -3
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +3 -3
- package/dist/chat/index.d.ts +3 -3
- package/dist/chat/index.js.map +1 -1
- package/dist/{chat.service-CL1qtYxe.d.cts → chat.service-9M-KpLo-.d.cts} +1 -1
- package/dist/{chat.service-q05GnhEm.d.ts → chat.service-B4FLFxR1.d.ts} +1 -1
- package/dist/content/index.cjs +1582 -452
- package/dist/content/index.cjs.map +1 -1
- package/dist/content/index.d.cts +165 -219
- package/dist/content/index.d.ts +165 -219
- package/dist/content/index.js +1525 -453
- package/dist/content/index.js.map +1 -1
- package/dist/{create-ai-9PW752z8.d.ts → create-ai-0eZFFqTw.d.ts} +1 -1
- package/dist/{create-ai-CXYKPK6G.d.cts → create-ai-BrKR_5fQ.d.cts} +1 -1
- package/dist/hooks/index.cjs +924 -31
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +44 -8
- package/dist/hooks/index.d.ts +44 -8
- package/dist/hooks/index.js +919 -32
- package/dist/hooks/index.js.map +1 -1
- package/dist/index-BUVULo3h.d.ts +177 -0
- package/dist/index-BulSkB2L.d.cts +177 -0
- package/dist/index.cjs +3057 -563
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -5
- package/dist/index.d.ts +8 -5
- package/dist/index.js +3011 -564
- package/dist/index.js.map +1 -1
- package/dist/{provider.factory-DfDfk0dK.d.ts → provider.factory-BwPnqSEU.d.ts} +3 -1
- package/dist/{provider.factory-BxsfK4WW.d.cts → provider.factory-DyfJtR8M.d.cts} +3 -1
- package/dist/providers/index.cjs +818 -27
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.cts +50 -8
- package/dist/providers/index.d.ts +50 -8
- package/dist/providers/index.js +806 -28
- package/dist/providers/index.js.map +1 -1
- package/dist/wizard/index.d.cts +2 -86
- package/dist/wizard/index.d.ts +2 -86
- package/package.json +21 -1
- package/dist/ContentGeneration-B6iK2B33.d.cts +0 -93
- package/dist/ContentGeneration-B6iK2B33.d.ts +0 -93
package/dist/content/index.js
CHANGED
|
@@ -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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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/
|
|
2670
|
-
var
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
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
|
-
|
|
3029
|
+
const decoded = atob(raw);
|
|
3030
|
+
return Uint8Array.from(decoded, (char) => char.charCodeAt(0));
|
|
2677
3031
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
if (
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|