@umituz/react-native-ai-generation-content 1.61.39 → 1.61.41
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 -1
- package/src/domains/creations/infrastructure/repositories/CreationsWriter.ts +16 -213
- package/src/domains/creations/infrastructure/repositories/creations-operations.ts +128 -0
- package/src/domains/creations/infrastructure/repositories/creations-state-operations.ts +86 -0
- package/src/domains/creations/presentation/components/GalleryScreenHeader.tsx +54 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +23 -56
- package/src/domains/creations/presentation/utils/filter-buttons.util.ts +75 -0
- package/src/domains/generation/wizard/presentation/screens/GeneratingScreen.tsx +36 -43
- package/src/domains/generation/wizard/presentation/screens/TextInputScreen.tsx +6 -39
- package/src/domains/scenarios/presentation/screens/ScenarioPreviewScreen.tsx +7 -25
- package/src/features/image-to-video/presentation/screens/ImageToVideoWizardFlow.tsx +26 -46
- package/src/features/shared/index.ts +6 -0
- package/src/features/shared/presentation/components/AutoSkipPreview.tsx +24 -0
- package/src/features/shared/presentation/components/index.ts +6 -0
- package/src/features/shared/presentation/utils/index.ts +14 -0
- package/src/features/shared/presentation/utils/wizard-flow.utils.ts +84 -0
- package/src/features/text-to-image/presentation/screens/TextToImageWizardFlow.tsx +26 -46
- package/src/features/text-to-video/presentation/screens/TextToVideoWizardFlow.tsx +26 -46
- package/src/infrastructure/logging/debug.util.ts +1 -1
- package/src/infrastructure/logging/index.ts +1 -1
- package/src/infrastructure/validation/advanced-validator.ts +97 -0
- package/src/infrastructure/validation/ai-validator.ts +77 -0
- package/src/infrastructure/validation/base-validator.ts +149 -0
- package/src/infrastructure/validation/entity-validator.ts +64 -0
- package/src/infrastructure/validation/input-validator.ts +37 -409
- package/src/infrastructure/validation/sanitizer.ts +43 -0
- package/src/presentation/components/buttons/ContinueButton.tsx +72 -0
- package/src/presentation/components/buttons/index.ts +1 -0
|
@@ -3,26 +3,19 @@
|
|
|
3
3
|
* Step-based wizard flow for text-to-video generation
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React, { useMemo
|
|
6
|
+
import React, { useMemo } from "react";
|
|
7
7
|
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
9
|
import { GenericWizardFlow } from "../../../../domains/generation/wizard/presentation/components";
|
|
10
10
|
import { TEXT_TO_VIDEO_WIZARD_CONFIG } from "../../../../domains/generation/wizard/configs";
|
|
11
11
|
import { useAIFeatureGate } from "../../../../domains/access-control";
|
|
12
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
createDefaultAlerts,
|
|
14
|
+
createScenarioData,
|
|
15
|
+
useWizardFlowHandlers,
|
|
16
|
+
AutoSkipPreview,
|
|
17
|
+
} from "../../../shared";
|
|
13
18
|
import type { BaseWizardFlowProps } from "../../../../domains/generation/wizard/presentation/components/WizardFlow.types";
|
|
14
|
-
import type { AlertMessages } from "../../../../presentation/hooks/generation/types";
|
|
15
|
-
|
|
16
|
-
const AutoSkipPreview: React.FC<{ onContinue: () => void }> = ({ onContinue }) => {
|
|
17
|
-
const hasContinued = useRef(false);
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (!hasContinued.current) {
|
|
20
|
-
hasContinued.current = true;
|
|
21
|
-
onContinue();
|
|
22
|
-
}
|
|
23
|
-
}, [onContinue]);
|
|
24
|
-
return null;
|
|
25
|
-
};
|
|
26
19
|
|
|
27
20
|
export type TextToVideoWizardFlowProps = BaseWizardFlowProps;
|
|
28
21
|
|
|
@@ -41,46 +34,33 @@ export const TextToVideoWizardFlow: React.FC<TextToVideoWizardFlowProps> = (prop
|
|
|
41
34
|
|
|
42
35
|
const tokens = useAppDesignTokens();
|
|
43
36
|
|
|
44
|
-
// Centralized access control - handles offline, auth, credits, paywall
|
|
45
37
|
const { requireFeature } = useAIFeatureGate({
|
|
46
38
|
creditCost,
|
|
47
39
|
onNetworkError,
|
|
48
40
|
});
|
|
49
41
|
|
|
50
|
-
const scenario
|
|
51
|
-
() =>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
networkError: t("common.errors.network"),
|
|
64
|
-
policyViolation: t("common.errors.policy"),
|
|
65
|
-
saveFailed: t("common.errors.saveFailed"),
|
|
66
|
-
creditFailed: t("common.errors.creditFailed"),
|
|
67
|
-
unknown: t("common.errors.unknown"),
|
|
68
|
-
}),
|
|
69
|
-
[t],
|
|
42
|
+
const scenario = useMemo(
|
|
43
|
+
() =>
|
|
44
|
+
createScenarioData(
|
|
45
|
+
{
|
|
46
|
+
id: "text-to-video",
|
|
47
|
+
outputType: "video",
|
|
48
|
+
inputType: "text",
|
|
49
|
+
model,
|
|
50
|
+
titleKey: "text2video.title",
|
|
51
|
+
},
|
|
52
|
+
t
|
|
53
|
+
),
|
|
54
|
+
[model, t]
|
|
70
55
|
);
|
|
71
56
|
|
|
72
|
-
const
|
|
73
|
-
(_data: Record<string, unknown>, proceed: () => void) => {
|
|
74
|
-
// Use centralized access control - checks offline, auth, credits
|
|
75
|
-
requireFeature(proceed);
|
|
76
|
-
},
|
|
77
|
-
[requireFeature],
|
|
78
|
-
);
|
|
57
|
+
const defaultAlerts = useMemo(() => createDefaultAlerts(t), [t]);
|
|
79
58
|
|
|
80
|
-
const handleGenerationComplete =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
59
|
+
const { handleGenerationStart, handleGenerationComplete } = useWizardFlowHandlers({
|
|
60
|
+
requireFeature,
|
|
61
|
+
onGenerationComplete,
|
|
62
|
+
onBack,
|
|
63
|
+
});
|
|
84
64
|
|
|
85
65
|
return (
|
|
86
66
|
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced Validation Utilities
|
|
3
|
+
* Complex validators for objects, arrays, and combined validations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ValidationResult } from "./base-validator";
|
|
7
|
+
import { validateString } from "./base-validator";
|
|
8
|
+
import type { StringValidationOptions } from "./base-validator";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validates object structure
|
|
12
|
+
*/
|
|
13
|
+
export function validateObject(
|
|
14
|
+
input: unknown,
|
|
15
|
+
requiredFields: readonly string[] = []
|
|
16
|
+
): ValidationResult {
|
|
17
|
+
const errors: string[] = [];
|
|
18
|
+
|
|
19
|
+
if (typeof input !== "object" || input === null) {
|
|
20
|
+
return { isValid: false, errors: ["Input must be an object"] };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const field of requiredFields) {
|
|
24
|
+
if (!(field in input)) {
|
|
25
|
+
errors.push(`Missing required field: ${field}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { isValid: errors.length === 0, errors };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validates array input
|
|
34
|
+
*/
|
|
35
|
+
export function validateArray(
|
|
36
|
+
input: unknown,
|
|
37
|
+
options: {
|
|
38
|
+
readonly minLength?: number;
|
|
39
|
+
readonly maxLength?: number;
|
|
40
|
+
readonly itemType?: "string" | "number" | "object";
|
|
41
|
+
} = {}
|
|
42
|
+
): ValidationResult {
|
|
43
|
+
const errors: string[] = [];
|
|
44
|
+
|
|
45
|
+
if (!Array.isArray(input)) {
|
|
46
|
+
return { isValid: false, errors: ["Input must be an array"] };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (options.minLength !== undefined && input.length < options.minLength) {
|
|
50
|
+
errors.push(`Array must have at least ${options.minLength} items`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (options.maxLength !== undefined && input.length > options.maxLength) {
|
|
54
|
+
errors.push(`Array must have at most ${options.maxLength} items`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (options.itemType) {
|
|
58
|
+
for (let i = 0; i < input.length; i++) {
|
|
59
|
+
const item = input[i];
|
|
60
|
+
const isValidType =
|
|
61
|
+
options.itemType === "string"
|
|
62
|
+
? typeof item === "string"
|
|
63
|
+
: options.itemType === "number"
|
|
64
|
+
? typeof item === "number"
|
|
65
|
+
: typeof item === "object" && item !== null;
|
|
66
|
+
|
|
67
|
+
if (!isValidType) {
|
|
68
|
+
errors.push(`Item at index ${i} is not a ${options.itemType}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { isValid: errors.length === 0, errors };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Combines multiple validation results
|
|
78
|
+
*/
|
|
79
|
+
export function combineValidationResults(
|
|
80
|
+
results: readonly ValidationResult[]
|
|
81
|
+
): ValidationResult {
|
|
82
|
+
const allErrors = results.flatMap((r) => r.errors);
|
|
83
|
+
return { isValid: allErrors.length === 0, errors: allErrors };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Sanitizes and validates user input in one step
|
|
88
|
+
*/
|
|
89
|
+
export function sanitizeAndValidate(
|
|
90
|
+
input: unknown,
|
|
91
|
+
sanitizeFn: (input: unknown) => string,
|
|
92
|
+
options: StringValidationOptions = {}
|
|
93
|
+
): { readonly sanitized: string; readonly validation: ValidationResult } {
|
|
94
|
+
const sanitized = sanitizeFn(input);
|
|
95
|
+
const validation = validateString(sanitized, options);
|
|
96
|
+
return { sanitized, validation };
|
|
97
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI-Specific Validation Utilities
|
|
3
|
+
* Validators for AI generation prompts and image data
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MAX_PROMPT_LENGTH,
|
|
8
|
+
MIN_PROMPT_LENGTH,
|
|
9
|
+
} from "../constants/validation.constants";
|
|
10
|
+
import { validateString, validateURL, validateBase64, type ValidationResult, type StringValidationOptions } from "./base-validator";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validates prompt/input text for AI generation
|
|
14
|
+
*/
|
|
15
|
+
export function validateAIPrompt(input: unknown): ValidationResult {
|
|
16
|
+
const options: StringValidationOptions = {
|
|
17
|
+
minLength: MIN_PROMPT_LENGTH,
|
|
18
|
+
maxLength: MAX_PROMPT_LENGTH,
|
|
19
|
+
trim: true,
|
|
20
|
+
};
|
|
21
|
+
return validateString(input, options);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validates image data (base64 or URL)
|
|
26
|
+
*/
|
|
27
|
+
export function validateImageData(input: unknown): ValidationResult {
|
|
28
|
+
if (typeof input !== "string") {
|
|
29
|
+
return { isValid: false, errors: ["Image data must be a string"] };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (input.startsWith("http://") || input.startsWith("https://")) {
|
|
33
|
+
return validateURL(input);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (input.startsWith("data:image/")) {
|
|
37
|
+
const base64Part = input.split(",")[1];
|
|
38
|
+
if (!base64Part) {
|
|
39
|
+
return { isValid: false, errors: ["Invalid data URI format"] };
|
|
40
|
+
}
|
|
41
|
+
return validateBase64(base64Part);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
isValid: false,
|
|
46
|
+
errors: ["Image data must be a URL or base64 data URI"],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Validates video URL
|
|
52
|
+
*/
|
|
53
|
+
export function validateVideoUrl(input: unknown): ValidationResult {
|
|
54
|
+
if (typeof input !== "string") {
|
|
55
|
+
return { isValid: false, errors: ["Video URL must be a string"] };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const urlResult = validateURL(input);
|
|
59
|
+
if (!urlResult.isValid) {
|
|
60
|
+
return urlResult;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const url = new URL(input);
|
|
64
|
+
const validExtensions = [".mp4", ".mov", ".webm", ".gif"];
|
|
65
|
+
const hasValidExtension = validExtensions.some((ext) =>
|
|
66
|
+
url.pathname.toLowerCase().endsWith(ext)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (!hasValidExtension) {
|
|
70
|
+
return {
|
|
71
|
+
isValid: false,
|
|
72
|
+
errors: ["Video URL must have a valid video extension"],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { isValid: true, errors: [] };
|
|
77
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Validation Utilities
|
|
3
|
+
* Core validation functions for strings, numbers, URLs, emails, and base64
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validation result type
|
|
8
|
+
*/
|
|
9
|
+
export interface ValidationResult {
|
|
10
|
+
readonly isValid: boolean;
|
|
11
|
+
readonly errors: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* String validation options
|
|
16
|
+
*/
|
|
17
|
+
export interface StringValidationOptions {
|
|
18
|
+
readonly minLength?: number;
|
|
19
|
+
readonly maxLength?: number;
|
|
20
|
+
readonly pattern?: RegExp;
|
|
21
|
+
readonly allowedCharacters?: RegExp;
|
|
22
|
+
readonly trim?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Numeric validation options
|
|
27
|
+
*/
|
|
28
|
+
export interface NumericValidationOptions {
|
|
29
|
+
readonly min?: number;
|
|
30
|
+
readonly max?: number;
|
|
31
|
+
readonly integer?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validates a string input against provided rules
|
|
36
|
+
*/
|
|
37
|
+
export function validateString(
|
|
38
|
+
input: unknown,
|
|
39
|
+
options: StringValidationOptions = {}
|
|
40
|
+
): ValidationResult {
|
|
41
|
+
const errors: string[] = [];
|
|
42
|
+
|
|
43
|
+
if (typeof input !== "string") {
|
|
44
|
+
return { isValid: false, errors: ["Input must be a string"] };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const value = options.trim !== false ? input.trim() : input;
|
|
48
|
+
|
|
49
|
+
if (options.minLength !== undefined && value.length < options.minLength) {
|
|
50
|
+
errors.push(`Input must be at least ${options.minLength} characters`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (options.maxLength !== undefined && value.length > options.maxLength) {
|
|
54
|
+
errors.push(`Input must be at most ${options.maxLength} characters`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (options.pattern && !options.pattern.test(value)) {
|
|
58
|
+
errors.push("Input format is invalid");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (options.allowedCharacters && !options.allowedCharacters.test(value)) {
|
|
62
|
+
errors.push("Input contains invalid characters");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { isValid: errors.length === 0, errors };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validates a numeric input
|
|
70
|
+
*/
|
|
71
|
+
export function validateNumber(
|
|
72
|
+
input: unknown,
|
|
73
|
+
options: NumericValidationOptions = {}
|
|
74
|
+
): ValidationResult {
|
|
75
|
+
const errors: string[] = [];
|
|
76
|
+
|
|
77
|
+
if (typeof input !== "number" || isNaN(input)) {
|
|
78
|
+
return { isValid: false, errors: ["Input must be a number"] };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (options.integer && !Number.isInteger(input)) {
|
|
82
|
+
errors.push("Input must be an integer");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (options.min !== undefined && input < options.min) {
|
|
86
|
+
errors.push(`Input must be at least ${options.min}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options.max !== undefined && input > options.max) {
|
|
90
|
+
errors.push(`Input must be at most ${options.max}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { isValid: errors.length === 0, errors };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Validates URL format
|
|
98
|
+
*/
|
|
99
|
+
export function validateURL(input: unknown): ValidationResult {
|
|
100
|
+
if (typeof input !== "string") {
|
|
101
|
+
return { isValid: false, errors: ["URL must be a string"] };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const url = new URL(input);
|
|
106
|
+
if (!["http:", "https:"].includes(url.protocol)) {
|
|
107
|
+
return { isValid: false, errors: ["Only HTTP and HTTPS protocols are allowed"] };
|
|
108
|
+
}
|
|
109
|
+
return { isValid: true, errors: [] };
|
|
110
|
+
} catch {
|
|
111
|
+
return { isValid: false, errors: ["Invalid URL format"] };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Validates email format
|
|
117
|
+
*/
|
|
118
|
+
export function validateEmail(input: unknown): ValidationResult {
|
|
119
|
+
if (typeof input !== "string") {
|
|
120
|
+
return { isValid: false, errors: ["Email must be a string"] };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
124
|
+
if (!emailRegex.test(input)) {
|
|
125
|
+
return { isValid: false, errors: ["Invalid email format"] };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { isValid: true, errors: [] };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Validates base64 string
|
|
133
|
+
*/
|
|
134
|
+
export function validateBase64(input: unknown): ValidationResult {
|
|
135
|
+
if (typeof input !== "string") {
|
|
136
|
+
return { isValid: false, errors: ["Input must be a string"] };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
140
|
+
if (!base64Regex.test(input)) {
|
|
141
|
+
return { isValid: false, errors: ["Invalid base64 format"] };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (input.length % 4 !== 0) {
|
|
145
|
+
return { isValid: false, errors: ["Base64 string length must be a multiple of 4"] };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { isValid: true, errors: [] };
|
|
149
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity Validation Utilities
|
|
3
|
+
* Validators for domain entities like user ID and creation ID
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { validateString, type ValidationResult, type StringValidationOptions } from "./base-validator";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validates user ID
|
|
10
|
+
*/
|
|
11
|
+
export function validateUserId(input: unknown): ValidationResult {
|
|
12
|
+
const options: StringValidationOptions = {
|
|
13
|
+
minLength: 1,
|
|
14
|
+
maxLength: 100,
|
|
15
|
+
pattern: /^[a-zA-Z0-9_-]+$/,
|
|
16
|
+
};
|
|
17
|
+
return validateString(input, options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validates creation ID
|
|
22
|
+
*/
|
|
23
|
+
export function validateCreationId(input: unknown): ValidationResult {
|
|
24
|
+
const options: StringValidationOptions = {
|
|
25
|
+
minLength: 1,
|
|
26
|
+
maxLength: 100,
|
|
27
|
+
};
|
|
28
|
+
return validateString(input, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validates scenario ID
|
|
33
|
+
*/
|
|
34
|
+
export function validateScenarioId(input: unknown): ValidationResult {
|
|
35
|
+
const options: StringValidationOptions = {
|
|
36
|
+
minLength: 1,
|
|
37
|
+
maxLength: 100,
|
|
38
|
+
pattern: /^[a-z0-9-]+$/,
|
|
39
|
+
};
|
|
40
|
+
return validateString(input, options);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validates model name
|
|
45
|
+
*/
|
|
46
|
+
export function validateModelName(input: unknown): ValidationResult {
|
|
47
|
+
const options: StringValidationOptions = {
|
|
48
|
+
minLength: 1,
|
|
49
|
+
maxLength: 100,
|
|
50
|
+
};
|
|
51
|
+
return validateString(input, options);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Validates provider name
|
|
56
|
+
*/
|
|
57
|
+
export function validateProviderName(input: unknown): ValidationResult {
|
|
58
|
+
const options: StringValidationOptions = {
|
|
59
|
+
minLength: 1,
|
|
60
|
+
maxLength: 50,
|
|
61
|
+
pattern: /^[a-z0-9-]+$/,
|
|
62
|
+
};
|
|
63
|
+
return validateString(input, options);
|
|
64
|
+
}
|