@fuzionx/framework 0.1.39 → 0.1.42

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 (80) hide show
  1. package/README.md +501 -501
  2. package/bin/fx.js +12 -12
  3. package/cli/db-sync.js +100 -99
  4. package/cli/index.js +494 -493
  5. package/cli/templates/make/app/controllers/HomeController.js +14 -14
  6. package/cli/templates/make/app/routes/api.js +7 -7
  7. package/cli/templates/make/app/routes/web.js +5 -5
  8. package/cli/templates/make/app/views/default/errors/404.html +11 -11
  9. package/cli/templates/make/app/views/default/errors/500.html +14 -14
  10. package/cli/templates/make/app/views/default/layouts/main.html +22 -22
  11. package/cli/templates/make/app/views/default/pages/home.html +11 -11
  12. package/cli/templates/make/controller.js.tpl +40 -40
  13. package/cli/templates/make/event.js.tpl +8 -8
  14. package/cli/templates/make/job.js.tpl +10 -10
  15. package/cli/templates/make/middleware.js.tpl +10 -10
  16. package/cli/templates/make/model.js.tpl +15 -15
  17. package/cli/templates/make/service.js.tpl +15 -15
  18. package/cli/templates/make/task.js.tpl +15 -15
  19. package/cli/templates/make/test.js.tpl +7 -7
  20. package/cli/templates/make/worker.js.tpl +14 -14
  21. package/cli/templates/make/ws.js.tpl +18 -18
  22. package/index.js +67 -67
  23. package/lib/core/AppError.js +46 -46
  24. package/lib/core/Application.js +1006 -1006
  25. package/lib/core/AutoLoader.js +227 -226
  26. package/lib/core/Base.js +64 -64
  27. package/lib/core/Config.js +228 -228
  28. package/lib/core/Context.js +484 -484
  29. package/lib/database/ConnectionManager.js +208 -208
  30. package/lib/database/MariaModel.js +29 -29
  31. package/lib/database/Model.js +247 -247
  32. package/lib/database/ModelRegistry.js +72 -72
  33. package/lib/database/MongoModel.js +232 -232
  34. package/lib/database/Pagination.js +37 -37
  35. package/lib/database/PostgreModel.js +29 -29
  36. package/lib/database/QueryBuilder.js +172 -172
  37. package/lib/database/SQLiteModel.js +27 -27
  38. package/lib/database/SqlModel.js +257 -257
  39. package/lib/database/SqlQueryBuilder.js +332 -332
  40. package/lib/helpers/CryptoHelper.js +48 -48
  41. package/lib/helpers/FileHelper.js +61 -61
  42. package/lib/helpers/HashHelper.js +39 -39
  43. package/lib/helpers/I18nHelper.js +174 -174
  44. package/lib/helpers/Logger.js +108 -108
  45. package/lib/helpers/MediaHelper.js +84 -84
  46. package/lib/http/Controller.js +34 -34
  47. package/lib/http/ErrorHandler.js +136 -136
  48. package/lib/http/Middleware.js +43 -43
  49. package/lib/http/Router.js +109 -109
  50. package/lib/http/Validation.js +125 -125
  51. package/lib/middleware/apiAuth.js +79 -79
  52. package/lib/middleware/auth.js +42 -42
  53. package/lib/middleware/bodyParser.js +19 -19
  54. package/lib/middleware/cors.js +47 -47
  55. package/lib/middleware/csrf.js +32 -32
  56. package/lib/middleware/index.js +13 -13
  57. package/lib/middleware/session.js +27 -27
  58. package/lib/middleware/theme.js +20 -20
  59. package/lib/realtime/RoomManager.js +85 -85
  60. package/lib/realtime/WsHandler.js +107 -107
  61. package/lib/schedule/Job.js +38 -38
  62. package/lib/schedule/Queue.js +103 -103
  63. package/lib/schedule/Scheduler.js +171 -171
  64. package/lib/schedule/Task.js +39 -39
  65. package/lib/schedule/WorkerPool.js +225 -225
  66. package/lib/services/EventBus.js +94 -94
  67. package/lib/services/Service.js +261 -261
  68. package/lib/services/Storage.js +112 -112
  69. package/lib/utilities/ArrUtil.js +112 -112
  70. package/lib/utilities/DateUtil.js +98 -98
  71. package/lib/utilities/FunctionUtil.js +119 -119
  72. package/lib/utilities/NumUtil.js +75 -75
  73. package/lib/utilities/ObjectUtil.js +170 -170
  74. package/lib/utilities/PaginationUtil.js +81 -81
  75. package/lib/utilities/StrUtil.js +105 -105
  76. package/lib/utilities/index.js +18 -18
  77. package/lib/view/OpenAPI.js +231 -231
  78. package/lib/view/View.js +83 -83
  79. package/package.json +2 -2
  80. package/testing/index.js +232 -232
@@ -1,170 +1,170 @@
1
- /**
2
- * ObjectUtil — 객체 순수 유틸리티
3
- */
4
-
5
- /**
6
- * Deep clone (structuredClone 폴백)
7
- * @param {*} obj
8
- * @returns {*}
9
- */
10
- export function deepClone(obj) {
11
- if (typeof structuredClone === 'function') return structuredClone(obj);
12
- return JSON.parse(JSON.stringify(obj));
13
- }
14
-
15
- /**
16
- * Deep merge — 비파괴 병합
17
- * @param {object} target
18
- * @param {...object} sources
19
- * @returns {object}
20
- */
21
- export function deepMerge(target, ...sources) {
22
- const result = { ...target };
23
- for (const source of sources) {
24
- if (!source) continue;
25
- for (const key of Object.keys(source)) {
26
- const sv = source[key];
27
- const tv = result[key];
28
- if (isPlainObject(sv) && isPlainObject(tv)) {
29
- result[key] = deepMerge(tv, sv);
30
- } else {
31
- result[key] = sv;
32
- }
33
- }
34
- }
35
- return result;
36
- }
37
-
38
- /**
39
- * 특정 키만 선택
40
- * @param {object} obj
41
- * @param {string[]} keys
42
- * @returns {object}
43
- *
44
- * @example pick({ a:1, b:2, c:3 }, ['a', 'c']) → { a:1, c:3 }
45
- */
46
- export function pick(obj, keys) {
47
- const result = {};
48
- for (const key of keys) {
49
- if (key in obj) result[key] = obj[key];
50
- }
51
- return result;
52
- }
53
-
54
- /**
55
- * 특정 키 제외
56
- * @param {object} obj
57
- * @param {string[]} keys
58
- * @returns {object}
59
- */
60
- export function omit(obj, keys) {
61
- const keySet = new Set(keys);
62
- const result = {};
63
- for (const key of Object.keys(obj)) {
64
- if (!keySet.has(key)) result[key] = obj[key];
65
- }
66
- return result;
67
- }
68
-
69
- /**
70
- * Flatten — 중첩 객체를 dot notation으로 평탄화
71
- * @param {object} obj
72
- * @param {string} [prefix='']
73
- * @returns {object}
74
- *
75
- * @example flatten({ a: { b: 1, c: { d: 2 } } }) → { 'a.b': 1, 'a.c.d': 2 }
76
- */
77
- export function flatten(obj, prefix = '') {
78
- const result = {};
79
- for (const key of Object.keys(obj)) {
80
- const path = prefix ? `${prefix}.${key}` : key;
81
- const val = obj[key];
82
- if (isPlainObject(val)) {
83
- Object.assign(result, flatten(val, path));
84
- } else {
85
- result[path] = val;
86
- }
87
- }
88
- return result;
89
- }
90
-
91
- /**
92
- * Unflatten — dot notation → 중첩 객체
93
- * @param {object} obj
94
- * @returns {object}
95
- */
96
- export function unflatten(obj) {
97
- const result = {};
98
- for (const [key, val] of Object.entries(obj)) {
99
- const parts = key.split('.');
100
- let current = result;
101
- for (let i = 0; i < parts.length - 1; i++) {
102
- if (!current[parts[i]] || typeof current[parts[i]] !== 'object') {
103
- current[parts[i]] = {};
104
- }
105
- current = current[parts[i]];
106
- }
107
- current[parts[parts.length - 1]] = val;
108
- }
109
- return result;
110
- }
111
-
112
- /**
113
- * 객체가 비어있는지 확인
114
- * @param {*} obj
115
- * @returns {boolean}
116
- */
117
- export function isEmpty(obj) {
118
- if (!obj) return true;
119
- return Object.keys(obj).length === 0;
120
- }
121
-
122
- /**
123
- * dot notation으로 값 접근
124
- * @param {object} obj
125
- * @param {string} path
126
- * @param {*} [defaultValue]
127
- * @returns {*}
128
- *
129
- * @example get({ a: { b: { c: 42 } } }, 'a.b.c') → 42
130
- */
131
- export function get(obj, path, defaultValue) {
132
- const parts = path.split('.');
133
- let current = obj;
134
- for (const part of parts) {
135
- if (current == null || typeof current !== 'object') return defaultValue;
136
- current = current[part];
137
- }
138
- return current !== undefined ? current : defaultValue;
139
- }
140
-
141
- /**
142
- * dot notation으로 값 설정 (비파괴 — 새 객체 반환)
143
- * @param {object} obj
144
- * @param {string} path
145
- * @param {*} value
146
- * @returns {object}
147
- */
148
- export function set(obj, path, value) {
149
- const result = deepClone(obj);
150
- const parts = path.split('.');
151
- let current = result;
152
- for (let i = 0; i < parts.length - 1; i++) {
153
- if (!current[parts[i]] || typeof current[parts[i]] !== 'object') {
154
- current[parts[i]] = {};
155
- }
156
- current = current[parts[i]];
157
- }
158
- current[parts[parts.length - 1]] = value;
159
- return result;
160
- }
161
-
162
- /**
163
- * 순수 객체인지 확인 (Array, Date 등 제외)
164
- * @param {*} val
165
- * @returns {boolean}
166
- */
167
- export function isPlainObject(val) {
168
- return val !== null && typeof val === 'object' && !Array.isArray(val)
169
- && Object.getPrototypeOf(val) === Object.prototype;
170
- }
1
+ /**
2
+ * ObjectUtil — 객체 순수 유틸리티
3
+ */
4
+
5
+ /**
6
+ * Deep clone (structuredClone 폴백)
7
+ * @param {*} obj
8
+ * @returns {*}
9
+ */
10
+ export function deepClone(obj) {
11
+ if (typeof structuredClone === 'function') return structuredClone(obj);
12
+ return JSON.parse(JSON.stringify(obj));
13
+ }
14
+
15
+ /**
16
+ * Deep merge — 비파괴 병합
17
+ * @param {object} target
18
+ * @param {...object} sources
19
+ * @returns {object}
20
+ */
21
+ export function deepMerge(target, ...sources) {
22
+ const result = { ...target };
23
+ for (const source of sources) {
24
+ if (!source) continue;
25
+ for (const key of Object.keys(source)) {
26
+ const sv = source[key];
27
+ const tv = result[key];
28
+ if (isPlainObject(sv) && isPlainObject(tv)) {
29
+ result[key] = deepMerge(tv, sv);
30
+ } else {
31
+ result[key] = sv;
32
+ }
33
+ }
34
+ }
35
+ return result;
36
+ }
37
+
38
+ /**
39
+ * 특정 키만 선택
40
+ * @param {object} obj
41
+ * @param {string[]} keys
42
+ * @returns {object}
43
+ *
44
+ * @example pick({ a:1, b:2, c:3 }, ['a', 'c']) → { a:1, c:3 }
45
+ */
46
+ export function pick(obj, keys) {
47
+ const result = {};
48
+ for (const key of keys) {
49
+ if (key in obj) result[key] = obj[key];
50
+ }
51
+ return result;
52
+ }
53
+
54
+ /**
55
+ * 특정 키 제외
56
+ * @param {object} obj
57
+ * @param {string[]} keys
58
+ * @returns {object}
59
+ */
60
+ export function omit(obj, keys) {
61
+ const keySet = new Set(keys);
62
+ const result = {};
63
+ for (const key of Object.keys(obj)) {
64
+ if (!keySet.has(key)) result[key] = obj[key];
65
+ }
66
+ return result;
67
+ }
68
+
69
+ /**
70
+ * Flatten — 중첩 객체를 dot notation으로 평탄화
71
+ * @param {object} obj
72
+ * @param {string} [prefix='']
73
+ * @returns {object}
74
+ *
75
+ * @example flatten({ a: { b: 1, c: { d: 2 } } }) → { 'a.b': 1, 'a.c.d': 2 }
76
+ */
77
+ export function flatten(obj, prefix = '') {
78
+ const result = {};
79
+ for (const key of Object.keys(obj)) {
80
+ const path = prefix ? `${prefix}.${key}` : key;
81
+ const val = obj[key];
82
+ if (isPlainObject(val)) {
83
+ Object.assign(result, flatten(val, path));
84
+ } else {
85
+ result[path] = val;
86
+ }
87
+ }
88
+ return result;
89
+ }
90
+
91
+ /**
92
+ * Unflatten — dot notation → 중첩 객체
93
+ * @param {object} obj
94
+ * @returns {object}
95
+ */
96
+ export function unflatten(obj) {
97
+ const result = {};
98
+ for (const [key, val] of Object.entries(obj)) {
99
+ const parts = key.split('.');
100
+ let current = result;
101
+ for (let i = 0; i < parts.length - 1; i++) {
102
+ if (!current[parts[i]] || typeof current[parts[i]] !== 'object') {
103
+ current[parts[i]] = {};
104
+ }
105
+ current = current[parts[i]];
106
+ }
107
+ current[parts[parts.length - 1]] = val;
108
+ }
109
+ return result;
110
+ }
111
+
112
+ /**
113
+ * 객체가 비어있는지 확인
114
+ * @param {*} obj
115
+ * @returns {boolean}
116
+ */
117
+ export function isEmpty(obj) {
118
+ if (!obj) return true;
119
+ return Object.keys(obj).length === 0;
120
+ }
121
+
122
+ /**
123
+ * dot notation으로 값 접근
124
+ * @param {object} obj
125
+ * @param {string} path
126
+ * @param {*} [defaultValue]
127
+ * @returns {*}
128
+ *
129
+ * @example get({ a: { b: { c: 42 } } }, 'a.b.c') → 42
130
+ */
131
+ export function get(obj, path, defaultValue) {
132
+ const parts = path.split('.');
133
+ let current = obj;
134
+ for (const part of parts) {
135
+ if (current == null || typeof current !== 'object') return defaultValue;
136
+ current = current[part];
137
+ }
138
+ return current !== undefined ? current : defaultValue;
139
+ }
140
+
141
+ /**
142
+ * dot notation으로 값 설정 (비파괴 — 새 객체 반환)
143
+ * @param {object} obj
144
+ * @param {string} path
145
+ * @param {*} value
146
+ * @returns {object}
147
+ */
148
+ export function set(obj, path, value) {
149
+ const result = deepClone(obj);
150
+ const parts = path.split('.');
151
+ let current = result;
152
+ for (let i = 0; i < parts.length - 1; i++) {
153
+ if (!current[parts[i]] || typeof current[parts[i]] !== 'object') {
154
+ current[parts[i]] = {};
155
+ }
156
+ current = current[parts[i]];
157
+ }
158
+ current[parts[parts.length - 1]] = value;
159
+ return result;
160
+ }
161
+
162
+ /**
163
+ * 순수 객체인지 확인 (Array, Date 등 제외)
164
+ * @param {*} val
165
+ * @returns {boolean}
166
+ */
167
+ export function isPlainObject(val) {
168
+ return val !== null && typeof val === 'object' && !Array.isArray(val)
169
+ && Object.getPrototypeOf(val) === Object.prototype;
170
+ }
@@ -1,81 +1,81 @@
1
- /**
2
- * PaginationUtil — 페이지네이션 순수 유틸리티
3
- *
4
- * Stateless 함수들. DB Pagination 클래스와 달리
5
- * 인메모리 배열/수동 메타 계산에 사용.
6
- *
7
- * @see lib/database/Pagination.js — DB 쿼리 결과용
8
- */
9
-
10
- /**
11
- * 메타 계산 — total, page, perPage로 페이지네이션 정보 생성
12
- * @param {number} total - 전체 레코드 수
13
- * @param {number} page - 현재 페이지 (1-based)
14
- * @param {number} perPage - 페이지당 수
15
- * @returns {{ total, page, perPage, lastPage, hasMore, from, to }}
16
- */
17
- export function buildMeta(total, page = 1, perPage = 20) {
18
- const lastPage = Math.ceil(total / perPage) || 1;
19
- const from = (page - 1) * perPage + 1;
20
- const to = Math.min(page * perPage, total);
21
- return {
22
- total,
23
- page,
24
- perPage,
25
- lastPage,
26
- hasMore: page < lastPage,
27
- from: total > 0 ? from : 0,
28
- to: total > 0 ? to : 0,
29
- };
30
- }
31
-
32
- /**
33
- * 인메모리 배열 페이지네이션
34
- * @param {Array} items - 전체 배열
35
- * @param {number} page - 현재 페이지 (1-based)
36
- * @param {number} perPage - 페이지당 수
37
- * @returns {{ data: Array, meta: object }}
38
- */
39
- export function paginate(items, page = 1, perPage = 20) {
40
- const total = items.length;
41
- const start = (page - 1) * perPage;
42
- const data = items.slice(start, start + perPage);
43
- return { data, meta: buildMeta(total, page, perPage) };
44
- }
45
-
46
- /**
47
- * offset/limit 계산
48
- * @param {number} page
49
- * @param {number} perPage
50
- * @returns {{ offset: number, limit: number }}
51
- */
52
- export function offsetLimit(page = 1, perPage = 20) {
53
- return {
54
- offset: (page - 1) * perPage,
55
- limit: perPage,
56
- };
57
- }
58
-
59
- /**
60
- * 페이지 번호 배열 생성 (페이지 네비게이션 UI용)
61
- * @param {number} current - 현재 페이지
62
- * @param {number} last - 마지막 페이지
63
- * @param {number} [delta=2] - 현재 페이지 앞뒤로 표시할 수
64
- * @returns {Array<number|'...'>}
65
- *
66
- * @example
67
- * pageNumbers(5, 20, 2) → [1, '...', 3, 4, 5, 6, 7, '...', 20]
68
- */
69
- export function pageNumbers(current, last, delta = 2) {
70
- const pages = [];
71
- const rangeStart = Math.max(2, current - delta);
72
- const rangeEnd = Math.min(last - 1, current + delta);
73
-
74
- pages.push(1);
75
- if (rangeStart > 2) pages.push('...');
76
- for (let i = rangeStart; i <= rangeEnd; i++) pages.push(i);
77
- if (rangeEnd < last - 1) pages.push('...');
78
- if (last > 1) pages.push(last);
79
-
80
- return pages;
81
- }
1
+ /**
2
+ * PaginationUtil — 페이지네이션 순수 유틸리티
3
+ *
4
+ * Stateless 함수들. DB Pagination 클래스와 달리
5
+ * 인메모리 배열/수동 메타 계산에 사용.
6
+ *
7
+ * @see lib/database/Pagination.js — DB 쿼리 결과용
8
+ */
9
+
10
+ /**
11
+ * 메타 계산 — total, page, perPage로 페이지네이션 정보 생성
12
+ * @param {number} total - 전체 레코드 수
13
+ * @param {number} page - 현재 페이지 (1-based)
14
+ * @param {number} perPage - 페이지당 수
15
+ * @returns {{ total, page, perPage, lastPage, hasMore, from, to }}
16
+ */
17
+ export function buildMeta(total, page = 1, perPage = 20) {
18
+ const lastPage = Math.ceil(total / perPage) || 1;
19
+ const from = (page - 1) * perPage + 1;
20
+ const to = Math.min(page * perPage, total);
21
+ return {
22
+ total,
23
+ page,
24
+ perPage,
25
+ lastPage,
26
+ hasMore: page < lastPage,
27
+ from: total > 0 ? from : 0,
28
+ to: total > 0 ? to : 0,
29
+ };
30
+ }
31
+
32
+ /**
33
+ * 인메모리 배열 페이지네이션
34
+ * @param {Array} items - 전체 배열
35
+ * @param {number} page - 현재 페이지 (1-based)
36
+ * @param {number} perPage - 페이지당 수
37
+ * @returns {{ data: Array, meta: object }}
38
+ */
39
+ export function paginate(items, page = 1, perPage = 20) {
40
+ const total = items.length;
41
+ const start = (page - 1) * perPage;
42
+ const data = items.slice(start, start + perPage);
43
+ return { data, meta: buildMeta(total, page, perPage) };
44
+ }
45
+
46
+ /**
47
+ * offset/limit 계산
48
+ * @param {number} page
49
+ * @param {number} perPage
50
+ * @returns {{ offset: number, limit: number }}
51
+ */
52
+ export function offsetLimit(page = 1, perPage = 20) {
53
+ return {
54
+ offset: (page - 1) * perPage,
55
+ limit: perPage,
56
+ };
57
+ }
58
+
59
+ /**
60
+ * 페이지 번호 배열 생성 (페이지 네비게이션 UI용)
61
+ * @param {number} current - 현재 페이지
62
+ * @param {number} last - 마지막 페이지
63
+ * @param {number} [delta=2] - 현재 페이지 앞뒤로 표시할 수
64
+ * @returns {Array<number|'...'>}
65
+ *
66
+ * @example
67
+ * pageNumbers(5, 20, 2) → [1, '...', 3, 4, 5, 6, 7, '...', 20]
68
+ */
69
+ export function pageNumbers(current, last, delta = 2) {
70
+ const pages = [];
71
+ const rangeStart = Math.max(2, current - delta);
72
+ const rangeEnd = Math.min(last - 1, current + delta);
73
+
74
+ pages.push(1);
75
+ if (rangeStart > 2) pages.push('...');
76
+ for (let i = rangeStart; i <= rangeEnd; i++) pages.push(i);
77
+ if (rangeEnd < last - 1) pages.push('...');
78
+ if (last > 1) pages.push(last);
79
+
80
+ return pages;
81
+ }