jell-utils 0.1.0 → 0.2.0
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/.claude/commands/tm/add-dependency.md +58 -0
- package/.claude/commands/tm/add-subtask.md +79 -0
- package/.claude/commands/tm/add-task.md +81 -0
- package/.claude/commands/tm/analyze-complexity.md +124 -0
- package/.claude/commands/tm/analyze-project.md +100 -0
- package/.claude/commands/tm/auto-implement-tasks.md +100 -0
- package/.claude/commands/tm/command-pipeline.md +80 -0
- package/.claude/commands/tm/complexity-report.md +120 -0
- package/.claude/commands/tm/convert-task-to-subtask.md +74 -0
- package/.claude/commands/tm/expand-all-tasks.md +52 -0
- package/.claude/commands/tm/expand-task.md +52 -0
- package/.claude/commands/tm/fix-dependencies.md +82 -0
- package/.claude/commands/tm/help.md +101 -0
- package/.claude/commands/tm/init-project-quick.md +49 -0
- package/.claude/commands/tm/init-project.md +53 -0
- package/.claude/commands/tm/install-taskmaster.md +118 -0
- package/.claude/commands/tm/learn.md +106 -0
- package/.claude/commands/tm/list-tasks-by-status.md +42 -0
- package/.claude/commands/tm/list-tasks-with-subtasks.md +30 -0
- package/.claude/commands/tm/list-tasks.md +46 -0
- package/.claude/commands/tm/next-task.md +69 -0
- package/.claude/commands/tm/parse-prd-with-research.md +51 -0
- package/.claude/commands/tm/parse-prd.md +52 -0
- package/.claude/commands/tm/project-status.md +67 -0
- package/.claude/commands/tm/quick-install-taskmaster.md +23 -0
- package/.claude/commands/tm/remove-all-subtasks.md +94 -0
- package/.claude/commands/tm/remove-dependency.md +65 -0
- package/.claude/commands/tm/remove-subtask.md +87 -0
- package/.claude/commands/tm/remove-subtasks.md +89 -0
- package/.claude/commands/tm/remove-task.md +110 -0
- package/.claude/commands/tm/setup-models.md +52 -0
- package/.claude/commands/tm/show-task.md +85 -0
- package/.claude/commands/tm/smart-workflow.md +58 -0
- package/.claude/commands/tm/sync-readme.md +120 -0
- package/.claude/commands/tm/tm-main.md +147 -0
- package/.claude/commands/tm/to-cancelled.md +58 -0
- package/.claude/commands/tm/to-deferred.md +50 -0
- package/.claude/commands/tm/to-done.md +47 -0
- package/.claude/commands/tm/to-in-progress.md +39 -0
- package/.claude/commands/tm/to-pending.md +35 -0
- package/.claude/commands/tm/to-review.md +43 -0
- package/.claude/commands/tm/update-single-task.md +122 -0
- package/.claude/commands/tm/update-task.md +75 -0
- package/.claude/commands/tm/update-tasks-from-id.md +111 -0
- package/.claude/commands/tm/validate-dependencies.md +72 -0
- package/.claude/commands/tm/view-models.md +52 -0
- package/.claude/settings.local.json +17 -3
- package/.cursor/commands/tm/add-dependency.md +55 -0
- package/.cursor/commands/tm/add-subtask.md +76 -0
- package/.cursor/commands/tm/add-task.md +78 -0
- package/.cursor/commands/tm/analyze-complexity.md +121 -0
- package/.cursor/commands/tm/analyze-project.md +97 -0
- package/.cursor/commands/tm/auto-implement-tasks.md +97 -0
- package/.cursor/commands/tm/command-pipeline.md +77 -0
- package/.cursor/commands/tm/complexity-report.md +117 -0
- package/.cursor/commands/tm/convert-task-to-subtask.md +71 -0
- package/.cursor/commands/tm/expand-all-tasks.md +51 -0
- package/.cursor/commands/tm/expand-task.md +49 -0
- package/.cursor/commands/tm/fix-dependencies.md +81 -0
- package/.cursor/commands/tm/help.md +98 -0
- package/.cursor/commands/tm/init-project-quick.md +46 -0
- package/.cursor/commands/tm/init-project.md +50 -0
- package/.cursor/commands/tm/install-taskmaster.md +117 -0
- package/.cursor/commands/tm/learn.md +103 -0
- package/.cursor/commands/tm/list-tasks-by-status.md +39 -0
- package/.cursor/commands/tm/list-tasks-with-subtasks.md +29 -0
- package/.cursor/commands/tm/list-tasks.md +43 -0
- package/.cursor/commands/tm/next-task.md +66 -0
- package/.cursor/commands/tm/parse-prd-with-research.md +48 -0
- package/.cursor/commands/tm/parse-prd.md +49 -0
- package/.cursor/commands/tm/project-status.md +64 -0
- package/.cursor/commands/tm/quick-install-taskmaster.md +22 -0
- package/.cursor/commands/tm/remove-all-subtasks.md +93 -0
- package/.cursor/commands/tm/remove-dependency.md +62 -0
- package/.cursor/commands/tm/remove-subtask.md +84 -0
- package/.cursor/commands/tm/remove-subtasks.md +86 -0
- package/.cursor/commands/tm/remove-task.md +107 -0
- package/.cursor/commands/tm/setup-models.md +51 -0
- package/.cursor/commands/tm/show-task.md +82 -0
- package/.cursor/commands/tm/smart-workflow.md +55 -0
- package/.cursor/commands/tm/sync-readme.md +117 -0
- package/.cursor/commands/tm/tm-main.md +146 -0
- package/.cursor/commands/tm/to-cancelled.md +55 -0
- package/.cursor/commands/tm/to-deferred.md +47 -0
- package/.cursor/commands/tm/to-done.md +44 -0
- package/.cursor/commands/tm/to-in-progress.md +36 -0
- package/.cursor/commands/tm/to-pending.md +32 -0
- package/.cursor/commands/tm/to-review.md +40 -0
- package/.cursor/commands/tm/update-single-task.md +119 -0
- package/.cursor/commands/tm/update-task.md +72 -0
- package/.cursor/commands/tm/update-tasks-from-id.md +108 -0
- package/.cursor/commands/tm/validate-dependencies.md +71 -0
- package/.cursor/commands/tm/view-models.md +51 -0
- package/.cursor/mcp.json +20 -0
- package/.cursor/rules/cursor_rules.mdc +53 -0
- package/.cursor/rules/self_improve.mdc +72 -0
- package/.cursor/rules/taskmaster/dev_workflow.mdc +424 -0
- package/.cursor/rules/taskmaster/taskmaster.mdc +573 -0
- package/.env.example +12 -0
- package/.gemini/commands/tm/add-dependency.toml +58 -0
- package/.gemini/commands/tm/add-subtask.toml +79 -0
- package/.gemini/commands/tm/add-task.toml +81 -0
- package/.gemini/commands/tm/analyze-complexity.toml +124 -0
- package/.gemini/commands/tm/analyze-project.toml +100 -0
- package/.gemini/commands/tm/auto-implement-tasks.toml +100 -0
- package/.gemini/commands/tm/command-pipeline.toml +80 -0
- package/.gemini/commands/tm/complexity-report.toml +120 -0
- package/.gemini/commands/tm/convert-task-to-subtask.toml +74 -0
- package/.gemini/commands/tm/expand-all-tasks.toml +54 -0
- package/.gemini/commands/tm/expand-task.toml +52 -0
- package/.gemini/commands/tm/fix-dependencies.toml +84 -0
- package/.gemini/commands/tm/help.toml +101 -0
- package/.gemini/commands/tm/init-project-quick.toml +49 -0
- package/.gemini/commands/tm/init-project.toml +53 -0
- package/.gemini/commands/tm/install-taskmaster.toml +120 -0
- package/.gemini/commands/tm/learn.toml +106 -0
- package/.gemini/commands/tm/list-tasks-by-status.toml +42 -0
- package/.gemini/commands/tm/list-tasks-with-subtasks.toml +32 -0
- package/.gemini/commands/tm/list-tasks.toml +46 -0
- package/.gemini/commands/tm/next-task.toml +69 -0
- package/.gemini/commands/tm/parse-prd-with-research.toml +51 -0
- package/.gemini/commands/tm/parse-prd.toml +52 -0
- package/.gemini/commands/tm/project-status.toml +67 -0
- package/.gemini/commands/tm/quick-install-taskmaster.toml +25 -0
- package/.gemini/commands/tm/remove-all-subtasks.toml +96 -0
- package/.gemini/commands/tm/remove-dependency.toml +65 -0
- package/.gemini/commands/tm/remove-subtask.toml +87 -0
- package/.gemini/commands/tm/remove-subtasks.toml +89 -0
- package/.gemini/commands/tm/remove-task.toml +110 -0
- package/.gemini/commands/tm/setup-models.toml +54 -0
- package/.gemini/commands/tm/show-task.toml +85 -0
- package/.gemini/commands/tm/smart-workflow.toml +58 -0
- package/.gemini/commands/tm/sync-readme.toml +120 -0
- package/.gemini/commands/tm/tm-main.toml +149 -0
- package/.gemini/commands/tm/to-cancelled.toml +58 -0
- package/.gemini/commands/tm/to-deferred.toml +50 -0
- package/.gemini/commands/tm/to-done.toml +47 -0
- package/.gemini/commands/tm/to-in-progress.toml +39 -0
- package/.gemini/commands/tm/to-pending.toml +35 -0
- package/.gemini/commands/tm/to-review.toml +43 -0
- package/.gemini/commands/tm/update-single-task.toml +122 -0
- package/.gemini/commands/tm/update-task.toml +75 -0
- package/.gemini/commands/tm/update-tasks-from-id.toml +111 -0
- package/.gemini/commands/tm/validate-dependencies.toml +74 -0
- package/.gemini/commands/tm/view-models.toml +54 -0
- package/.gemini/settings.json +20 -0
- package/.mcp.json +15 -0
- package/.taskmaster/CLAUDE.md +435 -0
- package/.taskmaster/config.json +44 -0
- package/.taskmaster/docs/prd.txt +584 -0
- package/.taskmaster/state.json +6 -0
- package/.taskmaster/tasks/tasks.json +304 -0
- package/.taskmaster/templates/example_prd.txt +47 -0
- package/.taskmaster/templates/example_prd_rpg.txt +511 -0
- package/CLAUDE.md +4 -138
- package/README.md +551 -0
- package/coverage/clover.xml +134 -22
- package/coverage/coverage-final.json +1 -1
- package/coverage/lcov-report/index.html +21 -21
- package/coverage/lcov-report/index.js.html +854 -29
- package/coverage/lcov.info +246 -39
- package/internal-docs/CLAUDE.md +139 -0
- package/internal-docs/NPM_DEPLOYMENT_GUIDE.md +203 -0
- package/internal-docs/OBSIDIAN_PROJECT_PROMPT.md +302 -0
- package/lib/index.d.ts +88 -0
- package/lib/index.js +275 -0
- package/package.json +11 -3
- /package/{DEPLOYMENT.md → internal-docs/DEPLOYMENT.md} +0 -0
- /package/{TEST_RESULTS.md → internal-docs/TEST_RESULTS.md} +0 -0
- /package/{TEST_SIMULATION.md → internal-docs/TEST_SIMULATION.md} +0 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# jell-utils.js - Obsidian 프로젝트 문서
|
|
2
|
+
|
|
3
|
+
## 프로젝트 개요
|
|
4
|
+
|
|
5
|
+
**프로젝트명**: jell-utils
|
|
6
|
+
**버전**: 0.1.0
|
|
7
|
+
**타입**: TypeScript 기반 NPM 패키지
|
|
8
|
+
**목적**: 개인용 JavaScript/TypeScript 유틸리티 함수 라이브러리
|
|
9
|
+
**저장소**: https://github.com/jellive/jell-utils.js
|
|
10
|
+
**NPM**: https://www.npmjs.com/package/jell-utils
|
|
11
|
+
**라이선스**: MIT
|
|
12
|
+
**작성자**: Jell <jellive7@gmail.com>
|
|
13
|
+
|
|
14
|
+
## 프로젝트 상태
|
|
15
|
+
|
|
16
|
+
- ✅ **배포 상태**: 활성 (npm에 배포됨)
|
|
17
|
+
- ✅ **테스트**: 50개 테스트 케이스, 79.67% 커버리지
|
|
18
|
+
- ✅ **빌드**: TypeScript → JavaScript 컴파일
|
|
19
|
+
- ✅ **품질 관리**: ESLint, Prettier 적용
|
|
20
|
+
- ✅ **CI/CD**: GitHub Actions (semantic-release)
|
|
21
|
+
|
|
22
|
+
## 핵심 기능 카테고리
|
|
23
|
+
|
|
24
|
+
### 1. 한국어 관련 기능
|
|
25
|
+
|
|
26
|
+
- `isKorean(message: string)`: 한글 문자 포함 여부 검사
|
|
27
|
+
- `isBusinessNumber(businessNumber: string)`: 사업자등록번호 유효성 검증 (체크섬 알고리즘)
|
|
28
|
+
- `chosungSearch(str: string, search: string)`: 한글 초성 검색 (예: "ㄱㄴㄷ"로 "가나다" 검색)
|
|
29
|
+
- `getKoreanDate(dateString, isYear)`: 한국식 날짜 포맷 (예: "3월 15일", "2024년 3월 15일")
|
|
30
|
+
|
|
31
|
+
### 2. 문자열 처리
|
|
32
|
+
|
|
33
|
+
- `parseTag(txt: string)`: HTML 엔티티 디코딩 (< → <)
|
|
34
|
+
- `toCamelCase(txt: string)`: snake_case → camelCase 변환
|
|
35
|
+
- `toSnakeCase(txt: string)`: 일반 문자열 → snake_case 변환
|
|
36
|
+
- `toTitleCase(txt: string)`: Title Case 변환 (첫 글자 대문자)
|
|
37
|
+
- `toText(txt: string)`: HTML <br> → \n 변환
|
|
38
|
+
- `toHtml(txt: string)`: \n → <br> 변환
|
|
39
|
+
- `clearTag(txt: string, preserveErrorTags)`: XSS 방지하며 HTML 태그 제거
|
|
40
|
+
- `replaceBetween(str, txt, startIndex, endIndex)`: 인덱스 기반 문자열 교체
|
|
41
|
+
- `extractNumbers(str: string)`: 문자열에서 숫자만 추출
|
|
42
|
+
- `maskString(str, visibleStart, visibleEnd, maskChar)`: 민감정보 마스킹 (예: 카드번호)
|
|
43
|
+
- `getFileExtension(filename: string)`: 파일 확장자 추출
|
|
44
|
+
|
|
45
|
+
### 3. 숫자 및 시간 파싱
|
|
46
|
+
|
|
47
|
+
- `parseNumber(target: string, defaultValue: number, isFloat)`: 문자열을 숫자로 안전하게 파싱
|
|
48
|
+
- `parseTime(target: string, defaultValue: number)`: 시간 문자열 파싱 (MM:SS, HH:MM:SS)
|
|
49
|
+
|
|
50
|
+
### 4. 날짜 처리
|
|
51
|
+
|
|
52
|
+
- `getNowDate()`: 현재 시간을 datetime 포맷으로 반환
|
|
53
|
+
- `formatDate(date)`: yyyy-mm-dd 포맷으로 변환
|
|
54
|
+
- `calDDay(date)`: D-day 계산
|
|
55
|
+
- `dateDiff(date1, date2)`: 두 날짜 차이 계산 (일/시간/분/초 객체 반환)
|
|
56
|
+
|
|
57
|
+
### 5. 객체 조작
|
|
58
|
+
|
|
59
|
+
- `clone<T>(obj: T)`: 깊은 복사 (structuredClone 또는 JSON fallback)
|
|
60
|
+
- `getByPath<T>(obj, path, defaultValue)`: 경로 문자열로 중첩 객체 값 가져오기 (예: 'user.profile.name')
|
|
61
|
+
- `setByPath(obj, path, value)`: 경로 문자열로 중첩 객체 값 설정
|
|
62
|
+
- `deepMerge<T>(target, source)`: 객체 깊은 병합
|
|
63
|
+
- `objectToQueryString(obj)`: 객체를 URL 쿼리 스트링으로 변환
|
|
64
|
+
|
|
65
|
+
### 6. 배열 조작
|
|
66
|
+
|
|
67
|
+
- `equalArrays(a, b)`: 배열 비교 (얕은 비교)
|
|
68
|
+
- `groupBy<T>(array, key)`: 배열을 특정 키로 그룹화
|
|
69
|
+
- `sortBy<T>(array, key, order)`: 객체 배열 정렬 (asc/desc)
|
|
70
|
+
|
|
71
|
+
### 7. 비동기 처리
|
|
72
|
+
|
|
73
|
+
- `retry<T>(fn, maxRetries, delay)`: exponential backoff로 비동기 함수 재시도
|
|
74
|
+
|
|
75
|
+
### 8. 브라우저 유틸리티
|
|
76
|
+
|
|
77
|
+
- `isiOS()`: iOS 기기 감지 (User Agent 기반)
|
|
78
|
+
|
|
79
|
+
## 기술 스택
|
|
80
|
+
|
|
81
|
+
### 언어 및 런타임
|
|
82
|
+
|
|
83
|
+
- TypeScript 5.2.2
|
|
84
|
+
- Node.js (ES6+)
|
|
85
|
+
|
|
86
|
+
### 개발 도구
|
|
87
|
+
|
|
88
|
+
- **빌드**: TypeScript Compiler (tsc)
|
|
89
|
+
- **테스트**: Jest 29.7.0 + ts-jest
|
|
90
|
+
- **린트**: ESLint + @typescript-eslint
|
|
91
|
+
- **포맷팅**: Prettier
|
|
92
|
+
- **CI/CD**: semantic-release + GitHub Actions
|
|
93
|
+
|
|
94
|
+
### 의존성 관리
|
|
95
|
+
|
|
96
|
+
- npm (주)
|
|
97
|
+
- yarn (부)
|
|
98
|
+
|
|
99
|
+
## 프로젝트 구조
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
jell-utils.js/
|
|
103
|
+
├── src/
|
|
104
|
+
│ └── index.ts # 메인 소스 파일 (모든 유틸 함수)
|
|
105
|
+
├── lib/
|
|
106
|
+
│ ├── index.js # 컴파일된 JavaScript
|
|
107
|
+
│ └── index.d.ts # TypeScript 타입 정의
|
|
108
|
+
├── __tests__/
|
|
109
|
+
│ ├── korean.test.ts # 한국어 관련 테스트
|
|
110
|
+
│ ├── string.test.ts # 문자열 처리 테스트
|
|
111
|
+
│ ├── number.test.ts # 숫자/시간 파싱 테스트
|
|
112
|
+
│ ├── date.test.ts # 날짜 처리 테스트
|
|
113
|
+
│ ├── object.test.ts # 객체 조작 테스트
|
|
114
|
+
│ ├── array.test.ts # 배열 조작 테스트
|
|
115
|
+
│ └── async.test.ts # 비동기 처리 테스트
|
|
116
|
+
├── coverage/ # 테스트 커버리지 리포트
|
|
117
|
+
├── .github/workflows/ # GitHub Actions CI/CD
|
|
118
|
+
├── package.json
|
|
119
|
+
├── tsconfig.json
|
|
120
|
+
├── jestconfig.json
|
|
121
|
+
└── .npmrc # npm 인증 토큰 (gitignore)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 개발 워크플로우
|
|
125
|
+
|
|
126
|
+
### 로컬 개발
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# 의존성 설치
|
|
130
|
+
npm install
|
|
131
|
+
|
|
132
|
+
# 빌드
|
|
133
|
+
npm run build
|
|
134
|
+
|
|
135
|
+
# 테스트
|
|
136
|
+
npm test
|
|
137
|
+
npm run test:coverage
|
|
138
|
+
|
|
139
|
+
# 린트
|
|
140
|
+
npm run lint
|
|
141
|
+
npm run lint:fix
|
|
142
|
+
|
|
143
|
+
# 포맷팅
|
|
144
|
+
npm run format
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 배포 프로세스
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# 1. 변경사항 커밋
|
|
151
|
+
git add .
|
|
152
|
+
git commit -m "feat: new feature description"
|
|
153
|
+
git push
|
|
154
|
+
|
|
155
|
+
# 2. 버전 업데이트
|
|
156
|
+
npm version patch # 버그 수정
|
|
157
|
+
npm version minor # 새 기능
|
|
158
|
+
npm version major # Breaking changes
|
|
159
|
+
|
|
160
|
+
# 3. 배포 (자동으로 test + lint + build 실행)
|
|
161
|
+
npm publish
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Git Workflow
|
|
165
|
+
|
|
166
|
+
- **브랜치**: master (메인 브랜치)
|
|
167
|
+
- **커밋 컨벤션**: Conventional Commits
|
|
168
|
+
- `feat:` - 새 기능 (MINOR)
|
|
169
|
+
- `fix:` - 버그 수정 (PATCH)
|
|
170
|
+
- `docs:` - 문서 변경
|
|
171
|
+
- `chore:` - 빌드/설정 변경
|
|
172
|
+
- `BREAKING CHANGE:` - Breaking changes (MAJOR)
|
|
173
|
+
|
|
174
|
+
## 품질 관리
|
|
175
|
+
|
|
176
|
+
### 테스트 커버리지
|
|
177
|
+
|
|
178
|
+
- **Statements**: 79.67%
|
|
179
|
+
- **Branch**: 73.38%
|
|
180
|
+
- **Functions**: 88.88%
|
|
181
|
+
- **Lines**: 82.81%
|
|
182
|
+
- **총 테스트**: 50개 (모두 통과)
|
|
183
|
+
|
|
184
|
+
### 자동화된 검증
|
|
185
|
+
|
|
186
|
+
- `prepublishOnly`: 배포 전 test + lint 자동 실행
|
|
187
|
+
- `prepare`: 빌드 전 자동 실행
|
|
188
|
+
- GitHub Actions: PR 및 push 시 CI 자동 실행
|
|
189
|
+
|
|
190
|
+
## 사용 예시
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import util from 'jell-utils'
|
|
194
|
+
|
|
195
|
+
// 한국어 검증
|
|
196
|
+
util.isKorean('안녕하세요') // true
|
|
197
|
+
util.isBusinessNumber('1018626554') // true
|
|
198
|
+
util.chosungSearch('김철수', 'ㄱㅊㅅ') // true
|
|
199
|
+
|
|
200
|
+
// 문자열 처리
|
|
201
|
+
util.toCamelCase('hello_world') // 'helloWorld'
|
|
202
|
+
util.maskString('1234567890', 3, 3) // '123****890'
|
|
203
|
+
|
|
204
|
+
// 객체 조작
|
|
205
|
+
const obj = { user: { profile: { name: 'John' } } }
|
|
206
|
+
util.getByPath(obj, 'user.profile.name') // 'John'
|
|
207
|
+
|
|
208
|
+
// 배열 처리
|
|
209
|
+
const items = [{ age: 30 }, { age: 25 }, { age: 35 }]
|
|
210
|
+
util.sortBy(items, 'age', 'asc') // age 기준 오름차순 정렬
|
|
211
|
+
|
|
212
|
+
// 비동기 재시도
|
|
213
|
+
await util.retry(
|
|
214
|
+
async () => {
|
|
215
|
+
return await fetchData()
|
|
216
|
+
},
|
|
217
|
+
3,
|
|
218
|
+
1000
|
|
219
|
+
) // 3번 재시도, 1초 간격
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 주요 이정표
|
|
223
|
+
|
|
224
|
+
### v0.0.x (초기 버전)
|
|
225
|
+
|
|
226
|
+
- 기본 유틸리티 함수 구현
|
|
227
|
+
- 한국어 관련 기능
|
|
228
|
+
|
|
229
|
+
### v0.1.0 (2025-10-11) ✨ 현재 버전
|
|
230
|
+
|
|
231
|
+
- 50개 포괄적인 테스트 케이스 추가
|
|
232
|
+
- 한국어 특화 기능 강화 (사업자등록번호, 초성 검색)
|
|
233
|
+
- 객체/배열 고급 조작 함수 추가
|
|
234
|
+
- 비동기 retry 유틸리티 추가
|
|
235
|
+
- 테스트 커버리지 79.67% 달성
|
|
236
|
+
- ESLint strict 모드 적용 (any 타입 제거)
|
|
237
|
+
- npm 배포 자동화 개선
|
|
238
|
+
|
|
239
|
+
## 향후 계획
|
|
240
|
+
|
|
241
|
+
### 단기 (v0.2.0)
|
|
242
|
+
|
|
243
|
+
- [ ] 추가 한국어 유틸리티 (주민등록번호 검증, 한글 자모 분리 등)
|
|
244
|
+
- [ ] 테스트 커버리지 90% 이상 달성
|
|
245
|
+
- [ ] 브라우저 호환성 테스트 추가
|
|
246
|
+
- [ ] 성능 벤치마크 추가
|
|
247
|
+
|
|
248
|
+
### 중기 (v0.x.x)
|
|
249
|
+
|
|
250
|
+
- [ ] Tree-shaking 지원 (개별 함수 import)
|
|
251
|
+
- [ ] 웹 워커 지원
|
|
252
|
+
- [ ] React hooks 버전 제공
|
|
253
|
+
- [ ] 더 많은 날짜/시간 유틸리티
|
|
254
|
+
|
|
255
|
+
### 장기 (v1.0.0)
|
|
256
|
+
|
|
257
|
+
- [ ] 안정화 및 API freeze
|
|
258
|
+
- [ ] 완전한 문서화 (JSDoc + 사이트)
|
|
259
|
+
- [ ] 플러그인 시스템
|
|
260
|
+
- [ ] 다국어 지원 확장
|
|
261
|
+
|
|
262
|
+
## 알려진 이슈
|
|
263
|
+
|
|
264
|
+
- TypeScript 5.9.2 사용 중 (ESLint는 5.4.0 미만 권장)
|
|
265
|
+
- jest의 ts-jest 경고 (allowJs 설정 관련)
|
|
266
|
+
- Git push 권한 문제 (credential 재설정 필요)
|
|
267
|
+
|
|
268
|
+
## 참고 문서
|
|
269
|
+
|
|
270
|
+
- [NPM 배포 가이드](./NPM_DEPLOYMENT_GUIDE.md) - 로컬 전용
|
|
271
|
+
- [GitHub Repository](https://github.com/jellive/jell-utils.js)
|
|
272
|
+
- [NPM Package](https://www.npmjs.com/package/jell-utils)
|
|
273
|
+
- [블로그](https://blog.jell.kr)
|
|
274
|
+
|
|
275
|
+
## 프로젝트 관리 메타데이터
|
|
276
|
+
|
|
277
|
+
**프로젝트 타입**: 오픈소스 라이브러리
|
|
278
|
+
**유지보수 상태**: 활발히 개발 중
|
|
279
|
+
**기여 가능 여부**: Yes (MIT 라이선스)
|
|
280
|
+
**주 사용자**: 개인 프로젝트 (공개)
|
|
281
|
+
**업데이트 주기**: 기능 추가 시 비정기적
|
|
282
|
+
**최종 업데이트**: 2025-10-11 (v0.1.0 배포)
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Obsidian 태그
|
|
287
|
+
|
|
288
|
+
#project #typescript #npm #utilities #korean #open-source #active #library
|
|
289
|
+
|
|
290
|
+
## 연관 프로젝트
|
|
291
|
+
|
|
292
|
+
- (여기에 이 라이브러리를 사용하는 다른 프로젝트 링크 추가)
|
|
293
|
+
|
|
294
|
+
## 노트
|
|
295
|
+
|
|
296
|
+
- 이 라이브러리는 개인 프로젝트에서 자주 사용하는 패턴들을 모아놓은 것
|
|
297
|
+
- 한국어 특화 기능이 강점
|
|
298
|
+
- TypeScript로 작성되어 타입 안정성 보장
|
|
299
|
+
- 테스트 커버리지가 높아 안정적
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
package/lib/index.d.ts
CHANGED
|
@@ -211,5 +211,93 @@ declare const util: {
|
|
|
211
211
|
* @returns masked string
|
|
212
212
|
*/
|
|
213
213
|
maskString: (str: string, visibleStart?: number, visibleEnd?: number, maskChar?: string) => string;
|
|
214
|
+
/**
|
|
215
|
+
* @description Format date with custom format tokens
|
|
216
|
+
* @param date Date object or date string
|
|
217
|
+
* @param format Format string with tokens (YYYY, MM, DD, HH, mm, ss, ddd, dddd)
|
|
218
|
+
* @returns Formatted date string
|
|
219
|
+
*/
|
|
220
|
+
formatDateAdvanced: (date: Date | string, format: string) => string;
|
|
221
|
+
/**
|
|
222
|
+
* @description Get relative time string in Korean
|
|
223
|
+
* @param date Date object or date string
|
|
224
|
+
* @returns Korean relative time string (e.g., '방금 전', '5분 전', '어제')
|
|
225
|
+
*/
|
|
226
|
+
timeAgo: (date: Date | string) => string;
|
|
227
|
+
/**
|
|
228
|
+
* @description Get the number of days in a month
|
|
229
|
+
* @param year Year (e.g., 2025)
|
|
230
|
+
* @param month Month (1-12)
|
|
231
|
+
* @returns Number of days in the month
|
|
232
|
+
*/
|
|
233
|
+
getDaysInMonth: (year: number, month: number) => number;
|
|
234
|
+
/**
|
|
235
|
+
* @description Check if a year is a leap year
|
|
236
|
+
* @param year Year to check
|
|
237
|
+
* @returns true if leap year, false otherwise
|
|
238
|
+
*/
|
|
239
|
+
isLeapYear: (year: number) => boolean;
|
|
240
|
+
/**
|
|
241
|
+
* @description Format number with thousand separators
|
|
242
|
+
* @param num Number to format
|
|
243
|
+
* @returns Formatted string with commas
|
|
244
|
+
*/
|
|
245
|
+
formatNumber: (num: number) => string;
|
|
246
|
+
/**
|
|
247
|
+
* @description Format number as currency
|
|
248
|
+
* @param amount Amount to format
|
|
249
|
+
* @param currency Currency code (KRW, USD, EUR, JPY, CNY)
|
|
250
|
+
* @returns Formatted currency string
|
|
251
|
+
*/
|
|
252
|
+
formatCurrency: (amount: number, currency?: "KRW" | "USD" | "EUR" | "JPY" | "CNY") => string;
|
|
253
|
+
/**
|
|
254
|
+
* @description Format bytes to human-readable file size
|
|
255
|
+
* @param bytes Number of bytes
|
|
256
|
+
* @param precision Decimal precision (default: 0 for B/KB, 2 for larger)
|
|
257
|
+
* @returns Formatted file size string
|
|
258
|
+
*/
|
|
259
|
+
formatFileSize: (bytes: number, precision?: number) => string;
|
|
260
|
+
/**
|
|
261
|
+
* @description Convert number to Korean words
|
|
262
|
+
* @param num Number to convert
|
|
263
|
+
* @returns Korean number string (e.g., '천이백삼십사')
|
|
264
|
+
*/
|
|
265
|
+
numberToKorean: (num: number) => string;
|
|
266
|
+
/**
|
|
267
|
+
* @description Parse number from formatted string
|
|
268
|
+
* @param str String containing number (with commas, currency symbols, etc.)
|
|
269
|
+
* @returns Parsed number
|
|
270
|
+
*/
|
|
271
|
+
parseNumberFromString: (str: string) => number;
|
|
272
|
+
/**
|
|
273
|
+
* @description Validate email address
|
|
274
|
+
* @param email Email address to validate
|
|
275
|
+
* @returns true if valid email, false otherwise
|
|
276
|
+
*/
|
|
277
|
+
isEmail: (email: string) => boolean;
|
|
278
|
+
/**
|
|
279
|
+
* @description Validate Korean phone number
|
|
280
|
+
* @param phone Phone number to validate
|
|
281
|
+
* @returns true if valid Korean phone number, false otherwise
|
|
282
|
+
*/
|
|
283
|
+
isPhoneNumber: (phone: string) => boolean;
|
|
284
|
+
/**
|
|
285
|
+
* @description Validate URL
|
|
286
|
+
* @param url URL to validate
|
|
287
|
+
* @returns true if valid URL, false otherwise
|
|
288
|
+
*/
|
|
289
|
+
isUrl: (url: string) => boolean;
|
|
290
|
+
/**
|
|
291
|
+
* @description Format Korean phone number with hyphens
|
|
292
|
+
* @param phone Phone number to format
|
|
293
|
+
* @returns Formatted phone number
|
|
294
|
+
*/
|
|
295
|
+
formatPhoneNumber: (phone: string) => string;
|
|
296
|
+
/**
|
|
297
|
+
* @description Format Korean business registration number
|
|
298
|
+
* @param brn Business registration number
|
|
299
|
+
* @returns Formatted business number (XXX-XX-XXXXX)
|
|
300
|
+
*/
|
|
301
|
+
formatBusinessNumber: (brn: string) => string;
|
|
214
302
|
};
|
|
215
303
|
export default util;
|
package/lib/index.js
CHANGED
|
@@ -653,6 +653,281 @@ var util = {
|
|
|
653
653
|
var end = str.substring(str.length - visibleEnd);
|
|
654
654
|
var masked = maskChar.repeat(str.length - visibleStart - visibleEnd);
|
|
655
655
|
return start + masked + end;
|
|
656
|
+
},
|
|
657
|
+
// ==================== Date Utilities (v0.2.0) ====================
|
|
658
|
+
/**
|
|
659
|
+
* @description Format date with custom format tokens
|
|
660
|
+
* @param date Date object or date string
|
|
661
|
+
* @param format Format string with tokens (YYYY, MM, DD, HH, mm, ss, ddd, dddd)
|
|
662
|
+
* @returns Formatted date string
|
|
663
|
+
*/
|
|
664
|
+
formatDateAdvanced: function (date, format) {
|
|
665
|
+
var d = new Date(date);
|
|
666
|
+
var weekdays = [
|
|
667
|
+
'일요일',
|
|
668
|
+
'월요일',
|
|
669
|
+
'화요일',
|
|
670
|
+
'수요일',
|
|
671
|
+
'목요일',
|
|
672
|
+
'금요일',
|
|
673
|
+
'토요일'
|
|
674
|
+
];
|
|
675
|
+
var replacements = {
|
|
676
|
+
YYYY: d.getFullYear().toString(),
|
|
677
|
+
MM: (d.getMonth() + 1).toString().padStart(2, '0'),
|
|
678
|
+
DD: d.getDate().toString().padStart(2, '0'),
|
|
679
|
+
HH: d.getHours().toString().padStart(2, '0'),
|
|
680
|
+
mm: d.getMinutes().toString().padStart(2, '0'),
|
|
681
|
+
ss: d.getSeconds().toString().padStart(2, '0'),
|
|
682
|
+
dddd: weekdays[d.getDay()],
|
|
683
|
+
ddd: weekdays[d.getDay()].charAt(0)
|
|
684
|
+
};
|
|
685
|
+
var result = format;
|
|
686
|
+
// Replace longer tokens first to avoid partial matches
|
|
687
|
+
var tokens = ['YYYY', 'dddd', 'ddd', 'MM', 'DD', 'HH', 'mm', 'ss'];
|
|
688
|
+
for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
|
|
689
|
+
var token = tokens_1[_i];
|
|
690
|
+
result = result.replace(new RegExp(token, 'g'), replacements[token]);
|
|
691
|
+
}
|
|
692
|
+
return result;
|
|
693
|
+
},
|
|
694
|
+
/**
|
|
695
|
+
* @description Get relative time string in Korean
|
|
696
|
+
* @param date Date object or date string
|
|
697
|
+
* @returns Korean relative time string (e.g., '방금 전', '5분 전', '어제')
|
|
698
|
+
*/
|
|
699
|
+
timeAgo: function (date) {
|
|
700
|
+
var now = Date.now();
|
|
701
|
+
var past = new Date(date).getTime();
|
|
702
|
+
var diff = now - past;
|
|
703
|
+
var minute = 60 * 1000;
|
|
704
|
+
var hour = 60 * minute;
|
|
705
|
+
var day = 24 * hour;
|
|
706
|
+
var week = 7 * day;
|
|
707
|
+
var month = 30 * day;
|
|
708
|
+
var year = 365 * day;
|
|
709
|
+
if (diff < minute)
|
|
710
|
+
return '방금 전';
|
|
711
|
+
if (diff < hour)
|
|
712
|
+
return "".concat(Math.floor(diff / minute), "\uBD84 \uC804");
|
|
713
|
+
if (diff < day)
|
|
714
|
+
return "".concat(Math.floor(diff / hour), "\uC2DC\uAC04 \uC804");
|
|
715
|
+
if (diff < 2 * day)
|
|
716
|
+
return '어제';
|
|
717
|
+
if (diff < week)
|
|
718
|
+
return "".concat(Math.floor(diff / day), "\uC77C \uC804");
|
|
719
|
+
if (diff < month)
|
|
720
|
+
return "".concat(Math.floor(diff / week), "\uC8FC \uC804");
|
|
721
|
+
if (diff < year)
|
|
722
|
+
return "".concat(Math.floor(diff / month), "\uAC1C\uC6D4 \uC804");
|
|
723
|
+
return "".concat(Math.floor(diff / year), "\uB144 \uC804");
|
|
724
|
+
},
|
|
725
|
+
/**
|
|
726
|
+
* @description Get the number of days in a month
|
|
727
|
+
* @param year Year (e.g., 2025)
|
|
728
|
+
* @param month Month (1-12)
|
|
729
|
+
* @returns Number of days in the month
|
|
730
|
+
*/
|
|
731
|
+
getDaysInMonth: function (year, month) {
|
|
732
|
+
return new Date(year, month, 0).getDate();
|
|
733
|
+
},
|
|
734
|
+
/**
|
|
735
|
+
* @description Check if a year is a leap year
|
|
736
|
+
* @param year Year to check
|
|
737
|
+
* @returns true if leap year, false otherwise
|
|
738
|
+
*/
|
|
739
|
+
isLeapYear: function (year) {
|
|
740
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
741
|
+
},
|
|
742
|
+
// ==================== Number Utilities (v0.2.0) ====================
|
|
743
|
+
/**
|
|
744
|
+
* @description Format number with thousand separators
|
|
745
|
+
* @param num Number to format
|
|
746
|
+
* @returns Formatted string with commas
|
|
747
|
+
*/
|
|
748
|
+
formatNumber: function (num) {
|
|
749
|
+
return num.toLocaleString('en-US');
|
|
750
|
+
},
|
|
751
|
+
/**
|
|
752
|
+
* @description Format number as currency
|
|
753
|
+
* @param amount Amount to format
|
|
754
|
+
* @param currency Currency code (KRW, USD, EUR, JPY, CNY)
|
|
755
|
+
* @returns Formatted currency string
|
|
756
|
+
*/
|
|
757
|
+
formatCurrency: function (amount, currency) {
|
|
758
|
+
if (currency === void 0) { currency = 'KRW'; }
|
|
759
|
+
var formatted = util.formatNumber(amount);
|
|
760
|
+
var symbols = {
|
|
761
|
+
KRW: { symbol: '원', prefix: false },
|
|
762
|
+
USD: { symbol: '$', prefix: true },
|
|
763
|
+
EUR: { symbol: '€', prefix: true },
|
|
764
|
+
JPY: { symbol: '¥', prefix: true },
|
|
765
|
+
CNY: { symbol: '¥', prefix: true }
|
|
766
|
+
};
|
|
767
|
+
var _a = symbols[currency], symbol = _a.symbol, prefix = _a.prefix;
|
|
768
|
+
return prefix ? "".concat(symbol).concat(formatted) : "".concat(formatted).concat(symbol);
|
|
769
|
+
},
|
|
770
|
+
/**
|
|
771
|
+
* @description Format bytes to human-readable file size
|
|
772
|
+
* @param bytes Number of bytes
|
|
773
|
+
* @param precision Decimal precision (default: 0 for B/KB, 2 for larger)
|
|
774
|
+
* @returns Formatted file size string
|
|
775
|
+
*/
|
|
776
|
+
formatFileSize: function (bytes, precision) {
|
|
777
|
+
var units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
778
|
+
var size = bytes;
|
|
779
|
+
var unitIndex = 0;
|
|
780
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
781
|
+
size /= 1024;
|
|
782
|
+
unitIndex++;
|
|
783
|
+
}
|
|
784
|
+
var decimals = precision !== undefined ? precision : unitIndex === 0 ? 0 : 0;
|
|
785
|
+
return "".concat(size.toFixed(decimals), " ").concat(units[unitIndex]);
|
|
786
|
+
},
|
|
787
|
+
/**
|
|
788
|
+
* @description Convert number to Korean words
|
|
789
|
+
* @param num Number to convert
|
|
790
|
+
* @returns Korean number string (e.g., '천이백삼십사')
|
|
791
|
+
*/
|
|
792
|
+
numberToKorean: function (num) {
|
|
793
|
+
if (num === 0)
|
|
794
|
+
return '영';
|
|
795
|
+
var digits = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
|
|
796
|
+
var units = ['', '십', '백', '천'];
|
|
797
|
+
var bigUnits = ['', '만', '억', '조'];
|
|
798
|
+
var processGroup = function (n) {
|
|
799
|
+
var result = '';
|
|
800
|
+
var temp = n;
|
|
801
|
+
for (var i = 3; i >= 0; i--) {
|
|
802
|
+
var digit = Math.floor(temp / Math.pow(10, i));
|
|
803
|
+
temp %= Math.pow(10, i);
|
|
804
|
+
if (digit > 0) {
|
|
805
|
+
// Omit '일' before 십, 백, 천 (but not for units[0])
|
|
806
|
+
if (digit > 1 || i === 0) {
|
|
807
|
+
result += digits[digit];
|
|
808
|
+
}
|
|
809
|
+
result += units[i];
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return result;
|
|
813
|
+
};
|
|
814
|
+
var result = '';
|
|
815
|
+
var groupIndex = 0;
|
|
816
|
+
var remaining = num;
|
|
817
|
+
while (remaining > 0) {
|
|
818
|
+
var group = remaining % 10000;
|
|
819
|
+
if (group > 0) {
|
|
820
|
+
var groupStr = processGroup(group);
|
|
821
|
+
// '만' 단위에서 1은 생략, '억' 이상에서는 '일' 포함
|
|
822
|
+
if (group === 1 && groupIndex >= 1) {
|
|
823
|
+
if (groupIndex >= 2) {
|
|
824
|
+
// 억 이상: '일억', '일조'
|
|
825
|
+
result = '일' + bigUnits[groupIndex] + result;
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
// 만: '만' (일 생략)
|
|
829
|
+
result = bigUnits[groupIndex] + result;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
result = groupStr + bigUnits[groupIndex] + result;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
remaining = Math.floor(remaining / 10000);
|
|
837
|
+
groupIndex++;
|
|
838
|
+
}
|
|
839
|
+
return result;
|
|
840
|
+
},
|
|
841
|
+
/**
|
|
842
|
+
* @description Parse number from formatted string
|
|
843
|
+
* @param str String containing number (with commas, currency symbols, etc.)
|
|
844
|
+
* @returns Parsed number
|
|
845
|
+
*/
|
|
846
|
+
parseNumberFromString: function (str) {
|
|
847
|
+
var cleaned = str.replace(/[^0-9.-]/g, '');
|
|
848
|
+
return parseFloat(cleaned);
|
|
849
|
+
},
|
|
850
|
+
// ==================== Validation Utilities (v0.2.0) ====================
|
|
851
|
+
/**
|
|
852
|
+
* @description Validate email address
|
|
853
|
+
* @param email Email address to validate
|
|
854
|
+
* @returns true if valid email, false otherwise
|
|
855
|
+
*/
|
|
856
|
+
isEmail: function (email) {
|
|
857
|
+
var emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
858
|
+
return emailRegex.test(email);
|
|
859
|
+
},
|
|
860
|
+
/**
|
|
861
|
+
* @description Validate Korean phone number
|
|
862
|
+
* @param phone Phone number to validate
|
|
863
|
+
* @returns true if valid Korean phone number, false otherwise
|
|
864
|
+
*/
|
|
865
|
+
isPhoneNumber: function (phone) {
|
|
866
|
+
var mobileRegex = /^01[016789]-?\d{3,4}-?\d{4}$/;
|
|
867
|
+
var landlineRegex = /^0[2-6][1-5]?-?\d{3,4}-?\d{4}$/;
|
|
868
|
+
return mobileRegex.test(phone) || landlineRegex.test(phone);
|
|
869
|
+
},
|
|
870
|
+
/**
|
|
871
|
+
* @description Validate URL
|
|
872
|
+
* @param url URL to validate
|
|
873
|
+
* @returns true if valid URL, false otherwise
|
|
874
|
+
*/
|
|
875
|
+
isUrl: function (url) {
|
|
876
|
+
try {
|
|
877
|
+
var parsed = new URL(url);
|
|
878
|
+
return ['http:', 'https:', 'ftp:'].includes(parsed.protocol);
|
|
879
|
+
}
|
|
880
|
+
catch (_a) {
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
},
|
|
884
|
+
/**
|
|
885
|
+
* @description Format Korean phone number with hyphens
|
|
886
|
+
* @param phone Phone number to format
|
|
887
|
+
* @returns Formatted phone number
|
|
888
|
+
*/
|
|
889
|
+
formatPhoneNumber: function (phone) {
|
|
890
|
+
var digits = phone.replace(/[^0-9]/g, '');
|
|
891
|
+
if (digits.startsWith('02')) {
|
|
892
|
+
// Seoul: 02-XXXX-XXXX
|
|
893
|
+
if (digits.length === 10) {
|
|
894
|
+
return "".concat(digits.slice(0, 2), "-").concat(digits.slice(2, 6), "-").concat(digits.slice(6));
|
|
895
|
+
}
|
|
896
|
+
else if (digits.length === 9) {
|
|
897
|
+
return "".concat(digits.slice(0, 2), "-").concat(digits.slice(2, 5), "-").concat(digits.slice(5));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
else if (digits.startsWith('01')) {
|
|
901
|
+
// Mobile: 01X-XXXX-XXXX or 01X-XXX-XXXX
|
|
902
|
+
if (digits.length === 11) {
|
|
903
|
+
return "".concat(digits.slice(0, 3), "-").concat(digits.slice(3, 7), "-").concat(digits.slice(7));
|
|
904
|
+
}
|
|
905
|
+
else if (digits.length === 10) {
|
|
906
|
+
return "".concat(digits.slice(0, 3), "-").concat(digits.slice(3, 6), "-").concat(digits.slice(6));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
else if (/^0[3-6]/.test(digits)) {
|
|
910
|
+
// Area code: 0XX-XXXX-XXXX
|
|
911
|
+
if (digits.length === 11) {
|
|
912
|
+
return "".concat(digits.slice(0, 3), "-").concat(digits.slice(3, 7), "-").concat(digits.slice(7));
|
|
913
|
+
}
|
|
914
|
+
else if (digits.length === 10) {
|
|
915
|
+
return "".concat(digits.slice(0, 3), "-").concat(digits.slice(3, 6), "-").concat(digits.slice(6));
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return phone;
|
|
919
|
+
},
|
|
920
|
+
/**
|
|
921
|
+
* @description Format Korean business registration number
|
|
922
|
+
* @param brn Business registration number
|
|
923
|
+
* @returns Formatted business number (XXX-XX-XXXXX)
|
|
924
|
+
*/
|
|
925
|
+
formatBusinessNumber: function (brn) {
|
|
926
|
+
var digits = brn.replace(/[^0-9]/g, '');
|
|
927
|
+
if (digits.length !== 10) {
|
|
928
|
+
return brn;
|
|
929
|
+
}
|
|
930
|
+
return "".concat(digits.slice(0, 3), "-").concat(digits.slice(3, 5), "-").concat(digits.slice(5));
|
|
656
931
|
}
|
|
657
932
|
};
|
|
658
933
|
exports.default = util;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jell-utils",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Korean-specialized TypeScript utility library with date, number, string, and validation utilities.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"author": "Jell <jellive7@gmail.com> (https://blog.jell.kr)",
|
|
@@ -15,7 +15,15 @@
|
|
|
15
15
|
"string-manipulation",
|
|
16
16
|
"date-formatting",
|
|
17
17
|
"korean-language",
|
|
18
|
-
"browser-utilities"
|
|
18
|
+
"browser-utilities",
|
|
19
|
+
"date-utils",
|
|
20
|
+
"number-formatting",
|
|
21
|
+
"validation",
|
|
22
|
+
"korean-utils",
|
|
23
|
+
"file-size",
|
|
24
|
+
"currency-formatting",
|
|
25
|
+
"phone-number",
|
|
26
|
+
"business-number"
|
|
19
27
|
],
|
|
20
28
|
"repository": {
|
|
21
29
|
"type": "git",
|