@umituz/react-native-ai-fal-provider 3.2.51 → 3.2.53
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/package.json +1 -4
- package/src/domain/services/ErrorClassificationService.ts +201 -0
- package/src/domain/services/ImageProcessingService.ts +89 -0
- package/src/domain/services/PricingService.ts +101 -0
- package/src/domain/services/ValidationService.ts +132 -0
- package/src/domain/services/index.ts +13 -0
- package/src/index.ts +24 -22
- package/src/infrastructure/utils/fal-error-handler.util.ts +14 -146
- package/src/infrastructure/utils/fal-storage.util.ts +2 -3
- package/src/infrastructure/utils/helpers/index.ts +2 -4
- package/src/infrastructure/utils/image-helpers.util.ts +13 -27
- package/src/infrastructure/utils/input-preprocessor.util.ts +6 -12
- package/src/infrastructure/utils/input-validator.util.ts +17 -156
- package/src/infrastructure/utils/pricing/fal-pricing.util.ts +26 -53
- package/src/infrastructure/utils/type-guards/index.ts +2 -2
- package/src/shared/helpers.ts +149 -0
- package/src/shared/index.ts +8 -0
- package/src/shared/type-guards.ts +122 -0
- package/src/shared/validators.ts +281 -0
- package/src/infrastructure/utils/helpers/calculation-helpers.util.ts +0 -168
- package/src/infrastructure/utils/helpers/error-helpers.util.ts +0 -65
- package/src/infrastructure/utils/helpers/object-helpers.util.ts +0 -44
- package/src/infrastructure/utils/helpers/timing-helpers.util.ts +0 -11
- package/src/infrastructure/utils/type-guards/model-type-guards.util.ts +0 -56
- package/src/infrastructure/utils/type-guards/validation-guards.util.ts +0 -101
- package/src/infrastructure/utils/validators/data-uri-validator.util.ts +0 -91
- package/src/infrastructure/utils/validators/string-validator.util.ts +0 -64
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Helper Utilities
|
|
3
|
+
* Consolidated helpers for error handling, object manipulation, timing, and calculations
|
|
4
|
+
*
|
|
5
|
+
* This file consolidates functionality from:
|
|
6
|
+
* - error-helpers.util.ts
|
|
7
|
+
* - object-helpers.util.ts
|
|
8
|
+
* - timing-helpers.util.ts
|
|
9
|
+
* - calculation-helpers.util.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ─── Error Helpers ─────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extract error message from any error type
|
|
16
|
+
* Handles Error instances, strings, and unknown types
|
|
17
|
+
*
|
|
18
|
+
* @param error - The error to extract message from
|
|
19
|
+
* @returns The error message as a string
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* try {
|
|
24
|
+
* await riskyOperation();
|
|
25
|
+
* } catch (error) {
|
|
26
|
+
* console.error('Operation failed:', getErrorMessage(error));
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function getErrorMessage(error: unknown): string {
|
|
31
|
+
return error instanceof Error ? error.message : String(error);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract error message with fallback
|
|
36
|
+
* Returns fallback if error message is empty or whitespace-only
|
|
37
|
+
*
|
|
38
|
+
* @param error - The error to extract message from
|
|
39
|
+
* @param fallback - Fallback message if error message is empty
|
|
40
|
+
* @returns The error message or fallback
|
|
41
|
+
*/
|
|
42
|
+
export function getErrorMessageOr(error: unknown, fallback: string): string {
|
|
43
|
+
const message = getErrorMessage(error);
|
|
44
|
+
return message && message.trim().length > 0 ? message : fallback;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create error message with context prefix
|
|
49
|
+
* Useful for adding operation context to error messages
|
|
50
|
+
*
|
|
51
|
+
* @param error - The error to extract message from
|
|
52
|
+
* @param context - Context to prepend to the error message
|
|
53
|
+
* @returns Formatted error message with context
|
|
54
|
+
*/
|
|
55
|
+
export function formatErrorMessage(error: unknown, context: string): string {
|
|
56
|
+
return `${context}: ${getErrorMessage(error)}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ─── Object Helpers ───────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build error message with context
|
|
63
|
+
*
|
|
64
|
+
* @param type - Error type or category
|
|
65
|
+
* @param context - Object containing key-value pairs for context
|
|
66
|
+
* @returns Formatted error message string
|
|
67
|
+
*/
|
|
68
|
+
export function buildErrorMessage(
|
|
69
|
+
type: string,
|
|
70
|
+
context: Record<string, unknown>
|
|
71
|
+
): string {
|
|
72
|
+
const contextStr = Object.entries(context)
|
|
73
|
+
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
|
|
74
|
+
.join(", ");
|
|
75
|
+
return `${type}${contextStr ? ` (${contextStr})` : ""}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if value is defined (not null or undefined)
|
|
80
|
+
*
|
|
81
|
+
* @param value - Value to check
|
|
82
|
+
* @returns Type guard indicating value is not null/undefined
|
|
83
|
+
*/
|
|
84
|
+
export function isDefined<T>(value: T | null | undefined): value is T {
|
|
85
|
+
return value !== null && value !== undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Filter out null and undefined values from object
|
|
90
|
+
*
|
|
91
|
+
* @param obj - Object to filter
|
|
92
|
+
* @returns New object with null/undefined values removed
|
|
93
|
+
*/
|
|
94
|
+
export function removeNullish<T extends Record<string, unknown>>(
|
|
95
|
+
obj: T
|
|
96
|
+
): Partial<T> {
|
|
97
|
+
return Object.fromEntries(
|
|
98
|
+
Object.entries(obj).filter(([_, value]) => isDefined(value))
|
|
99
|
+
) as Partial<T>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Generate unique ID with optional prefix
|
|
104
|
+
*
|
|
105
|
+
* @param prefix - Optional prefix for the ID
|
|
106
|
+
* @returns Unique identifier string
|
|
107
|
+
*/
|
|
108
|
+
export function generateUniqueId(prefix: string = ""): string {
|
|
109
|
+
const timestamp = Date.now().toString(36);
|
|
110
|
+
const randomStr = Math.random().toString(36).substring(2, 9);
|
|
111
|
+
return prefix ? `${prefix}_${timestamp}${randomStr}` : `${timestamp}${randomStr}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─── Timing Helpers ───────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Sleep for specified milliseconds
|
|
118
|
+
*
|
|
119
|
+
* @param ms - Milliseconds to sleep
|
|
120
|
+
* @returns Promise that resolves after delay
|
|
121
|
+
*/
|
|
122
|
+
export function sleep(ms: number): Promise<void> {
|
|
123
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── Calculation Helpers ──────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Calculate elapsed time in milliseconds from a start timestamp
|
|
130
|
+
*
|
|
131
|
+
* @param startTime - Start timestamp in milliseconds
|
|
132
|
+
* @returns Elapsed time in milliseconds
|
|
133
|
+
*/
|
|
134
|
+
export function getElapsedTime(startTime: number): number {
|
|
135
|
+
return Date.now() - startTime;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Calculate actual file size from base64 string
|
|
140
|
+
* Base64 encoding inflates size by ~33%, so actual size is ~75% of base64 length
|
|
141
|
+
*
|
|
142
|
+
* @param base64String - Base64 encoded string
|
|
143
|
+
* @returns Actual size in kilobytes
|
|
144
|
+
*/
|
|
145
|
+
export function getActualSizeKB(base64String: string): number {
|
|
146
|
+
const base64SizeKB = Math.round(base64String.length / 1024);
|
|
147
|
+
// Base64 inflates by ~33%, so actual size is ~75%
|
|
148
|
+
return Math.round(base64SizeKB * 0.75);
|
|
149
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Type Guards
|
|
3
|
+
* Runtime type checking for model types, error types, and other domain types
|
|
4
|
+
*
|
|
5
|
+
* This file consolidates functionality from:
|
|
6
|
+
* - model-type-guards.util.ts
|
|
7
|
+
* - validation-guards.util.ts (type guard portions)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { FalModelType } from "../domain/entities/fal.types";
|
|
11
|
+
import type { ModelType } from "../domain/types/model-selection.types";
|
|
12
|
+
import { FalErrorType } from "../domain/entities/error.types";
|
|
13
|
+
|
|
14
|
+
// ─── Model Type Guards ─────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if a string is a valid FalModelType
|
|
18
|
+
*
|
|
19
|
+
* @param value - Value to check
|
|
20
|
+
* @returns Type guard indicating if the value is a FalModelType
|
|
21
|
+
*/
|
|
22
|
+
export function isFalModelType(value: unknown): value is FalModelType {
|
|
23
|
+
const validTypes: ReadonlyArray<FalModelType> = [
|
|
24
|
+
"text-to-image",
|
|
25
|
+
"text-to-video",
|
|
26
|
+
"text-to-voice",
|
|
27
|
+
"image-to-video",
|
|
28
|
+
"image-to-image",
|
|
29
|
+
"text-to-text",
|
|
30
|
+
];
|
|
31
|
+
return typeof value === "string" && validTypes.includes(value as FalModelType);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if a string is a valid ModelType
|
|
36
|
+
*
|
|
37
|
+
* @param value - Value to check
|
|
38
|
+
* @returns Type guard indicating if the value is a ModelType
|
|
39
|
+
*/
|
|
40
|
+
export function isModelType(value: unknown): value is ModelType {
|
|
41
|
+
const validTypes: ReadonlyArray<ModelType> = [
|
|
42
|
+
"text-to-image",
|
|
43
|
+
"text-to-video",
|
|
44
|
+
"image-to-video",
|
|
45
|
+
"text-to-voice",
|
|
46
|
+
];
|
|
47
|
+
return typeof value === "string" && validTypes.includes(value as ModelType);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── Error Type Guards ────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if error is a FalErrorType
|
|
54
|
+
*
|
|
55
|
+
* @param value - Value to check
|
|
56
|
+
* @returns Type guard indicating if the value is a FalErrorType
|
|
57
|
+
*/
|
|
58
|
+
export function isFalErrorType(value: unknown): value is FalErrorType {
|
|
59
|
+
const validTypes: ReadonlyArray<FalErrorType> = [
|
|
60
|
+
FalErrorType.NETWORK,
|
|
61
|
+
FalErrorType.TIMEOUT,
|
|
62
|
+
FalErrorType.API_ERROR,
|
|
63
|
+
FalErrorType.VALIDATION,
|
|
64
|
+
FalErrorType.IMAGE_TOO_SMALL,
|
|
65
|
+
FalErrorType.CONTENT_POLICY,
|
|
66
|
+
FalErrorType.RATE_LIMIT,
|
|
67
|
+
FalErrorType.AUTHENTICATION,
|
|
68
|
+
FalErrorType.QUOTA_EXCEEDED,
|
|
69
|
+
FalErrorType.MODEL_NOT_FOUND,
|
|
70
|
+
FalErrorType.UNKNOWN,
|
|
71
|
+
];
|
|
72
|
+
return typeof value === "string" && validTypes.includes(value as FalErrorType);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── API Error Type Guards ────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if error is retryable based on error type
|
|
79
|
+
*
|
|
80
|
+
* @param error - Error to check
|
|
81
|
+
* @returns True if error is retryable
|
|
82
|
+
*/
|
|
83
|
+
export function isRetryableError(error: unknown): boolean {
|
|
84
|
+
if (error instanceof Error) {
|
|
85
|
+
const message = error.message.toLowerCase();
|
|
86
|
+
return (
|
|
87
|
+
message.includes("network") ||
|
|
88
|
+
message.includes("timeout") ||
|
|
89
|
+
message.includes("timed out") ||
|
|
90
|
+
message.includes("econnrefused") ||
|
|
91
|
+
message.includes("enotfound") ||
|
|
92
|
+
message.includes("fetch")
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if value is a local file URI
|
|
100
|
+
*
|
|
101
|
+
* @param value - Value to check
|
|
102
|
+
* @returns Type guard indicating if the value is a local file URI
|
|
103
|
+
*/
|
|
104
|
+
export function isLocalFileUri(value: unknown): value is string {
|
|
105
|
+
return (
|
|
106
|
+
typeof value === "string" &&
|
|
107
|
+
(value.startsWith("file://") || value.startsWith("content://"))
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Check if value is an HTTP/HTTPS URL
|
|
113
|
+
*
|
|
114
|
+
* @param value - Value to check
|
|
115
|
+
* @returns Type guard indicating if the value is an HTTP/HTTPS URL
|
|
116
|
+
*/
|
|
117
|
+
export function isHttpUrl(value: unknown): value is string {
|
|
118
|
+
return (
|
|
119
|
+
typeof value === "string" &&
|
|
120
|
+
(value.startsWith("http://") || value.startsWith("https://"))
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Validation Utilities
|
|
3
|
+
* Consolidated validators for strings, data URIs, and business rules
|
|
4
|
+
*
|
|
5
|
+
* This file consolidates functionality from:
|
|
6
|
+
* - data-uri-validator.util.ts
|
|
7
|
+
* - string-validator.util.ts
|
|
8
|
+
* - validation-guards.util.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
const MIN_BASE64_IMAGE_LENGTH = 100;
|
|
14
|
+
const MIN_MODEL_ID_LENGTH = 3;
|
|
15
|
+
const MAX_PROMPT_LENGTH = 5000;
|
|
16
|
+
const MAX_TIMEOUT_MS = 300000; // 5 minutes
|
|
17
|
+
const MAX_RETRY_COUNT = 5;
|
|
18
|
+
|
|
19
|
+
const SUSPICIOUS_PATTERNS = [
|
|
20
|
+
/<script/i,
|
|
21
|
+
/javascript:/i,
|
|
22
|
+
/on\w+\s*=/i,
|
|
23
|
+
/<iframe/i,
|
|
24
|
+
/<embed/i,
|
|
25
|
+
/<object/i,
|
|
26
|
+
/data:(?!image\/)/i,
|
|
27
|
+
/vbscript:/i,
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
const MODEL_ID_PATTERN = /^[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_.]+(\/[a-zA-Z0-9-_.]+)*$/;
|
|
31
|
+
|
|
32
|
+
// ─── String Validators ─────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if string is empty or whitespace-only
|
|
36
|
+
*
|
|
37
|
+
* @param value - Value to check
|
|
38
|
+
* @returns True if the value is an empty string or contains only whitespace
|
|
39
|
+
*/
|
|
40
|
+
export function isEmptyString(value: unknown): boolean {
|
|
41
|
+
return typeof value === "string" && value.trim().length === 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if value is a non-empty string
|
|
46
|
+
* Type guard version for better TypeScript inference
|
|
47
|
+
*
|
|
48
|
+
* @param value - Value to check
|
|
49
|
+
* @returns Type guard indicating if the value is a non-empty string
|
|
50
|
+
*/
|
|
51
|
+
export function isNonEmptyString(value: unknown): value is string {
|
|
52
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if value is a string (empty or non-empty)
|
|
57
|
+
* Basic type guard for string validation
|
|
58
|
+
*
|
|
59
|
+
* @param value - Value to check
|
|
60
|
+
* @returns Type guard indicating if the value is a string
|
|
61
|
+
*/
|
|
62
|
+
export function isString(value: unknown): value is string {
|
|
63
|
+
return typeof value === "string";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if string contains suspicious content
|
|
68
|
+
*
|
|
69
|
+
* @param value - String to check
|
|
70
|
+
* @returns True if suspicious patterns are found
|
|
71
|
+
*/
|
|
72
|
+
function hasSuspiciousContent(value: string): boolean {
|
|
73
|
+
return SUSPICIOUS_PATTERNS.some((pattern) => pattern.test(value));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ─── Data URI Validators ──────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if value is a data URI (any type)
|
|
80
|
+
*
|
|
81
|
+
* @param value - Value to check
|
|
82
|
+
* @returns Type guard indicating if the value is a data URI string
|
|
83
|
+
*/
|
|
84
|
+
export function isDataUri(value: unknown): value is string {
|
|
85
|
+
return typeof value === "string" && value.startsWith("data:");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if value is an image data URI
|
|
90
|
+
*
|
|
91
|
+
* @param value - Value to check
|
|
92
|
+
* @returns Type guard indicating if the value is an image data URI string
|
|
93
|
+
*/
|
|
94
|
+
export function isImageDataUri(value: unknown): value is string {
|
|
95
|
+
return typeof value === "string" && value.startsWith("data:image/");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if value is a base64-encoded data URI
|
|
100
|
+
*
|
|
101
|
+
* @param value - Value to check
|
|
102
|
+
* @returns Type guard indicating if the value contains base64 encoding
|
|
103
|
+
*/
|
|
104
|
+
export function isBase64DataUri(value: unknown): value is string {
|
|
105
|
+
return isDataUri(value) && typeof value === "string" && value.includes("base64,");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Extract MIME type from data URI
|
|
110
|
+
*
|
|
111
|
+
* @param dataUri - Data URI string
|
|
112
|
+
* @returns MIME type or null if not found
|
|
113
|
+
*/
|
|
114
|
+
export function extractMimeType(dataUri: string): string | null {
|
|
115
|
+
const match = dataUri.match(/^data:([^;,]+)/);
|
|
116
|
+
return match ? match[1] : null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Extract base64 content from data URI
|
|
121
|
+
*
|
|
122
|
+
* @param dataUri - Data URI string
|
|
123
|
+
* @returns Base64 content or null if not base64-encoded
|
|
124
|
+
*/
|
|
125
|
+
export function extractBase64Content(dataUri: string): string | null {
|
|
126
|
+
const parts = dataUri.split("base64,");
|
|
127
|
+
return parts.length === 2 ? parts[1] : null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Business Validators ───────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Validate base64 image string
|
|
134
|
+
*
|
|
135
|
+
* @param value - Value to validate
|
|
136
|
+
* @returns True if value is a valid base64 image
|
|
137
|
+
*/
|
|
138
|
+
export function isValidBase64Image(value: unknown): boolean {
|
|
139
|
+
if (typeof value !== "string") {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check data URI prefix
|
|
144
|
+
if (value.startsWith("data:image/")) {
|
|
145
|
+
const parts = value.split("base64,");
|
|
146
|
+
if (parts.length < 2) return false;
|
|
147
|
+
const base64Part = parts[1];
|
|
148
|
+
if (!base64Part) return false;
|
|
149
|
+
return base64Part.length >= MIN_BASE64_IMAGE_LENGTH;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check if it's a valid base64 string with minimum length
|
|
153
|
+
const base64Pattern = /^[A-Za-z0-9+/]+=*$/;
|
|
154
|
+
return base64Pattern.test(value) && value.length >= MIN_BASE64_IMAGE_LENGTH;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Validate API key format
|
|
159
|
+
*
|
|
160
|
+
* @param value - Value to validate
|
|
161
|
+
* @returns True if value is a valid API key
|
|
162
|
+
*/
|
|
163
|
+
export function isValidApiKey(value: unknown): boolean {
|
|
164
|
+
return typeof value === "string" && value.length > 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Validate model ID format
|
|
169
|
+
* Pattern: org/model or org/model/sub1/sub2/... (multiple path segments)
|
|
170
|
+
* Allows dots for versions (e.g., v1.5) but prevents path traversal (..)
|
|
171
|
+
*
|
|
172
|
+
* @param value - Value to validate
|
|
173
|
+
* @returns True if value is a valid model ID
|
|
174
|
+
*/
|
|
175
|
+
export function isValidModelId(value: unknown): boolean {
|
|
176
|
+
if (typeof value !== "string") {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Prevent path traversal attacks
|
|
181
|
+
if (value.includes("..")) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Ensure it doesn't start or end with dots
|
|
186
|
+
if (value.startsWith(".") || value.endsWith(".")) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
MODEL_ID_PATTERN.test(value) && value.length >= MIN_MODEL_ID_LENGTH
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Validate prompt string
|
|
197
|
+
*
|
|
198
|
+
* @param value - Value to validate
|
|
199
|
+
* @returns True if value is a valid prompt
|
|
200
|
+
*/
|
|
201
|
+
export function isValidPrompt(value: unknown): boolean {
|
|
202
|
+
if (!isNonEmptyString(value)) return false;
|
|
203
|
+
return value.length <= MAX_PROMPT_LENGTH && !hasSuspiciousContent(value);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Validate timeout value
|
|
208
|
+
*
|
|
209
|
+
* @param value - Value to validate
|
|
210
|
+
* @returns True if value is a valid timeout
|
|
211
|
+
*/
|
|
212
|
+
export function isValidTimeout(value: unknown): boolean {
|
|
213
|
+
return (
|
|
214
|
+
typeof value === "number" &&
|
|
215
|
+
!isNaN(value) &&
|
|
216
|
+
isFinite(value) &&
|
|
217
|
+
value > 0 &&
|
|
218
|
+
value <= MAX_TIMEOUT_MS
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Validate retry count
|
|
224
|
+
*
|
|
225
|
+
* @param value - Value to validate
|
|
226
|
+
* @returns True if value is a valid retry count
|
|
227
|
+
*/
|
|
228
|
+
export function isValidRetryCount(value: unknown): boolean {
|
|
229
|
+
return (
|
|
230
|
+
typeof value === "number" &&
|
|
231
|
+
!isNaN(value) &&
|
|
232
|
+
isFinite(value) &&
|
|
233
|
+
Number.isInteger(value) &&
|
|
234
|
+
value >= 0 &&
|
|
235
|
+
value <= MAX_RETRY_COUNT
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Validate URL format and protocol
|
|
241
|
+
* Rejects malicious URLs and unsafe protocols
|
|
242
|
+
*
|
|
243
|
+
* @param value - URL string to validate
|
|
244
|
+
* @returns True if URL is valid and safe
|
|
245
|
+
*/
|
|
246
|
+
export function isValidAndSafeUrl(value: string): boolean {
|
|
247
|
+
// Allow http/https URLs
|
|
248
|
+
if (value.startsWith("http://") || value.startsWith("https://")) {
|
|
249
|
+
try {
|
|
250
|
+
const url = new URL(value);
|
|
251
|
+
// Reject URLs with @ (potential auth bypass)
|
|
252
|
+
if (url.href.includes("@") && url.username) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
// Ensure domain exists
|
|
256
|
+
if (!url.hostname || url.hostname.length === 0) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
} catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Allow local file URIs
|
|
266
|
+
if (value.startsWith("file://") || value.startsWith("content://")) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Allow base64 image data URIs only
|
|
271
|
+
if (isImageDataUri(value)) {
|
|
272
|
+
// Check for suspicious content in data URI
|
|
273
|
+
const dataContent = value.substring(0, 200);
|
|
274
|
+
if (hasSuspiciousContent(dataContent)) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return false;
|
|
281
|
+
}
|