d3ro-voice/apps/desktop/src/main/services/memo/memo-export-file-name.ts

35 lines
1.6 KiB
TypeScript

// src/main/services/memo/memo-export-file-name.ts
// 메모 내보내기 파일명 정책 (순수 함수). 태그는 자유 텍스트이고 폰 동기화로도 들어오므로
// 경로 구분자·NTFS 예약 문자·제어 문자를 그대로 파일명에 넣으면 하위 폴더 ENOENT,
// NTFS 대체 데이터 스트림(':'), exports 폴더 이탈('..')이 생긴다. 여기서 한 번에 막는다.
/** 파일명에 들어갈 태그 조각의 최대 길이(코드 포인트 기준). */
export const MEMO_EXPORT_TAG_MAX_LENGTH = 64
// Windows 예약 문자 + 경로 구분자 + C0/DEL 제어 문자
// eslint-disable-next-line no-control-regex
const UNSAFE_FILE_NAME_CHARS = /[\\/:*?"<>|\u0000-\u001f\u007f]/g
/**
* 태그를 단일 파일명 세그먼트로 안전하게 바꾼다.
* 결과가 비면 빈 문자열을 돌려주고, 호출자는 태그 접미사를 생략한다.
*/
export function sanitizeMemoExportTag(tag: string): string {
const replaced = tag
.normalize('NFC')
.replace(UNSAFE_FILE_NAME_CHARS, '_')
.replace(/\s+/g, '_')
.replace(/\.{2,}/g, '.')
.replace(/_+/g, '_')
.replace(/^[._]+|[._]+$/g, '')
const capped = Array.from(replaced).slice(0, MEMO_EXPORT_TAG_MAX_LENGTH).join('')
return capped.replace(/[._]+$/g, '')
}
/** `memo[_<safe-tag>]_<timestamp>.md` 형식의 파일명을 만든다. */
export function buildMemoExportFileName(tag: string | undefined, timestamp: number): string {
const safeTag = tag ? sanitizeMemoExportTag(tag) : ''
const tagSuffix = safeTag ? `_${safeTag}` : ''
return `memo${tagSuffix}_${timestamp}.md`
}