@fuzionx/framework 0.1.42 → 0.1.44

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 -100
  4. package/cli/index.js +494 -494
  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 -227
  26. package/lib/core/Base.js +64 -64
  27. package/lib/core/Config.js +331 -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,208 +1,208 @@
1
- /**
2
- * ConnectionManager — DB 연결 관리 싱글톤
3
- *
4
- * 드라이버별 연결 생성/캐싱/종료.
5
- * SQLite (기본), MariaDB, PostgreSQL, MongoDB 지원.
6
- *
7
- * @see docs/framework/02-database-orm.md
8
- * @see docs/framework/17-config.md
9
- */
10
- import { createRequire } from 'node:module';
11
-
12
- const _require = createRequire(import.meta.url);
13
-
14
- /**
15
- * optional dependency 안전 로드
16
- * @private
17
- */
18
- function tryRequire(name) {
19
- try { return _require(name); } catch { return null; }
20
- }
21
-
22
- export default class ConnectionManager {
23
- constructor() {
24
- /** @type {Map<string, object>} name → connection */
25
- this._connections = new Map();
26
- /** @type {Map<string, object>} name → config */
27
- this._configs = new Map();
28
- /** @type {string} default connection name */
29
- this._default = 'main';
30
- }
31
-
32
- /**
33
- * 설정으로 초기화
34
- * @param {object} dbConfig - fuzionx.yaml database 섹션
35
- */
36
- configure(dbConfig) {
37
- this._default = dbConfig.default || 'main';
38
- const connections = dbConfig.connections || {};
39
- for (const [name, cfg] of Object.entries(connections)) {
40
- this._configs.set(name, cfg);
41
- }
42
- }
43
-
44
- /**
45
- * 연결 가져오기 (lazy 생성)
46
- * @param {string} [name] - connection name (default: _default)
47
- * @returns {object} driver connection instance
48
- */
49
- get(name) {
50
- const connName = name || this._default;
51
- if (this._connections.has(connName)) {
52
- return this._connections.get(connName);
53
- }
54
-
55
- const config = this._configs.get(connName);
56
- if (!config) {
57
- throw new Error(`DB connection '${connName}' not configured. Check fuzionx.yaml database.connections.`);
58
- }
59
-
60
- const conn = this._createConnection(connName, config);
61
- this._connections.set(connName, conn);
62
- return conn;
63
- }
64
-
65
- /**
66
- * 드라이버 타입 조회
67
- * @param {string} [name]
68
- * @returns {string} 'sqlite' | 'mariadb' | 'postgres' | 'mongodb'
69
- */
70
- getDriver(name) {
71
- const connName = name || this._default;
72
- const config = this._configs.get(connName);
73
- return config?.driver || 'sqlite';
74
- }
75
-
76
- /**
77
- * 드라이버별 연결 생성
78
- * @private
79
- */
80
- _createConnection(name, config) {
81
- const driver = (config.driver || 'sqlite').toLowerCase();
82
-
83
- switch (driver) {
84
- case 'sqlite': {
85
- const Database = tryRequire('better-sqlite3');
86
- if (!Database) return this._createStub('sqlite', config);
87
-
88
- const dbPath = config.database || config.path || ':memory:';
89
- const db = new Database(dbPath, {
90
- verbose: config.verbose ? console.log : undefined,
91
- });
92
- db.pragma('journal_mode = WAL');
93
- db.pragma('foreign_keys = ON');
94
- return { type: 'sqlite', db, config };
95
- }
96
-
97
- case 'mariadb':
98
- case 'mysql': {
99
- const knex = tryRequire('knex');
100
- if (!knex) return this._createStub('knex', config, 'mariadb');
101
-
102
- const knexInit = typeof knex === 'function' ? knex : knex.default;
103
- const db = knexInit({
104
- client: 'mysql2',
105
- connection: {
106
- host: config.host || '127.0.0.1',
107
- port: config.port || 3306,
108
- user: config.user || 'root',
109
- password: config.password || '',
110
- database: config.database || '',
111
- charset: config.charset || 'utf8mb4',
112
- },
113
- pool: config.pool || { min: 2, max: 10 },
114
- });
115
- return { type: 'knex', driver: 'mariadb', db, config };
116
- }
117
-
118
- case 'postgres':
119
- case 'postgresql': {
120
- const knex = tryRequire('knex');
121
- if (!knex) return this._createStub('knex', config, 'postgres');
122
-
123
- const knexInit = typeof knex === 'function' ? knex : knex.default;
124
- const db = knexInit({
125
- client: 'pg',
126
- connection: {
127
- host: config.host || '127.0.0.1',
128
- port: config.port || 5432,
129
- user: config.user || 'postgres',
130
- password: config.password || '',
131
- database: config.database || '',
132
- },
133
- pool: config.pool || { min: 2, max: 10 },
134
- });
135
- return { type: 'knex', driver: 'postgres', db, config };
136
- }
137
-
138
- case 'mongodb':
139
- case 'mongo': {
140
- const mongoose = tryRequire('mongoose');
141
- if (!mongoose) return this._createStub('mongo', config);
142
- // Lazy connect — promise 캐싱으로 이중 연결 방지
143
- const conn = { type: 'mongo', mongoose, config, _connected: false, _connectPromise: null };
144
- conn.connect = () => {
145
- if (conn._connected) return Promise.resolve();
146
- if (conn._connectPromise) return conn._connectPromise;
147
- const uri = config.uri || config.url
148
- || `mongodb://${config.host || '127.0.0.1'}:${config.port || 27017}/${config.database || ''}`;
149
- conn._connectPromise = mongoose.connect(uri, config.options || {}).then(() => {
150
- conn._connected = true;
151
- });
152
- return conn._connectPromise;
153
- };
154
- // 즉시 연결 시작 (await 없이 — 백그라운드)
155
- conn.connect().catch(err => {
156
- console.error(`[ConnectionManager] MongoDB 연결 실패: ${err.message}`);
157
- });
158
- return conn;
159
- }
160
-
161
- default:
162
- throw new Error(`Unsupported DB driver: '${driver}'. Supported: sqlite, mariadb, postgres, mongodb.`);
163
- }
164
- }
165
-
166
- /**
167
- * 드라이버 미설치 stub
168
- * @private
169
- */
170
- _createStub(type, config, driver) {
171
- return { type, driver: driver || type, db: null, config, _stub: true };
172
- }
173
-
174
- /**
175
- * 모든 연결 종료 (graceful shutdown)
176
- */
177
- async closeAll() {
178
- for (const [name, conn] of this._connections) {
179
- try {
180
- if (conn.type === 'sqlite' && conn.db) {
181
- conn.db.close();
182
- } else if (conn.type === 'knex' && conn.db) {
183
- await conn.db.destroy();
184
- } else if (conn.type === 'mongo' && conn.mongoose) {
185
- await conn.mongoose.disconnect();
186
- }
187
- } catch (err) {
188
- console.error(`[ConnectionManager] Error closing '${name}':`, err.message);
189
- }
190
- }
191
- this._connections.clear();
192
- }
193
-
194
- /**
195
- * 특정 연결 종료
196
- * @param {string} name
197
- */
198
- async close(name) {
199
- const conn = this._connections.get(name);
200
- if (!conn) return;
201
- try {
202
- if (conn.type === 'sqlite' && conn.db) conn.db.close();
203
- else if (conn.type === 'knex' && conn.db) await conn.db.destroy();
204
- else if (conn.type === 'mongo' && conn.mongoose) await conn.mongoose.disconnect();
205
- } catch {}
206
- this._connections.delete(name);
207
- }
208
- }
1
+ /**
2
+ * ConnectionManager — DB 연결 관리 싱글톤
3
+ *
4
+ * 드라이버별 연결 생성/캐싱/종료.
5
+ * SQLite (기본), MariaDB, PostgreSQL, MongoDB 지원.
6
+ *
7
+ * @see docs/framework/02-database-orm.md
8
+ * @see docs/framework/17-config.md
9
+ */
10
+ import { createRequire } from 'node:module';
11
+
12
+ const _require = createRequire(import.meta.url);
13
+
14
+ /**
15
+ * optional dependency 안전 로드
16
+ * @private
17
+ */
18
+ function tryRequire(name) {
19
+ try { return _require(name); } catch { return null; }
20
+ }
21
+
22
+ export default class ConnectionManager {
23
+ constructor() {
24
+ /** @type {Map<string, object>} name → connection */
25
+ this._connections = new Map();
26
+ /** @type {Map<string, object>} name → config */
27
+ this._configs = new Map();
28
+ /** @type {string} default connection name */
29
+ this._default = 'main';
30
+ }
31
+
32
+ /**
33
+ * 설정으로 초기화
34
+ * @param {object} dbConfig - fuzionx.yaml database 섹션
35
+ */
36
+ configure(dbConfig) {
37
+ this._default = dbConfig.default || 'main';
38
+ const connections = dbConfig.connections || {};
39
+ for (const [name, cfg] of Object.entries(connections)) {
40
+ this._configs.set(name, cfg);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * 연결 가져오기 (lazy 생성)
46
+ * @param {string} [name] - connection name (default: _default)
47
+ * @returns {object} driver connection instance
48
+ */
49
+ get(name) {
50
+ const connName = name || this._default;
51
+ if (this._connections.has(connName)) {
52
+ return this._connections.get(connName);
53
+ }
54
+
55
+ const config = this._configs.get(connName);
56
+ if (!config) {
57
+ throw new Error(`DB connection '${connName}' not configured. Check fuzionx.yaml database.connections.`);
58
+ }
59
+
60
+ const conn = this._createConnection(connName, config);
61
+ this._connections.set(connName, conn);
62
+ return conn;
63
+ }
64
+
65
+ /**
66
+ * 드라이버 타입 조회
67
+ * @param {string} [name]
68
+ * @returns {string} 'sqlite' | 'mariadb' | 'postgres' | 'mongodb'
69
+ */
70
+ getDriver(name) {
71
+ const connName = name || this._default;
72
+ const config = this._configs.get(connName);
73
+ return config?.driver || 'sqlite';
74
+ }
75
+
76
+ /**
77
+ * 드라이버별 연결 생성
78
+ * @private
79
+ */
80
+ _createConnection(name, config) {
81
+ const driver = (config.driver || 'sqlite').toLowerCase();
82
+
83
+ switch (driver) {
84
+ case 'sqlite': {
85
+ const Database = tryRequire('better-sqlite3');
86
+ if (!Database) return this._createStub('sqlite', config);
87
+
88
+ const dbPath = config.database || config.path || ':memory:';
89
+ const db = new Database(dbPath, {
90
+ verbose: config.verbose ? console.log : undefined,
91
+ });
92
+ db.pragma('journal_mode = WAL');
93
+ db.pragma('foreign_keys = ON');
94
+ return { type: 'sqlite', db, config };
95
+ }
96
+
97
+ case 'mariadb':
98
+ case 'mysql': {
99
+ const knex = tryRequire('knex');
100
+ if (!knex) return this._createStub('knex', config, 'mariadb');
101
+
102
+ const knexInit = typeof knex === 'function' ? knex : knex.default;
103
+ const db = knexInit({
104
+ client: 'mysql2',
105
+ connection: {
106
+ host: config.host || '127.0.0.1',
107
+ port: config.port || 3306,
108
+ user: config.user || 'root',
109
+ password: config.password || '',
110
+ database: config.database || '',
111
+ charset: config.charset || 'utf8mb4',
112
+ },
113
+ pool: config.pool || { min: 2, max: 10 },
114
+ });
115
+ return { type: 'knex', driver: 'mariadb', db, config };
116
+ }
117
+
118
+ case 'postgres':
119
+ case 'postgresql': {
120
+ const knex = tryRequire('knex');
121
+ if (!knex) return this._createStub('knex', config, 'postgres');
122
+
123
+ const knexInit = typeof knex === 'function' ? knex : knex.default;
124
+ const db = knexInit({
125
+ client: 'pg',
126
+ connection: {
127
+ host: config.host || '127.0.0.1',
128
+ port: config.port || 5432,
129
+ user: config.user || 'postgres',
130
+ password: config.password || '',
131
+ database: config.database || '',
132
+ },
133
+ pool: config.pool || { min: 2, max: 10 },
134
+ });
135
+ return { type: 'knex', driver: 'postgres', db, config };
136
+ }
137
+
138
+ case 'mongodb':
139
+ case 'mongo': {
140
+ const mongoose = tryRequire('mongoose');
141
+ if (!mongoose) return this._createStub('mongo', config);
142
+ // Lazy connect — promise 캐싱으로 이중 연결 방지
143
+ const conn = { type: 'mongo', mongoose, config, _connected: false, _connectPromise: null };
144
+ conn.connect = () => {
145
+ if (conn._connected) return Promise.resolve();
146
+ if (conn._connectPromise) return conn._connectPromise;
147
+ const uri = config.uri || config.url
148
+ || `mongodb://${config.host || '127.0.0.1'}:${config.port || 27017}/${config.database || ''}`;
149
+ conn._connectPromise = mongoose.connect(uri, config.options || {}).then(() => {
150
+ conn._connected = true;
151
+ });
152
+ return conn._connectPromise;
153
+ };
154
+ // 즉시 연결 시작 (await 없이 — 백그라운드)
155
+ conn.connect().catch(err => {
156
+ console.error(`[ConnectionManager] MongoDB 연결 실패: ${err.message}`);
157
+ });
158
+ return conn;
159
+ }
160
+
161
+ default:
162
+ throw new Error(`Unsupported DB driver: '${driver}'. Supported: sqlite, mariadb, postgres, mongodb.`);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * 드라이버 미설치 stub
168
+ * @private
169
+ */
170
+ _createStub(type, config, driver) {
171
+ return { type, driver: driver || type, db: null, config, _stub: true };
172
+ }
173
+
174
+ /**
175
+ * 모든 연결 종료 (graceful shutdown)
176
+ */
177
+ async closeAll() {
178
+ for (const [name, conn] of this._connections) {
179
+ try {
180
+ if (conn.type === 'sqlite' && conn.db) {
181
+ conn.db.close();
182
+ } else if (conn.type === 'knex' && conn.db) {
183
+ await conn.db.destroy();
184
+ } else if (conn.type === 'mongo' && conn.mongoose) {
185
+ await conn.mongoose.disconnect();
186
+ }
187
+ } catch (err) {
188
+ console.error(`[ConnectionManager] Error closing '${name}':`, err.message);
189
+ }
190
+ }
191
+ this._connections.clear();
192
+ }
193
+
194
+ /**
195
+ * 특정 연결 종료
196
+ * @param {string} name
197
+ */
198
+ async close(name) {
199
+ const conn = this._connections.get(name);
200
+ if (!conn) return;
201
+ try {
202
+ if (conn.type === 'sqlite' && conn.db) conn.db.close();
203
+ else if (conn.type === 'knex' && conn.db) await conn.db.destroy();
204
+ else if (conn.type === 'mongo' && conn.mongoose) await conn.mongoose.disconnect();
205
+ } catch {}
206
+ this._connections.delete(name);
207
+ }
208
+ }
@@ -1,29 +1,29 @@
1
- /**
2
- * MariaModel — MariaDB/MySQL 모델 서브클래스
3
- *
4
- * Knex + mysql2 기반.
5
- * SqlModel의 공통 SQL 로직을 상속하며,
6
- * MariaDB 고유 기능(FULLTEXT, JSON 등)은 raw()로 접근.
7
- *
8
- * @see docs/framework/02-database-orm.md
9
- *
10
- * @example
11
- * import { MariaModel } from '@fuzionx/framework';
12
- *
13
- * export default class User extends MariaModel {
14
- * static table = 'users';
15
- * static connection = 'main';
16
- * static timestamps = true;
17
- * static columns = {
18
- * id: { type: 'increments' },
19
- * name: { type: 'string', length: 100 },
20
- * email: { type: 'string', length: 150, unique: true },
21
- * };
22
- * }
23
- */
24
- import SqlModel from './SqlModel.js';
25
-
26
- export default class MariaModel extends SqlModel {
27
- static driver = 'mariadb';
28
- static connection = 'main';
29
- }
1
+ /**
2
+ * MariaModel — MariaDB/MySQL 모델 서브클래스
3
+ *
4
+ * Knex + mysql2 기반.
5
+ * SqlModel의 공통 SQL 로직을 상속하며,
6
+ * MariaDB 고유 기능(FULLTEXT, JSON 등)은 raw()로 접근.
7
+ *
8
+ * @see docs/framework/02-database-orm.md
9
+ *
10
+ * @example
11
+ * import { MariaModel } from '@fuzionx/framework';
12
+ *
13
+ * export default class User extends MariaModel {
14
+ * static table = 'users';
15
+ * static connection = 'main';
16
+ * static timestamps = true;
17
+ * static columns = {
18
+ * id: { type: 'increments' },
19
+ * name: { type: 'string', length: 100 },
20
+ * email: { type: 'string', length: 150, unique: true },
21
+ * };
22
+ * }
23
+ */
24
+ import SqlModel from './SqlModel.js';
25
+
26
+ export default class MariaModel extends SqlModel {
27
+ static driver = 'mariadb';
28
+ static connection = 'main';
29
+ }