/** * FieldStore.ts * * Forma - 개별 필드 상태 관리 핵심 클래스 / Core class for individual field state management * 선택적 구독과 성능 최적화 지원 / Supports selective subscriptions and performance optimization * * @license MIT License * @copyright 2025 KIM YOUNG JIN (Kim Young Jin) * @author KIM YOUNG JIN (ehfuse@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * 개별 필드 상태 관리 Store / Individual field state management store * 선택적 구독과 성능 최적화를 위한 핵심 클래스 * Core class for selective subscriptions and performance optimization * * @template T 폼 데이터의 타입 / Form data type */ /** * Watch 콜백 타입 / Watch callback type */ type WatchCallback = (value: any, prevValue: any) => void; /** * hooks 가 사용하는 store 의 명령형 API 묶음 / Imperative store API surface used by hooks * 모든 메서드는 pre-bound 이며 store 수명 동안 식별자가 안정적이다. * All methods are pre-bound; identities are stable for the store's lifetime. */ export interface FieldStoreApi> { setValue: (fieldName: keyof T | string, value: any) => void; setValues: (newValues: Partial) => void; getValue: (fieldName: keyof T | string) => any; getValues: () => T; setBatch: (updates: Record) => void; reset: () => void; hasField: (path: string) => boolean; removeField: (path: string) => void; subscribe: (fieldName: keyof T | string, listener: () => void) => () => void; setInitialValues: (newInitialValues: T) => void; refreshFields: (prefix: string) => void; } export declare class FieldStore> { private fields; private dotNotationListeners; private dotPathIndex; private initialValues; private dirtyFields; private isInitialEmpty; private globalListeners; private watchers; private wildcardWatcherPaths; private valuesVersion; private cachedValues; private cachedValuesVersion; private api; /** @internal 테스트 전용: collect 함수가 검사한 구독 경로 수 누적 / test-only counter of scanned subscribed paths */ __debugDotScanCount: number; constructor(initialValues: T); private areValuesEqual; /** * 값 변경 세대를 올리고 getValues() 스냅샷 캐시를 무효화한다. * Bump the mutation version and invalidate the getValues() snapshot cache. * 모든 쓰기 지점(setValue/setValueWithoutNotify/reset/setInitialValues/ * removeField/refreshFields/subscribe 필드 생성/destroy)에서 호출해야 한다. * Must be called from every write site. */ private bumpValuesVersion; /** * dot 구독 경로를 루트필드 역인덱스에 등록한다. * Register a subscribed dot path in the root-field reverse index. */ private indexDotPath; /** * dot 구독 경로를 루트필드 역인덱스에서 제거한다 (빈 버킷은 정리). * Remove a subscribed dot path from the reverse index (cleaning empty buckets). */ private unindexDotPath; /** * 특정 필드 변경에 의해 깨어나야 할 dot-notation 구독자들을 수집한다. * Collect dot-notation subscribers that must be notified for a given field change. * * setValue(즉시 알림)와 setValueWithoutNotify(배치 수집)가 동일한 매칭 규칙을 * 공유하도록 단일 진실 원천(single source of truth)으로 추출한 함수. * Extracted as a single source of truth so setValue (immediate) and * setValueWithoutNotify (batched) share the exact same matching rules, * eliminating single-vs-batch notification divergence. * * @param changedPath 실제로 변경된 경로 / The path that actually changed * - dot 경로 set: 전체 경로 (예: "a.b.c") / full dot path for nested set * - 일반 필드 set: 루트 필드명 (예: "user") / root field name for plain set * @param rootFieldStr 루트 필드명 / Root field name (changedPath 의 첫 세그먼트) * @param oldValue 변경 전 값 / Previous value * - dot 경로: 루트 필드의 이전 값 / previous root value * - 일반 필드: 필드의 이전 값 / previous field value * @param newValue 변경 후 값 / New value (대응되는 범위) * @returns 깨워야 할 listener 집합 / Set of listeners to notify */ private collectAffectedDotListeners; /** * 점 없는 루트 필드(fieldStr)가 통째로 교체될 때, 그 하위를 구독한 * dot-notation 구독자들 중 깨어나야 할 listener 들을 수집한다. * Collect dot-notation subscribers to notify when a plain root field * (fieldStr) is replaced as a whole. * * setValue(즉시) / setValueWithoutNotify(배치) 가 동일한 매칭 규칙을 공유하도록 * 단일 진실 원천으로 추출. 분기 규칙은 기존 구현과 byte 단위로 동일. * Extracted as a single source of truth so setValue (immediate) and * setValueWithoutNotify (batched) share identical matching rules. * Branch logic is byte-for-byte identical to the previous inline implementations. * * 주의: "값이 실제로 변경되었는가" 게이트는 각 호출부에 남겨둔다. * - setValue: field.value !== value (참조 비교) * - setValueWithoutNotify: deepEqual 깊은 비교 * Note: the "did the value actually change" gate stays at each call site * (reference compare for setValue, deep compare for setValueWithoutNotify). * * @param fieldStr 교체된 루트 필드명 / Replaced root field name * @param oldValue 필드의 이전 값 / Previous field value * @param value 필드의 새 값 / New field value * @returns 깨워야 할 listener 집합 / Set of listeners to notify */ private collectAffectedPlainFieldListeners; private updateDirtyForField; /** * 특정 필드 값 가져오기 / Get specific field value * Dot notation 지원 / Supports dot notation * @param fieldName 필드명 또는 dot notation 경로 또는 "*" (전체) / Field name or dot notation path or "*" (all) * @returns 필드 값 / Field value */ getValue(fieldName: keyof T | string): any; /** * 특정 필드 구독 / Subscribe to specific field * Dot notation 지원 / Supports dot notation * @param fieldName 필드명 또는 dot notation 경로 또는 "*" (전체) / Field name or dot notation path or "*" (all) * @param listener 변경 시 호출될 콜백 / Callback to call on change * @returns 구독 해제 함수 / Unsubscribe function */ subscribe(fieldName: keyof T | string, listener: () => void): () => void; /** * 전역 구독 / Global subscription * isModified 등을 위해 사용 / Used for isModified etc. * @param listener 변경 시 호출될 콜백 / Callback to call on change * @returns 구독 해제 함수 / Unsubscribe function */ subscribeGlobal(listener: () => void): () => void; /** * 필드 값 설정 / Set field value * Dot notation 지원 / Supports dot notation * @param fieldName 필드명 또는 dot notation 경로 / Field name or dot notation path * @param value 설정할 값 / Value to set */ setValue(fieldName: keyof T | string, value: any): void; /** * 모든 값 가져오기 / Get all values * ⚠ 변경 없으면 같은 캐시 객체를 반환한다 — 호출부는 결과를 읽기 전용으로 다뤄야 한다. * (같은 세대 안에서 참조가 안정적이므로 getSnapshot 용도로 사용 가능) * ⚠ Returns the SAME cached object until the next mutation — callers must treat * the result as read-only. Reference is stable within a version (getSnapshot-safe). * @returns 모든 필드 값을 포함한 객체 / Object containing all field values */ getValues(): T; /** * 모든 값 설정 / Set all values * @param newValues 설정할 값들 / Values to set */ setValues(newValues: Partial): void; /** * 초기값 재설정 / Reset initial values * @param newInitialValues 새로운 초기값 / New initial values */ setInitialValues(newInitialValues: T): void; /** * 수정 여부 확인 / Check if modified * @returns 초기값에서 변경되었는지 여부 / Whether changed from initial values */ isModified(): boolean; /** * 객체에 비어있지 않은 값이 있는지 확인 / Check if object has non-empty values */ private hasNonEmptyValues; private hasMeaningfulValue; /** * 특정 필드가 존재하는지 확인 / Check if a specific field exists * @param path 필드 경로 (dot notation 지원) / Field path (supports dot notation) * @returns 필드 존재 여부 / Whether the field exists */ hasField(path: string): boolean; /** * 특정 필드를 제거 / Remove a specific field * @param path 필드 경로 (dot notation 지원) / Field path (supports dot notation) */ removeField(path: string): void; /** * 전역 상태 변경에 구독 / Subscribe to global state changes * @param callback 상태 변경 시 실행될 콜백 / Callback to execute on state change * @returns 구독 해제 함수 / Unsubscribe function */ subscribeToAll(callback: (values: T) => void): () => void; /** * 특정 prefix를 가진 모든 필드 구독자들을 새로고침합니다 * Refresh all field subscribers with specific prefix * @param prefix 새로고침할 필드 prefix (예: "address") */ refreshFields(prefix: string): void; /** * Batch update multiple fields efficiently * 여러 필드를 효율적으로 일괄 업데이트 * @param updates - 업데이트할 필드들의 키-값 쌍 */ setBatch(updates: Record): void; /** * Set value without immediately notifying listeners (for batch operations) * 리스너 알림 없이 값 설정 (배치 작업용) */ private setValueWithoutNotify; /** * 초기값으로 리셋 / Reset to initial values */ reset(): void; /** * 필드 변경 감시 / Watch field changes * @param path 감시할 필드 경로 (dot notation 지원) / Field path to watch (supports dot notation) * @param callback 변경 시 실행할 콜백 / Callback to execute on change * @param options 옵션 / Options * @returns cleanup 함수 / Cleanup function */ watch(path: string, callback: WatchCallback, options?: { immediate?: boolean; }): () => void; /** * Watcher 알림 실행 / Notify watchers * @param path 변경된 필드 경로 / Changed field path * @param value 새 값 / New value * @param prevValue 이전 값 / Previous value * @param prevParentValues 부모 경로들의 이전 값 맵 / Map of previous values for parent paths */ private notifyWatchers; /** * 와일드카드 패턴 매칭 / Wildcard pattern matching * @param path 실제 경로 / Actual path (e.g., "todos.0.completed") * @param pattern 와일드카드 패턴 / Wildcard pattern (e.g., "todos.*.completed") * @returns 매칭 여부 / Whether path matches pattern */ private matchesWildcard; /** * 특정 path에 watcher가 등록되어 있는지 확인 / Check if watcher is registered for specific path * @param path 확인할 경로 / Path to check * @returns watcher 등록 여부 / Whether watcher is registered */ hasWatcher(path: string): boolean; /** * 등록된 모든 watcher path 목록 반환 (디버깅용) / Return all registered watcher paths (for debugging) * @returns watcher path 배열 / Array of watcher paths */ getWatchedPaths(): string[]; /** * store 의 명령형 API 묶음을 반환한다 (지연 생성 후 재사용). * Return the store's imperative API surface (lazily created, then reused). * 모든 메서드는 화살표 래퍼로 pre-bound 되어 store 수명 동안 식별자가 안정적이다 * — hooks 의 useCallback/useMemo deps 에 그대로 사용할 수 있다. * Every method is pre-bound via arrow wrappers; identities are stable for the * store's lifetime, safe for hooks' useCallback/useMemo deps. */ getApi(): FieldStoreApi; /** * 리소스 정리 / Clean up resources */ destroy(): void; } export {}; //# sourceMappingURL=FieldStore.d.ts.map