@byuckchon-frontend/utils 1.0.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.
@@ -0,0 +1,2 @@
1
+ export * from './src/index'
2
+ export {}
package/dist/index.mjs ADDED
@@ -0,0 +1,155 @@
1
+ const C = () => ({
2
+ formatPhoneNumber: (n) => {
3
+ const t = (n ?? "").replace(/\D/g, "");
4
+ if (/^1[568]\d{7}$/.test(t))
5
+ return t.replace(/^(\d{4})(\d{4})$/, "$1-$2");
6
+ if (t.startsWith("02")) {
7
+ if (t.length === 9)
8
+ return t.replace(/^(\d{2})(\d{3})(\d{4})$/, "$1-$2-$3");
9
+ if (t.length === 10)
10
+ return t.replace(/^(\d{2})(\d{4})(\d{4})$/, "$1-$2-$3");
11
+ }
12
+ if (/^0\d+/.test(t)) {
13
+ if (t.length === 10)
14
+ return t.replace(/^(\d{3})(\d{3})(\d{4})$/, "$1-$2-$3");
15
+ if (t.length === 11)
16
+ return t.replace(/^(\d{3})(\d{4})(\d{4})$/, "$1-$2-$3");
17
+ }
18
+ return n;
19
+ },
20
+ formatBizNumber: (n) => {
21
+ const t = (n ?? "").replace(/\D/g, "");
22
+ return t.length === 10 ? t.replace(/^(\d{3})(\d{2})(\d{5})$/, "$1-$2-$3") : t.length === 13 ? t.replace(/^(\d{6})(\d{7})$/, "$1-$2") : String(n ?? "");
23
+ },
24
+ formatDate: (n) => {
25
+ if (!(n instanceof Date) || isNaN(n.getTime())) return "";
26
+ const t = n.getHours(), r = n.getMinutes(), a = t < 12 ? "오전" : "오후", l = t % 12 === 0 ? 12 : t % 12;
27
+ return `${a} ${l}시 ${r}분`;
28
+ },
29
+ Masker: {
30
+ /**
31
+ * 이름/문자열 마스킹
32
+ * - 1글자: "*"
33
+ * - 2글자: 첫 글자 + "*"
34
+ * - 3글자 이상: 첫 글자 + (중간 전부 "*") + 마지막 글자
35
+ *
36
+ * @param {string} raw 원본 문자열
37
+ * @returns {string} 마스킹된 문자열 (공백/빈문자열 입력 시 빈 문자열)
38
+ * @example
39
+ * Masker.maskName("홍"); // "*"
40
+ * Masker.maskName("홍길"); // "홍*"
41
+ * Masker.maskName("홍길동"); // "홍*동"
42
+ */
43
+ maskName: (n) => {
44
+ const t = (n ?? "").trim(), r = [...t].length;
45
+ if (r <= 0) return "";
46
+ if (r === 1) return "*";
47
+ if (r === 2) {
48
+ const [d] = [...t];
49
+ return `${d}*`;
50
+ }
51
+ const a = [...t], l = a[0], i = a[r - 1], s = "*".repeat(r - 2);
52
+ return `${l}${s}${i}`;
53
+ },
54
+ /**
55
+ * 전화번호 마스킹
56
+ * - 하이픈 포함 입력을 우선 처리(중간 블록을 *로), 하이픈 미포함 시 3-*-4 규칙으로 생성
57
+ * - 최소 8자리 미만은 원본 반환
58
+ *
59
+ * @param {string} input 전화번호(하이픈 포함/미포함 모두 가능)
60
+ * @returns {string} 마스킹된 전화번호
61
+ * @example
62
+ * Masker.maskPhoneNumber("010-1234-5678"); // "010-****-5678"
63
+ * Masker.maskPhoneNumber("0212345678"); // "021****678" (하이픈 미포함 입력)
64
+ */
65
+ maskPhoneNumber: (n) => {
66
+ if (n.includes("-")) {
67
+ const i = n.split("-");
68
+ if (i.length === 3) {
69
+ const [s, d, $] = i;
70
+ return `${s}-${"*".repeat(
71
+ d.replace(/\D/g, "").length || 4
72
+ )}-${$}`;
73
+ }
74
+ if (i.length === 2) {
75
+ const [s, d] = i, $ = d.replace(/\D/g, ""), g = $.slice(-4), h = Math.max(1, $.length - g.length);
76
+ return `${s}-${"*".repeat(h)}${g}`;
77
+ }
78
+ return n;
79
+ }
80
+ const t = n.replace(/\D/g, "");
81
+ if (t.length < 8) return n;
82
+ const r = t.slice(0, 3), a = t.slice(-4), l = Math.max(1, t.length - (r.length + a.length));
83
+ return `${r}${"*".repeat(l)}${a}`;
84
+ }
85
+ },
86
+ commanizeData: (n) => {
87
+ const t = String(n ?? "").trim();
88
+ if (!t) return "";
89
+ const r = t.replace(/,/g, ""), a = r.startsWith("-") ? "-" : "", l = r.replace(/^-/, ""), [i, s] = l.split("."), d = i.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
90
+ return a + d + (s !== void 0 ? `.${s}` : "");
91
+ },
92
+ decommanizeData: (n) => n.replace(/,/g, "")
93
+ }), p = /^[A-Za-z0-9]([A-Za-z0-9_.+-]*[A-Za-z0-9])?@[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?\.[A-Za-z]{2,}$/, x = /^010(\d{4})(\d{4})$/, z = /^(070|02|031|032|033|034|035|036|037|038|039|041|042|043|051|052|053|054|055|061|062|063|064|070|071|072)[0-9]{3,4}[0-9]{4}$/, D = /([0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[1,2][0-9]|3[0,1]))/, b = /^(19[0-9][0-9]|20\d{2})(0[0-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$/, A = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+~`\-={}[\]:;"'<>,.?/\\]).{10,}$/, R = /^d{2}([0]\d|[1][0-2])([0][1-9]|[1-2]\d|[3][0-1])[-]*[1-4]\d{6}$/, B = /^(?:[0-9]{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[1,2][0-9]|3[0,1]))-[1-8][0-9]{6}$/, N = /^https?:\/\/(?:[-\w.])+(?:\.[a-zA-Z]{2,})+(?::\d+)?(?:\/[^\s]*)?$/i, v = /\.(jpg|jpeg|png|gif|pdf|doc|docx|xls|xlsx|ppt|pptx|txt|zip|rar)$/i;
94
+ function P(e) {
95
+ return p.test(e);
96
+ }
97
+ function Z(e) {
98
+ return x.test(e);
99
+ }
100
+ function k(e) {
101
+ return z.test(e);
102
+ }
103
+ function M(e) {
104
+ return D.test(e);
105
+ }
106
+ function j(e) {
107
+ return b.test(e);
108
+ }
109
+ function w(e) {
110
+ return A.test(e);
111
+ }
112
+ function S(e) {
113
+ return R.test(e);
114
+ }
115
+ function y(e) {
116
+ return B.test(e);
117
+ }
118
+ function F(e) {
119
+ const o = e.replace(/-/gi, "").split("").map((u) => parseInt(u, 10));
120
+ if (o.length === 10) {
121
+ if (o.every((c) => c === 0))
122
+ return !1;
123
+ const u = [1, 3, 7, 1, 3, 7, 1, 3, 5];
124
+ let f = 0;
125
+ for (let c = 0; c < 9; c++)
126
+ f += u[c] * o[c];
127
+ const m = (10 - f % 10) % 10;
128
+ return o[9] === m;
129
+ }
130
+ return !1;
131
+ }
132
+ function I(e) {
133
+ return N.test(e);
134
+ }
135
+ function W(e, o = 10) {
136
+ const u = typeof e == "string" ? e : e.name;
137
+ return !(!v.test(u) || e instanceof File && e.size / 1048576 > o);
138
+ }
139
+ const E = {
140
+ email: P,
141
+ phone: Z,
142
+ homePhone: k,
143
+ birth6: M,
144
+ birth8: j,
145
+ password: w,
146
+ juminAfter2020: y,
147
+ juminBefore2020: S,
148
+ corporateRegiNumber: F,
149
+ url: I,
150
+ file: W
151
+ };
152
+ export {
153
+ C as FormatData,
154
+ E as validate
155
+ };
@@ -0,0 +1 @@
1
+ (function(s,m){typeof exports=="object"&&typeof module<"u"?m(exports):typeof define=="function"&&define.amd?define(["exports"],m):(s=typeof globalThis<"u"?globalThis:s||self,m(s.index={}))})(this,(function(s){"use strict";const m=()=>({formatPhoneNumber:n=>{const e=(n??"").replace(/\D/g,"");if(/^1[568]\d{7}$/.test(e))return e.replace(/^(\d{4})(\d{4})$/,"$1-$2");if(e.startsWith("02")){if(e.length===9)return e.replace(/^(\d{2})(\d{3})(\d{4})$/,"$1-$2-$3");if(e.length===10)return e.replace(/^(\d{2})(\d{4})(\d{4})$/,"$1-$2-$3")}if(/^0\d+/.test(e)){if(e.length===10)return e.replace(/^(\d{3})(\d{3})(\d{4})$/,"$1-$2-$3");if(e.length===11)return e.replace(/^(\d{3})(\d{4})(\d{4})$/,"$1-$2-$3")}return n},formatBizNumber:n=>{const e=(n??"").replace(/\D/g,"");return e.length===10?e.replace(/^(\d{3})(\d{2})(\d{5})$/,"$1-$2-$3"):e.length===13?e.replace(/^(\d{6})(\d{7})$/,"$1-$2"):String(n??"")},formatDate:n=>{if(!(n instanceof Date)||isNaN(n.getTime()))return"";const e=n.getHours(),r=n.getMinutes(),i=e<12?"오전":"오후",l=e%12===0?12:e%12;return`${i} ${l}시 ${r}분`},Masker:{maskName:n=>{const e=(n??"").trim(),r=[...e].length;if(r<=0)return"";if(r===1)return"*";if(r===2){const[f]=[...e];return`${f}*`}const i=[...e],l=i[0],a=i[r-1],o="*".repeat(r-2);return`${l}${o}${a}`},maskPhoneNumber:n=>{if(n.includes("-")){const a=n.split("-");if(a.length===3){const[o,f,g]=a;return`${o}-${"*".repeat(f.replace(/\D/g,"").length||4)}-${g}`}if(a.length===2){const[o,f]=a,g=f.replace(/\D/g,""),p=g.slice(-4),E=Math.max(1,g.length-p.length);return`${o}-${"*".repeat(E)}${p}`}return n}const e=n.replace(/\D/g,"");if(e.length<8)return n;const r=e.slice(0,3),i=e.slice(-4),l=Math.max(1,e.length-(r.length+i.length));return`${r}${"*".repeat(l)}${i}`}},commanizeData:n=>{const e=String(n??"").trim();if(!e)return"";const r=e.replace(/,/g,""),i=r.startsWith("-")?"-":"",l=r.replace(/^-/,""),[a,o]=l.split("."),f=a.replace(/\B(?=(\d{3})+(?!\d))/g,",");return i+f+(o!==void 0?`.${o}`:"")},decommanizeData:n=>n.replace(/,/g,"")}),x=/^[A-Za-z0-9]([A-Za-z0-9_.+-]*[A-Za-z0-9])?@[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?\.[A-Za-z]{2,}$/,z=/^010(\d{4})(\d{4})$/,b=/^(070|02|031|032|033|034|035|036|037|038|039|041|042|043|051|052|053|054|055|061|062|063|064|070|071|072)[0-9]{3,4}[0-9]{4}$/,D=/([0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[1,2][0-9]|3[0,1]))/,A=/^(19[0-9][0-9]|20\d{2})(0[0-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$/,v=/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+~`\-={}[\]:;"'<>,.?/\\]).{10,}$/,R=/^d{2}([0]\d|[1][0-2])([0][1-9]|[1-2]\d|[3][0-1])[-]*[1-4]\d{6}$/,B=/^(?:[0-9]{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[1,2][0-9]|3[0,1]))-[1-8][0-9]{6}$/,N=/^https?:\/\/(?:[-\w.])+(?:\.[a-zA-Z]{2,})+(?::\d+)?(?:\/[^\s]*)?$/i,P=/\.(jpg|jpeg|png|gif|pdf|doc|docx|xls|xlsx|ppt|pptx|txt|zip|rar)$/i;function M(t){return x.test(t)}function Z(t){return z.test(t)}function j(t){return b.test(t)}function k(t){return D.test(t)}function y(t){return A.test(t)}function S(t){return v.test(t)}function w(t){return R.test(t)}function F(t){return B.test(t)}function T(t){const c=t.replace(/-/gi,"").split("").map(u=>parseInt(u,10));if(c.length===10){if(c.every(d=>d===0))return!1;const u=[1,3,7,1,3,7,1,3,5];let $=0;for(let d=0;d<9;d++)$+=u[d]*c[d];const h=(10-$%10)%10;return c[9]===h}return!1}function I(t){return N.test(t)}function W(t,c=10){const u=typeof t=="string"?t:t.name;return!(!P.test(u)||t instanceof File&&t.size/1048576>c)}const C={email:M,phone:Z,homePhone:j,birth6:k,birth8:y,password:S,juminAfter2020:F,juminBefore2020:w,corporateRegiNumber:T,url:I,file:W};s.FormatData=m,s.validate=C,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 유틸 팩토리: 전화번호/사업자번호/날짜 포매팅, 마스킹, 숫자 콤마 처리 등 제공
3
+ *
4
+ * @returns 객체 형태의 포매터/마스커 유틸들
5
+ * @example
6
+ * const { formatPhoneNumber, Masker, commanizeData } = FormatData();
7
+ * formatPhoneNumber("01012345678"); // "010-1234-5678"
8
+ * Masker.maskName("홍길동"); // "홍*동"
9
+ * commanizeData(1234567.89); // "1,234,567.89"
10
+ */
11
+ export declare const FormatData: () => {
12
+ formatPhoneNumber: (value: string) => string;
13
+ formatBizNumber: (value: string) => string;
14
+ formatDate: (value: Date) => string;
15
+ Masker: {
16
+ /**
17
+ * 이름/문자열 마스킹
18
+ * - 1글자: "*"
19
+ * - 2글자: 첫 글자 + "*"
20
+ * - 3글자 이상: 첫 글자 + (중간 전부 "*") + 마지막 글자
21
+ *
22
+ * @param {string} raw 원본 문자열
23
+ * @returns {string} 마스킹된 문자열 (공백/빈문자열 입력 시 빈 문자열)
24
+ * @example
25
+ * Masker.maskName("홍"); // "*"
26
+ * Masker.maskName("홍길"); // "홍*"
27
+ * Masker.maskName("홍길동"); // "홍*동"
28
+ */
29
+ maskName: (raw: string) => string;
30
+ /**
31
+ * 전화번호 마스킹
32
+ * - 하이픈 포함 입력을 우선 처리(중간 블록을 *로), 하이픈 미포함 시 3-*-4 규칙으로 생성
33
+ * - 최소 8자리 미만은 원본 반환
34
+ *
35
+ * @param {string} input 전화번호(하이픈 포함/미포함 모두 가능)
36
+ * @returns {string} 마스킹된 전화번호
37
+ * @example
38
+ * Masker.maskPhoneNumber("010-1234-5678"); // "010-****-5678"
39
+ * Masker.maskPhoneNumber("0212345678"); // "021****678" (하이픈 미포함 입력)
40
+ */
41
+ maskPhoneNumber: (input: string) => string;
42
+ };
43
+ commanizeData: (value: string | number) => string;
44
+ decommanizeData: (value: string) => string;
45
+ };
@@ -0,0 +1,2 @@
1
+ export * from './formatData';
2
+ export * from './validate';
@@ -0,0 +1,114 @@
1
+ /**
2
+ * 이메일 형식을 검증합니다.
3
+ * @param email - 검증할 이메일 문자열
4
+ * @returns 유효한 이메일 형식이면 true, 아니면 false
5
+ * @example
6
+ * validateEmail('test@example.com') // true
7
+ * validateEmail('invalid-email') // false
8
+ */
9
+ declare function validateEmail(email: string): boolean;
10
+ /**
11
+ * 휴대폰 번호 형식을 검증합니다. (010으로 시작하는 11자리 숫자, 하이픈 없음)
12
+ * @param phone - 검증할 휴대폰 번호 문자열
13
+ * @returns 유효한 휴대폰 번호 형식이면 true, 아니면 false
14
+ * @example
15
+ * validatePhone('01012345678') // true
16
+ * validatePhone('010-1234-5678') // false
17
+ */
18
+ declare function validatePhone(phone: string): boolean;
19
+ /**
20
+ * 집 전화번호 형식을 검증합니다. (한국의 모든 지역번호)
21
+ * @param homePhone - 검증할 집 전화번호 문자열
22
+ * @returns 유효한 집 전화번호 형식이면 true, 아니면 false
23
+ * @example
24
+ * validateHomePhone('0212345678') // true
25
+ * validateHomePhone('03112345678') // true
26
+ */
27
+ declare function validateHomePhone(homePhone: string): boolean;
28
+ /**
29
+ * 생년월일 6자리 형식을 검증합니다. (YYMMDD)
30
+ * @param birth - 검증할 생년월일 문자열
31
+ * @returns 유효한 생년월일 형식이면 true, 아니면 false
32
+ * @example
33
+ * validateBirth6('990101') // true
34
+ * validateBirth6('001231') // true
35
+ */
36
+ declare function validateBirth6(birth: string): boolean;
37
+ /**
38
+ * 생년월일 8자리 형식을 검증합니다. (YYYYMMDD)
39
+ * @param birth - 검증할 생년월일 문자열
40
+ * @returns 유효한 생년월일 형식이면 true, 아니면 false
41
+ * @example
42
+ * validateBirth8('19990101') // true
43
+ * validateBirth8('20001231') // true
44
+ */
45
+ declare function validateBirth8(birth: string): boolean;
46
+ /**
47
+ * 비밀번호 형식을 검증합니다. (영문 대소문자 + 숫자 + 특수문자 각 1회 이상, 10자리 이상)
48
+ * @param password - 검증할 비밀번호 문자열
49
+ * @returns 유효한 비밀번호 형식이면 true, 아니면 false
50
+ * @example
51
+ * validatePassword('MyPassword123!') // true
52
+ * validatePassword('weak') // false
53
+ */
54
+ declare function validatePassword(password: string): boolean;
55
+ /**
56
+ * 주민등록번호 형식을 검증합니다. (2020년 이전 출생자)
57
+ * @param jumin - 검증할 주민등록번호 문자열
58
+ * @returns 유효한 주민등록번호 형식이면 true, 아니면 false
59
+ * @example
60
+ * validateJuminBefore2020('990101-1234567') // true
61
+ */
62
+ declare function validateJuminBefore2020(jumin: string): boolean;
63
+ /**
64
+ * 주민등록번호 형식을 검증합니다. (2020년 이후 출생자 포함)
65
+ * @param jumin - 검증할 주민등록번호 문자열
66
+ * @returns 유효한 주민등록번호 형식이면 true, 아니면 false
67
+ * @example
68
+ * validateJuminAfter2020('200101-5234567') // true
69
+ */
70
+ declare function validateJuminAfter2020(jumin: string): boolean;
71
+ /**
72
+ * 사업자등록번호를 검증합니다.
73
+ * @param number - 검증할 사업자등록번호 문자열 (하이픈 포함 가능)
74
+ * @returns 유효한 사업자등록번호이면 true, 아니면 false
75
+ * @example
76
+ * validateCorporateRegiNumber('123-45-67890') // 유효성 검사 후 결과 반환
77
+ * validateCorporateRegiNumber('1234567890') // 유효성 검사 후 결과 반환
78
+ */
79
+ declare function validateCorporateRegiNumber(number: string): boolean;
80
+ /**
81
+ * URL 형식을 검증합니다.
82
+ * @param url - 검증할 URL 문자열
83
+ * @returns 유효한 URL 형식이면 true, 아니면 false
84
+ * @example
85
+ * validateUrl('https://example.com') // true
86
+ * validateUrl('http://example.com/path') // true
87
+ * validateUrl('not-a-url') // false
88
+ */
89
+ declare function validateUrl(url: string): boolean;
90
+ /**
91
+ * 파일의 확장자와 크기를 검증합니다.
92
+ * 지원 확장자: jpg, jpeg, png, gif, pdf, doc, docx, xls, xlsx, ppt, pptx, txt, zip, rar
93
+ * @param file - 검증할 File 객체 또는 파일명 문자열
94
+ * @param maxSizeInMB - 최대 파일 크기 (MB 단위, 기본값: 10MB)
95
+ * @returns 유효한 파일이면 true, 아니면 false
96
+ * @example
97
+ * validateFile(new File(['content'], 'document.pdf'), 5) // true (5MB 이하)
98
+ * validateFile('image.jpg', 10) // true (확장자 검증만)
99
+ */
100
+ declare function validateFile(file: File | string, maxSizeInMB?: number): boolean;
101
+ export declare const validate: {
102
+ email: typeof validateEmail;
103
+ phone: typeof validatePhone;
104
+ homePhone: typeof validateHomePhone;
105
+ birth6: typeof validateBirth6;
106
+ birth8: typeof validateBirth8;
107
+ password: typeof validatePassword;
108
+ juminAfter2020: typeof validateJuminAfter2020;
109
+ juminBefore2020: typeof validateJuminBefore2020;
110
+ corporateRegiNumber: typeof validateCorporateRegiNumber;
111
+ url: typeof validateUrl;
112
+ file: typeof validateFile;
113
+ };
114
+ export {};
@@ -0,0 +1,2 @@
1
+ declare const _default: import('vite').UserConfig;
2
+ export default _default;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@byuckchon-frontend/utils",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "dist/index.umd.js",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.mjs",
16
+ "require": "./dist/index.umd.js",
17
+ "default": "./dist/index.mjs"
18
+ }
19
+ },
20
+ "devDependencies": {
21
+ "vite": "^7.1.2",
22
+ "vite-plugin-dts": "^4.5.4",
23
+ "vite-plugin-libcss": "^1.1.2",
24
+ "vite-css-modules": "^1.10.0",
25
+ "typescript": "~5.8.3"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/RevolutionaryWarrior/byuckchon-frontend-packages.git"
30
+ },
31
+ "keywords": [
32
+ "javascript",
33
+ "typescript",
34
+ "utils",
35
+ "common",
36
+ "frontend"
37
+ ],
38
+ "author": "Byuckchon Frontend Team",
39
+ "license": "MIT",
40
+ "bugs": {
41
+ "url": "https://github.com/RevolutionaryWarrior/byuckchon-frontend-packages/issues"
42
+ },
43
+ "homepage": "https://github.com/RevolutionaryWarrior/byuckchon-frontend-packages#readme",
44
+ "description": "",
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "scripts": {
49
+ "build": "vite build",
50
+ "lint": "eslint src --ext ts,tsx",
51
+ "build:watch": "vite build --watch"
52
+ }
53
+ }