import { type Knex } from "knex"; import { type EntityManager } from "../entity/entity-manager"; import { type FixtureImportResult } from "../types/types"; import { type ExploreWithRelationsOptions } from "./data-explorer"; export type Locale = "ko" | "en" | "ja"; export type FixtureGeneratorOptions = { locale?: Locale; useLLM?: boolean; enableLLMCache?: boolean; llmModel?: string; }; export type GeneratorContext = { /** 생성 중인 fixture들 (메모리 상) */ fixtures: Map>; /** 참조 데이터 캐시 (DataExplorer 결과) */ referenceCache: Map[]>; /** 이미 import된 레코드를 추적하여 중복 import를 방지합니다 */ importedRecords: Set; }; export declare class FixtureGenerator { private sourceDb; private targetDbName; private entityManager; private dataExplorer; private locale; private mappings; private llmCache; private entityCache; private options; constructor(sourceDb: Knex, _targetDb: Knex, targetDbName: "fixture" | "test" | "production_master", entityManager: typeof EntityManager, options?: FixtureGeneratorOptions); /** * Fixture 생성 (단일) * @returns 생성된 fixture 데이터 (메모리 상) */ generate(entityName: string, overrides?: Record, context?: GeneratorContext): Promise>; /** * Relation 값 생성 + 자동 Import */ private generateRelationValue; /** * ExploreWithRelations 결과를 targetDb에 import * * 관계 체인을 따라간 결과(main + related)를 모두 import합니다. * 의존성 순서는 FixtureManager.insertFixtures가 자동으로 처리합니다. */ private importExploreResult; /** * fixtureGenerator 실행 (Faker.js만 지원) * * faker.* 형식의 표현식을 안전하게 파싱하여 실행합니다. * 예: "faker.internet.email()" → faker.internet.email() * 예: "faker.lorem.words(3)" → faker.lorem.words(3) */ private executeGenerator; /** * 필드의 타입과 이름을 분석하여 적절한 기본값을 생성합니다. * * 우선순위: * 1. 필드명 패턴 매칭 (salary, budget 등 의미있는 데이터) * 2. 특수 케이스 (Department name 등 도메인 지식) * 3. 배열 타입 (JSON 배열) * 4. Enum 타입 * 5. 타입별 기본값 */ private generateDefaultValue; /** * 배열 타입의 값을 생성합니다. * * 타입 ID와 필드명 패턴을 분석하여 적절한 배열 데이터를 생성합니다. * 예: image_urls → [{url, name, mime_type}, ...] * tag_ids → [1, 23, 45] */ private generateArrayValue; /** * JSON 매핑의 Faker 표현식을 파싱하여 실행합니다. * * 표현식 예시: * - "faker.internet.email()" → 인자 없음 * - "faker.number.int({ min: 1, max: 100 })" → JSON 인자 * - "{}" → 리터럴 값 (JSON.parse) * * fakerKO, fakerJA도 지원하여 다국어 데이터를 생성합니다. */ private executeFakerExpression; /** * fixtureHint를 LLM에게 전달하여 현실적인 테스트 데이터를 생성합니다. * * faker.js로는 생성하기 어려운 복잡한 텍스트(자기소개, 설명문 등)를 * LLM을 활용하여 생성합니다. 동일한 hint에 대한 중복 호출을 방지하기 위해 * 캐싱을 기본으로 지원합니다 (LLM API 비용 절감). * * ai 패키지는 dynamic import로 불러오므로, useLLM이 false인 경우 * 의존성이 설치되지 않아도 fixture 생성이 정상 동작합니다. */ private generateWithLLM; /** * 단일 필드를 LLM으로 생성합니다 (rowKey 없을 때 fallback용) */ private generateSingleWithLLM; /** * row 전체를 한 번에 생성하는 LLM 프롬프트를 만듭니다. */ private buildRowLLMPrompt; /** * row LLM 응답을 파싱하여 필드별 값으로 변환합니다. */ private parseRowLLMResponse; private buildLLMPrompt; private parseLLMResponse; private getDefaultValueForType; private parseScalarValue; /** * faker 함수 인자 문자열을 파싱하여 인자 배열로 반환합니다. * * 3단계 전략: * 1. JSON 직접 파싱 (표준 JSON 표현식) * 2. JS 객체 리터럴 → JSON 변환 후 재시도 (single quote, unquoted key 처리) * 3. 단순 단일 인자 폴백 (숫자, 문자열) */ private parseGeneratorArgs; /** * JS 객체 리터럴을 JSON으로 변환합니다. * * 두 가지 변환: * 1. Single-quoted 문자열 → double-quoted (이스케이프 처리 포함) * 2. Unquoted 객체 키 → double-quoted */ private convertJsLiteralToJson; /** * Sonamu.secret을 우선으로 하고, 없으면 환경변수에서 API 키를 읽습니다. * * Sonamu.secret은 프로젝트별 설정(sonamu.config.ts)이므로 더 높은 우선순위를 가지며, * 환경변수는 개발 환경이나 CI/CD에서 fallback으로 사용됩니다. */ private getApiKey; private getExpectedFormat; private getScalarFormat; private getExampleForType; private getScalarExample; /** * 이름을 이메일 로컬 파트용 로마나이즈드 문자열로 변환합니다. * * 한글 이름은 초성-중성-종성 분해 후 로마나이즈 처리합니다. * 영문 이름은 소문자로 변환하고 공백을 점(.)\uc73c로 치환합니다. * 예: "김철수" → "cheolsu.kim", "John Doe" → "john.doe" */ private romanizeName; /** * 한글 이름을 로마나이즈 처리합니다. * * 초성/중성/종성 매핑 테이블을 사용하여 한글을 로마자로 변환합니다. * 첫 글자를 성으로 간주하여 "김철수" → "cheolsu.kim" 형태로 출력합니다. */ private romanizeKoreanName; /** * LLM 캐시 통계를 반환합니다. */ getLLMCacheStats(): { size: number; enabled: boolean | undefined; }; /** * LLM 캐시를 초기화합니다. */ clearLLMCache(): void; /** * 컨텍스트 생성 */ private createContext; /** * 배치 생성 및 자동 저장 * * 1. 각 spec별로 fixture 생성 (메모리) * 2. FixtureRecord로 변환 * 3. FixtureManager.insertFixtures()로 targetDb에 저장 * * @returns 저장된 fixture 데이터 (실제 DB ID 포함) */ generateBatch(specs: Array<{ entity: string; count: number; overrides?: Record; }>): Promise; /** * 부모 fixture 결과를 기반으로 fixtureCompanions에 선언된 companion Entity를 생성합니다. * * generateBatch()에서만 호출되며, companion 생성 시 재귀를 방지하기 위해 * generateBatch()를 다시 호출하지 않고 직접 삽입합니다. */ private generateCompanions; /** * overrides 값의 "{{fieldName}}" 템플릿을 부모 fixture 데이터로 치환합니다. * * 예: { "account_id": "{{email}}" } → { "account_id": "user@example.com" } */ private resolveTemplateOverrides; /** * 실제 DB(sourceDb)에서 데이터를 조회하여 fixture DB(targetDb)에 import합니다. * * 1. DataExplorer로 sourceDb에서 데이터 조회 (관련 데이터 포함) * 2. FixtureRecord로 변환 * 3. targetDb에 삽입 * * @param entityName - 조회할 entity 이름 * @param options - 조회 옵션 (strategy, limit, includeRelations 등) * @returns 저장된 fixture 데이터 (실제 DB ID 포함) * * @example * // 프로덕션 DB에서 User 10명 + 관련 Employee, Department 가져오기 * await generator.importFromSource("User", { * strategy: "sample", * limit: 10, * includeRelations: true, * maxDepth: 2 * }); */ importFromSource(entityName: string, options: ExploreWithRelationsOptions): Promise; } //# sourceMappingURL=fixture-generator.d.ts.map