@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.cjs
CHANGED
|
@@ -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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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/
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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/
|
|
2672
|
-
var
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
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
|
-
|
|
3031
|
+
const decoded = atob(raw);
|
|
3032
|
+
return Uint8Array.from(decoded, (char) => char.charCodeAt(0));
|
|
2679
3033
|
}
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
if (
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|