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.
Files changed (170) hide show
  1. package/.claude/commands/tm/add-dependency.md +58 -0
  2. package/.claude/commands/tm/add-subtask.md +79 -0
  3. package/.claude/commands/tm/add-task.md +81 -0
  4. package/.claude/commands/tm/analyze-complexity.md +124 -0
  5. package/.claude/commands/tm/analyze-project.md +100 -0
  6. package/.claude/commands/tm/auto-implement-tasks.md +100 -0
  7. package/.claude/commands/tm/command-pipeline.md +80 -0
  8. package/.claude/commands/tm/complexity-report.md +120 -0
  9. package/.claude/commands/tm/convert-task-to-subtask.md +74 -0
  10. package/.claude/commands/tm/expand-all-tasks.md +52 -0
  11. package/.claude/commands/tm/expand-task.md +52 -0
  12. package/.claude/commands/tm/fix-dependencies.md +82 -0
  13. package/.claude/commands/tm/help.md +101 -0
  14. package/.claude/commands/tm/init-project-quick.md +49 -0
  15. package/.claude/commands/tm/init-project.md +53 -0
  16. package/.claude/commands/tm/install-taskmaster.md +118 -0
  17. package/.claude/commands/tm/learn.md +106 -0
  18. package/.claude/commands/tm/list-tasks-by-status.md +42 -0
  19. package/.claude/commands/tm/list-tasks-with-subtasks.md +30 -0
  20. package/.claude/commands/tm/list-tasks.md +46 -0
  21. package/.claude/commands/tm/next-task.md +69 -0
  22. package/.claude/commands/tm/parse-prd-with-research.md +51 -0
  23. package/.claude/commands/tm/parse-prd.md +52 -0
  24. package/.claude/commands/tm/project-status.md +67 -0
  25. package/.claude/commands/tm/quick-install-taskmaster.md +23 -0
  26. package/.claude/commands/tm/remove-all-subtasks.md +94 -0
  27. package/.claude/commands/tm/remove-dependency.md +65 -0
  28. package/.claude/commands/tm/remove-subtask.md +87 -0
  29. package/.claude/commands/tm/remove-subtasks.md +89 -0
  30. package/.claude/commands/tm/remove-task.md +110 -0
  31. package/.claude/commands/tm/setup-models.md +52 -0
  32. package/.claude/commands/tm/show-task.md +85 -0
  33. package/.claude/commands/tm/smart-workflow.md +58 -0
  34. package/.claude/commands/tm/sync-readme.md +120 -0
  35. package/.claude/commands/tm/tm-main.md +147 -0
  36. package/.claude/commands/tm/to-cancelled.md +58 -0
  37. package/.claude/commands/tm/to-deferred.md +50 -0
  38. package/.claude/commands/tm/to-done.md +47 -0
  39. package/.claude/commands/tm/to-in-progress.md +39 -0
  40. package/.claude/commands/tm/to-pending.md +35 -0
  41. package/.claude/commands/tm/to-review.md +43 -0
  42. package/.claude/commands/tm/update-single-task.md +122 -0
  43. package/.claude/commands/tm/update-task.md +75 -0
  44. package/.claude/commands/tm/update-tasks-from-id.md +111 -0
  45. package/.claude/commands/tm/validate-dependencies.md +72 -0
  46. package/.claude/commands/tm/view-models.md +52 -0
  47. package/.claude/settings.local.json +17 -3
  48. package/.cursor/commands/tm/add-dependency.md +55 -0
  49. package/.cursor/commands/tm/add-subtask.md +76 -0
  50. package/.cursor/commands/tm/add-task.md +78 -0
  51. package/.cursor/commands/tm/analyze-complexity.md +121 -0
  52. package/.cursor/commands/tm/analyze-project.md +97 -0
  53. package/.cursor/commands/tm/auto-implement-tasks.md +97 -0
  54. package/.cursor/commands/tm/command-pipeline.md +77 -0
  55. package/.cursor/commands/tm/complexity-report.md +117 -0
  56. package/.cursor/commands/tm/convert-task-to-subtask.md +71 -0
  57. package/.cursor/commands/tm/expand-all-tasks.md +51 -0
  58. package/.cursor/commands/tm/expand-task.md +49 -0
  59. package/.cursor/commands/tm/fix-dependencies.md +81 -0
  60. package/.cursor/commands/tm/help.md +98 -0
  61. package/.cursor/commands/tm/init-project-quick.md +46 -0
  62. package/.cursor/commands/tm/init-project.md +50 -0
  63. package/.cursor/commands/tm/install-taskmaster.md +117 -0
  64. package/.cursor/commands/tm/learn.md +103 -0
  65. package/.cursor/commands/tm/list-tasks-by-status.md +39 -0
  66. package/.cursor/commands/tm/list-tasks-with-subtasks.md +29 -0
  67. package/.cursor/commands/tm/list-tasks.md +43 -0
  68. package/.cursor/commands/tm/next-task.md +66 -0
  69. package/.cursor/commands/tm/parse-prd-with-research.md +48 -0
  70. package/.cursor/commands/tm/parse-prd.md +49 -0
  71. package/.cursor/commands/tm/project-status.md +64 -0
  72. package/.cursor/commands/tm/quick-install-taskmaster.md +22 -0
  73. package/.cursor/commands/tm/remove-all-subtasks.md +93 -0
  74. package/.cursor/commands/tm/remove-dependency.md +62 -0
  75. package/.cursor/commands/tm/remove-subtask.md +84 -0
  76. package/.cursor/commands/tm/remove-subtasks.md +86 -0
  77. package/.cursor/commands/tm/remove-task.md +107 -0
  78. package/.cursor/commands/tm/setup-models.md +51 -0
  79. package/.cursor/commands/tm/show-task.md +82 -0
  80. package/.cursor/commands/tm/smart-workflow.md +55 -0
  81. package/.cursor/commands/tm/sync-readme.md +117 -0
  82. package/.cursor/commands/tm/tm-main.md +146 -0
  83. package/.cursor/commands/tm/to-cancelled.md +55 -0
  84. package/.cursor/commands/tm/to-deferred.md +47 -0
  85. package/.cursor/commands/tm/to-done.md +44 -0
  86. package/.cursor/commands/tm/to-in-progress.md +36 -0
  87. package/.cursor/commands/tm/to-pending.md +32 -0
  88. package/.cursor/commands/tm/to-review.md +40 -0
  89. package/.cursor/commands/tm/update-single-task.md +119 -0
  90. package/.cursor/commands/tm/update-task.md +72 -0
  91. package/.cursor/commands/tm/update-tasks-from-id.md +108 -0
  92. package/.cursor/commands/tm/validate-dependencies.md +71 -0
  93. package/.cursor/commands/tm/view-models.md +51 -0
  94. package/.cursor/mcp.json +20 -0
  95. package/.cursor/rules/cursor_rules.mdc +53 -0
  96. package/.cursor/rules/self_improve.mdc +72 -0
  97. package/.cursor/rules/taskmaster/dev_workflow.mdc +424 -0
  98. package/.cursor/rules/taskmaster/taskmaster.mdc +573 -0
  99. package/.env.example +12 -0
  100. package/.gemini/commands/tm/add-dependency.toml +58 -0
  101. package/.gemini/commands/tm/add-subtask.toml +79 -0
  102. package/.gemini/commands/tm/add-task.toml +81 -0
  103. package/.gemini/commands/tm/analyze-complexity.toml +124 -0
  104. package/.gemini/commands/tm/analyze-project.toml +100 -0
  105. package/.gemini/commands/tm/auto-implement-tasks.toml +100 -0
  106. package/.gemini/commands/tm/command-pipeline.toml +80 -0
  107. package/.gemini/commands/tm/complexity-report.toml +120 -0
  108. package/.gemini/commands/tm/convert-task-to-subtask.toml +74 -0
  109. package/.gemini/commands/tm/expand-all-tasks.toml +54 -0
  110. package/.gemini/commands/tm/expand-task.toml +52 -0
  111. package/.gemini/commands/tm/fix-dependencies.toml +84 -0
  112. package/.gemini/commands/tm/help.toml +101 -0
  113. package/.gemini/commands/tm/init-project-quick.toml +49 -0
  114. package/.gemini/commands/tm/init-project.toml +53 -0
  115. package/.gemini/commands/tm/install-taskmaster.toml +120 -0
  116. package/.gemini/commands/tm/learn.toml +106 -0
  117. package/.gemini/commands/tm/list-tasks-by-status.toml +42 -0
  118. package/.gemini/commands/tm/list-tasks-with-subtasks.toml +32 -0
  119. package/.gemini/commands/tm/list-tasks.toml +46 -0
  120. package/.gemini/commands/tm/next-task.toml +69 -0
  121. package/.gemini/commands/tm/parse-prd-with-research.toml +51 -0
  122. package/.gemini/commands/tm/parse-prd.toml +52 -0
  123. package/.gemini/commands/tm/project-status.toml +67 -0
  124. package/.gemini/commands/tm/quick-install-taskmaster.toml +25 -0
  125. package/.gemini/commands/tm/remove-all-subtasks.toml +96 -0
  126. package/.gemini/commands/tm/remove-dependency.toml +65 -0
  127. package/.gemini/commands/tm/remove-subtask.toml +87 -0
  128. package/.gemini/commands/tm/remove-subtasks.toml +89 -0
  129. package/.gemini/commands/tm/remove-task.toml +110 -0
  130. package/.gemini/commands/tm/setup-models.toml +54 -0
  131. package/.gemini/commands/tm/show-task.toml +85 -0
  132. package/.gemini/commands/tm/smart-workflow.toml +58 -0
  133. package/.gemini/commands/tm/sync-readme.toml +120 -0
  134. package/.gemini/commands/tm/tm-main.toml +149 -0
  135. package/.gemini/commands/tm/to-cancelled.toml +58 -0
  136. package/.gemini/commands/tm/to-deferred.toml +50 -0
  137. package/.gemini/commands/tm/to-done.toml +47 -0
  138. package/.gemini/commands/tm/to-in-progress.toml +39 -0
  139. package/.gemini/commands/tm/to-pending.toml +35 -0
  140. package/.gemini/commands/tm/to-review.toml +43 -0
  141. package/.gemini/commands/tm/update-single-task.toml +122 -0
  142. package/.gemini/commands/tm/update-task.toml +75 -0
  143. package/.gemini/commands/tm/update-tasks-from-id.toml +111 -0
  144. package/.gemini/commands/tm/validate-dependencies.toml +74 -0
  145. package/.gemini/commands/tm/view-models.toml +54 -0
  146. package/.gemini/settings.json +20 -0
  147. package/.mcp.json +15 -0
  148. package/.taskmaster/CLAUDE.md +435 -0
  149. package/.taskmaster/config.json +44 -0
  150. package/.taskmaster/docs/prd.txt +584 -0
  151. package/.taskmaster/state.json +6 -0
  152. package/.taskmaster/tasks/tasks.json +304 -0
  153. package/.taskmaster/templates/example_prd.txt +47 -0
  154. package/.taskmaster/templates/example_prd_rpg.txt +511 -0
  155. package/CLAUDE.md +4 -138
  156. package/README.md +551 -0
  157. package/coverage/clover.xml +134 -22
  158. package/coverage/coverage-final.json +1 -1
  159. package/coverage/lcov-report/index.html +21 -21
  160. package/coverage/lcov-report/index.js.html +854 -29
  161. package/coverage/lcov.info +246 -39
  162. package/internal-docs/CLAUDE.md +139 -0
  163. package/internal-docs/NPM_DEPLOYMENT_GUIDE.md +203 -0
  164. package/internal-docs/OBSIDIAN_PROJECT_PROMPT.md +302 -0
  165. package/lib/index.d.ts +88 -0
  166. package/lib/index.js +275 -0
  167. package/package.json +11 -3
  168. /package/{DEPLOYMENT.md → internal-docs/DEPLOYMENT.md} +0 -0
  169. /package/{TEST_RESULTS.md → internal-docs/TEST_RESULTS.md} +0 -0
  170. /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 엔티티 디코딩 (&lt; → <)
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.1.0",
4
- "description": "This is personal used function collection.",
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",