@fuzionx/framework 0.1.43 → 0.1.45

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 -331
  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,227 +1,227 @@
1
- /**
2
- * AutoLoader — 앱 디렉토리 자동 스캔 + 등록
3
- *
4
- * Application.boot() 시 호출.
5
- * multi-app 구조:
6
- * mode='shared' → database/models, shared/events, shared/jobs, shared/workers
7
- * mode='app' → app/{name}/controllers, routes, services, middleware, ws
8
- *
9
- * @see docs/framework/04-bootstrap-lifecycle.md
10
- */
11
- import { promises as fs } from 'node:fs';
12
- import path from 'node:path';
13
- import { pathToFileURL } from 'node:url';
14
-
15
- /**
16
- * 디렉토리에서 .js 파일 목록 반환
17
- * @param {string} dir
18
- * @returns {Promise<string[]>}
19
- */
20
- async function scanDir(dir) {
21
- try {
22
- const entries = await fs.readdir(dir, { withFileTypes: true });
23
- return entries
24
- .filter(e => e.isFile() && e.name.endsWith('.js') && !e.name.startsWith('.'))
25
- .map(e => path.join(dir, e.name));
26
- } catch {
27
- return []; // 디렉토리 없으면 빈 배열
28
- }
29
- }
30
-
31
- /**
32
- * 파일명에서 PascalCase 이름 추출
33
- * 'UserController.js' → 'User'
34
- * 'User.js' → 'User'
35
- */
36
- function extractName(filePath, suffix = '') {
37
- const base = path.basename(filePath, '.js');
38
- return suffix && base.endsWith(suffix) ? base.slice(0, -suffix.length) : base;
39
- }
40
-
41
- export default class AutoLoader {
42
- /**
43
- * @param {import('./Application.js').default} app
44
- * @param {string} baseDir - 프로젝트 루트 (shared) 또는 앱 디렉토리 (app)
45
- * @param {object} [opts]
46
- * @param {'shared'|'app'} [opts.mode='shared']
47
- * @param {object} [opts.appContext] - 앱별 레지스트리 (mode='app' 시)
48
- */
49
- constructor(app, baseDir, opts = {}) {
50
- this.app = app;
51
- this.baseDir = baseDir;
52
- this._mode = opts.mode || 'shared';
53
- this._appContext = opts.appContext || null;
54
- }
55
-
56
- /**
57
- * 모드에 따른 스캔 + 등록 실행
58
- */
59
- async load() {
60
- if (this._mode === 'shared') {
61
- await this.loadModels('database/models');
62
- await this.loadEvents('shared/events');
63
- await this.loadJobs('shared/jobs');
64
- // workers는 WorkerPool._resolve()에서 shared/workers/ 경로로 자동 탐색
65
- } else if (this._mode === 'app') {
66
- await this.loadControllers();
67
- await this.loadRoutes();
68
- await this.loadServices();
69
- await this.loadMiddleware();
70
- await this.loadWsHandlers();
71
- }
72
- }
73
-
74
- /** database/models/*.js → ModelRegistry (공유) */
75
- async loadModels(subDir = 'models') {
76
- const files = await scanDir(path.join(this.baseDir, subDir));
77
- for (const file of files) {
78
- const mod = await import(pathToFileURL(file).href);
79
- const ModelClass = mod.default;
80
- if (!ModelClass) continue;
81
- const name = extractName(file);
82
- if (this.app.db) {
83
- this.app.db.register(name, ModelClass);
84
- }
85
- }
86
- }
87
-
88
- /**
89
- * controllers/*.js → 앱별 컨트롤러 레지스트리 + __handler__ static 등록
90
- *
91
- * 문서 01-routing-controllers.md:
92
- * 프로토타입 메서드를 static 레퍼런스로 자동 등록하여
93
- * 라우트에서 UserController.index 형태로 사용 가능.
94
- */
95
- async loadControllers() {
96
- const controllerDir = path.join(this.baseDir, 'controllers');
97
- const files = await scanDir(controllerDir);
98
- const registry = this._appContext?.controllers || this.app._controllers;
99
- if (!registry) return;
100
-
101
- for (const file of files) {
102
- const mod = await import(pathToFileURL(file).href);
103
- const ControllerClass = mod.default;
104
- if (!ControllerClass) continue;
105
-
106
- // __handler__ static 레퍼런스 자동 등록
107
- AutoLoader.registerController(ControllerClass);
108
-
109
- // 컨트롤러 이름 등록 (싱글톤 인스턴스는 boot 완료 후 생성)
110
- const name = extractName(file, 'Controller');
111
- registry.set(name, ControllerClass);
112
- }
113
- }
114
-
115
- /**
116
- * 컨트롤러 프로토타입 메서드를 static __handler__ 레퍼런스로 등록
117
- * @param {Function} ControllerClass
118
- */
119
- static registerController(ControllerClass) {
120
- const proto = ControllerClass.prototype;
121
- // Base 프로토타입 메서드 스킵 (Controller.register와 동일 로직)
122
- const baseNames = new Set(Object.getOwnPropertyNames(
123
- Object.getPrototypeOf(proto) // Base.prototype (Controller extends Base)
124
- ));
125
- for (const method of Object.getOwnPropertyNames(proto)) {
126
- if (method === 'constructor') continue;
127
- if (baseNames.has(method)) continue;
128
- if (typeof proto[method] !== 'function') continue;
129
- // static property로 __handler__ 디스크립터 등록
130
- ControllerClass[method] = {
131
- __handler__: true,
132
- controller: ControllerClass,
133
- method,
134
- };
135
- }
136
- }
137
-
138
- /** services/*.js → DI register (앱별 네임스페이스) */
139
- async loadServices() {
140
- const files = await scanDir(path.join(this.baseDir, 'services'));
141
- for (const file of files) {
142
- const mod = await import(pathToFileURL(file).href);
143
- const ServiceClass = mod.default;
144
- if (!ServiceClass) continue;
145
- const name = extractName(file, 'Service');
146
- this.app.register(`${name}Service`, (app) => new ServiceClass(app));
147
- }
148
- }
149
-
150
- /** middleware/*.js → 앱별 미들웨어 맵 */
151
- async loadMiddleware() {
152
- const registry = this._appContext?.middlewareRegistry || this.app._middlewareRegistry;
153
- if (!registry) return;
154
-
155
- const files = await scanDir(path.join(this.baseDir, 'middleware'));
156
- for (const file of files) {
157
- const mod = await import(pathToFileURL(file).href);
158
- const MwClass = mod.default;
159
- if (!MwClass) continue;
160
- const mwName = MwClass.alias || extractName(file, 'Middleware').toLowerCase();
161
- registry.set(mwName, MwClass);
162
- }
163
- }
164
-
165
- /** shared/events/*.js → EventBus.on() (공유) */
166
- async loadEvents(subDir = 'events') {
167
- const files = await scanDir(path.join(this.baseDir, subDir));
168
- for (const file of files) {
169
- const mod = await import(pathToFileURL(file).href);
170
- // events/*.js 는 export default (app) => { app.on('...', handler) }
171
- if (typeof mod.default === 'function') {
172
- mod.default(this.app);
173
- }
174
- }
175
- }
176
-
177
- /** shared/jobs/*.js → Scheduler + Queue (공유) */
178
- async loadJobs(subDir = 'jobs') {
179
- const files = await scanDir(path.join(this.baseDir, subDir));
180
- for (const file of files) {
181
- const mod = await import(pathToFileURL(file).href);
182
- const JobClass = mod.default;
183
- if (!JobClass) continue;
184
-
185
- // schedule이 있으면 cron Job → Scheduler
186
- if (JobClass.schedule && this.app._scheduler) {
187
- this.app._scheduler.register(JobClass);
188
- }
189
- // queue이 있으면 Task → Queue
190
- if (JobClass.queue && this.app._queue) {
191
- const name = JobClass.name;
192
- this.app._queue.register(name, JobClass);
193
- }
194
- }
195
- }
196
-
197
- /** ws/*.js → 앱별 WsHandler 등록 */
198
- async loadWsHandlers() {
199
- const registry = this._appContext?.wsHandlers || this.app._wsHandlers;
200
- if (!registry) return;
201
-
202
- const files = await scanDir(path.join(this.baseDir, 'ws'));
203
- for (const file of files) {
204
- const mod = await import(pathToFileURL(file).href);
205
- const HandlerClass = mod.default;
206
- if (!HandlerClass) continue;
207
- const ns = HandlerClass.namespace || '/';
208
- registry.set(ns, HandlerClass);
209
- }
210
- }
211
-
212
- /** routes/*.js → 앱별 Router.load() */
213
- async loadRoutes() {
214
- const router = this._appContext?.router || this.app._router;
215
- if (!router) return;
216
-
217
- const files = await scanDir(path.join(this.baseDir, 'routes'));
218
- for (const file of files) {
219
- const mod = await import(pathToFileURL(file).href);
220
- if (typeof mod.default === 'function') {
221
- router.load(mod.default);
222
- }
223
- }
224
- }
225
- }
226
-
227
- export { scanDir, extractName };
1
+ /**
2
+ * AutoLoader — 앱 디렉토리 자동 스캔 + 등록
3
+ *
4
+ * Application.boot() 시 호출.
5
+ * multi-app 구조:
6
+ * mode='shared' → database/models, shared/events, shared/jobs, shared/workers
7
+ * mode='app' → app/{name}/controllers, routes, services, middleware, ws
8
+ *
9
+ * @see docs/framework/04-bootstrap-lifecycle.md
10
+ */
11
+ import { promises as fs } from 'node:fs';
12
+ import path from 'node:path';
13
+ import { pathToFileURL } from 'node:url';
14
+
15
+ /**
16
+ * 디렉토리에서 .js 파일 목록 반환
17
+ * @param {string} dir
18
+ * @returns {Promise<string[]>}
19
+ */
20
+ async function scanDir(dir) {
21
+ try {
22
+ const entries = await fs.readdir(dir, { withFileTypes: true });
23
+ return entries
24
+ .filter(e => e.isFile() && e.name.endsWith('.js') && !e.name.startsWith('.'))
25
+ .map(e => path.join(dir, e.name));
26
+ } catch {
27
+ return []; // 디렉토리 없으면 빈 배열
28
+ }
29
+ }
30
+
31
+ /**
32
+ * 파일명에서 PascalCase 이름 추출
33
+ * 'UserController.js' → 'User'
34
+ * 'User.js' → 'User'
35
+ */
36
+ function extractName(filePath, suffix = '') {
37
+ const base = path.basename(filePath, '.js');
38
+ return suffix && base.endsWith(suffix) ? base.slice(0, -suffix.length) : base;
39
+ }
40
+
41
+ export default class AutoLoader {
42
+ /**
43
+ * @param {import('./Application.js').default} app
44
+ * @param {string} baseDir - 프로젝트 루트 (shared) 또는 앱 디렉토리 (app)
45
+ * @param {object} [opts]
46
+ * @param {'shared'|'app'} [opts.mode='shared']
47
+ * @param {object} [opts.appContext] - 앱별 레지스트리 (mode='app' 시)
48
+ */
49
+ constructor(app, baseDir, opts = {}) {
50
+ this.app = app;
51
+ this.baseDir = baseDir;
52
+ this._mode = opts.mode || 'shared';
53
+ this._appContext = opts.appContext || null;
54
+ }
55
+
56
+ /**
57
+ * 모드에 따른 스캔 + 등록 실행
58
+ */
59
+ async load() {
60
+ if (this._mode === 'shared') {
61
+ await this.loadModels('database/models');
62
+ await this.loadEvents('shared/events');
63
+ await this.loadJobs('shared/jobs');
64
+ // workers는 WorkerPool._resolve()에서 shared/workers/ 경로로 자동 탐색
65
+ } else if (this._mode === 'app') {
66
+ await this.loadControllers();
67
+ await this.loadRoutes();
68
+ await this.loadServices();
69
+ await this.loadMiddleware();
70
+ await this.loadWsHandlers();
71
+ }
72
+ }
73
+
74
+ /** database/models/*.js → ModelRegistry (공유) */
75
+ async loadModels(subDir = 'models') {
76
+ const files = await scanDir(path.join(this.baseDir, subDir));
77
+ for (const file of files) {
78
+ const mod = await import(pathToFileURL(file).href);
79
+ const ModelClass = mod.default;
80
+ if (!ModelClass) continue;
81
+ const name = extractName(file);
82
+ if (this.app.db) {
83
+ this.app.db.register(name, ModelClass);
84
+ }
85
+ }
86
+ }
87
+
88
+ /**
89
+ * controllers/*.js → 앱별 컨트롤러 레지스트리 + __handler__ static 등록
90
+ *
91
+ * 문서 01-routing-controllers.md:
92
+ * 프로토타입 메서드를 static 레퍼런스로 자동 등록하여
93
+ * 라우트에서 UserController.index 형태로 사용 가능.
94
+ */
95
+ async loadControllers() {
96
+ const controllerDir = path.join(this.baseDir, 'controllers');
97
+ const files = await scanDir(controllerDir);
98
+ const registry = this._appContext?.controllers || this.app._controllers;
99
+ if (!registry) return;
100
+
101
+ for (const file of files) {
102
+ const mod = await import(pathToFileURL(file).href);
103
+ const ControllerClass = mod.default;
104
+ if (!ControllerClass) continue;
105
+
106
+ // __handler__ static 레퍼런스 자동 등록
107
+ AutoLoader.registerController(ControllerClass);
108
+
109
+ // 컨트롤러 이름 등록 (싱글톤 인스턴스는 boot 완료 후 생성)
110
+ const name = extractName(file, 'Controller');
111
+ registry.set(name, ControllerClass);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * 컨트롤러 프로토타입 메서드를 static __handler__ 레퍼런스로 등록
117
+ * @param {Function} ControllerClass
118
+ */
119
+ static registerController(ControllerClass) {
120
+ const proto = ControllerClass.prototype;
121
+ // Base 프로토타입 메서드 스킵 (Controller.register와 동일 로직)
122
+ const baseNames = new Set(Object.getOwnPropertyNames(
123
+ Object.getPrototypeOf(proto) // Base.prototype (Controller extends Base)
124
+ ));
125
+ for (const method of Object.getOwnPropertyNames(proto)) {
126
+ if (method === 'constructor') continue;
127
+ if (baseNames.has(method)) continue;
128
+ if (typeof proto[method] !== 'function') continue;
129
+ // static property로 __handler__ 디스크립터 등록
130
+ ControllerClass[method] = {
131
+ __handler__: true,
132
+ controller: ControllerClass,
133
+ method,
134
+ };
135
+ }
136
+ }
137
+
138
+ /** services/*.js → DI register (앱별 네임스페이스) */
139
+ async loadServices() {
140
+ const files = await scanDir(path.join(this.baseDir, 'services'));
141
+ for (const file of files) {
142
+ const mod = await import(pathToFileURL(file).href);
143
+ const ServiceClass = mod.default;
144
+ if (!ServiceClass) continue;
145
+ const name = extractName(file, 'Service');
146
+ this.app.register(`${name}Service`, (app) => new ServiceClass(app));
147
+ }
148
+ }
149
+
150
+ /** middleware/*.js → 앱별 미들웨어 맵 */
151
+ async loadMiddleware() {
152
+ const registry = this._appContext?.middlewareRegistry || this.app._middlewareRegistry;
153
+ if (!registry) return;
154
+
155
+ const files = await scanDir(path.join(this.baseDir, 'middleware'));
156
+ for (const file of files) {
157
+ const mod = await import(pathToFileURL(file).href);
158
+ const MwClass = mod.default;
159
+ if (!MwClass) continue;
160
+ const mwName = MwClass.alias || extractName(file, 'Middleware').toLowerCase();
161
+ registry.set(mwName, MwClass);
162
+ }
163
+ }
164
+
165
+ /** shared/events/*.js → EventBus.on() (공유) */
166
+ async loadEvents(subDir = 'events') {
167
+ const files = await scanDir(path.join(this.baseDir, subDir));
168
+ for (const file of files) {
169
+ const mod = await import(pathToFileURL(file).href);
170
+ // events/*.js 는 export default (app) => { app.on('...', handler) }
171
+ if (typeof mod.default === 'function') {
172
+ mod.default(this.app);
173
+ }
174
+ }
175
+ }
176
+
177
+ /** shared/jobs/*.js → Scheduler + Queue (공유) */
178
+ async loadJobs(subDir = 'jobs') {
179
+ const files = await scanDir(path.join(this.baseDir, subDir));
180
+ for (const file of files) {
181
+ const mod = await import(pathToFileURL(file).href);
182
+ const JobClass = mod.default;
183
+ if (!JobClass) continue;
184
+
185
+ // schedule이 있으면 cron Job → Scheduler
186
+ if (JobClass.schedule && this.app._scheduler) {
187
+ this.app._scheduler.register(JobClass);
188
+ }
189
+ // queue이 있으면 Task → Queue
190
+ if (JobClass.queue && this.app._queue) {
191
+ const name = JobClass.name;
192
+ this.app._queue.register(name, JobClass);
193
+ }
194
+ }
195
+ }
196
+
197
+ /** ws/*.js → 앱별 WsHandler 등록 */
198
+ async loadWsHandlers() {
199
+ const registry = this._appContext?.wsHandlers || this.app._wsHandlers;
200
+ if (!registry) return;
201
+
202
+ const files = await scanDir(path.join(this.baseDir, 'ws'));
203
+ for (const file of files) {
204
+ const mod = await import(pathToFileURL(file).href);
205
+ const HandlerClass = mod.default;
206
+ if (!HandlerClass) continue;
207
+ const ns = HandlerClass.namespace || '/';
208
+ registry.set(ns, HandlerClass);
209
+ }
210
+ }
211
+
212
+ /** routes/*.js → 앱별 Router.load() */
213
+ async loadRoutes() {
214
+ const router = this._appContext?.router || this.app._router;
215
+ if (!router) return;
216
+
217
+ const files = await scanDir(path.join(this.baseDir, 'routes'));
218
+ for (const file of files) {
219
+ const mod = await import(pathToFileURL(file).href);
220
+ if (typeof mod.default === 'function') {
221
+ router.load(mod.default);
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ export { scanDir, extractName };
package/lib/core/Base.js CHANGED
@@ -1,64 +1,64 @@
1
- /**
2
- * Base — 공통 기본 클래스
3
- *
4
- * Controller, Service, Middleware, Job, Task, WsHandler가 상속.
5
- * 생성자에서 app을 받아 바로가기 프로퍼티 설정.
6
- *
7
- * @see docs/framework/class-design.mm.md (DI 주입 맵)
8
- */
9
- export default class Base {
10
- /**
11
- * @param {import('./Application.js').default} app
12
- */
13
- constructor(app) {
14
- /** @type {import('./Application.js').default} */
15
- this.app = app;
16
- /** @type {object} ModelRegistry */
17
- this.db = app.db;
18
- /** @type {import('./Config.js').default} */
19
- this.config = app.config;
20
- /** @type {object} Logger */
21
- this.logger = app.logger;
22
- }
23
-
24
- /**
25
- * 서비스 조회 (DI)
26
- * @param {string} name
27
- * @returns {*}
28
- */
29
- service(name) {
30
- return this.app.make(name);
31
- }
32
-
33
- /**
34
- * 이벤트 발행
35
- * @param {string} event
36
- * @param {*} [data]
37
- * @param {object} [opts]
38
- */
39
- async emit(event, data, opts) {
40
- await this.app.emit(event, data, opts);
41
- }
42
-
43
- /**
44
- * 큐 Task 디스패치
45
- * @param {string|Function} taskOrName
46
- * @param {object} data
47
- * @param {object} [opts]
48
- */
49
- dispatch(taskOrName, data, opts) {
50
- this.app.dispatch(taskOrName, data, opts);
51
- }
52
-
53
- /** @returns {object} WebSocket 매니저 */
54
- get ws() { return this.app.ws; }
55
-
56
- /** @returns {import('../schedule/WorkerPool.js').default} */
57
- get worker() { return this.app.worker; }
58
-
59
- /** @returns {import('./CryptoHelper.js').default} */
60
- get crypto() { return this.app.crypto; }
61
-
62
- /** @returns {import('./I18nHelper.js').default} */
63
- get i18n() { return this.app.i18n; }
64
- }
1
+ /**
2
+ * Base — 공통 기본 클래스
3
+ *
4
+ * Controller, Service, Middleware, Job, Task, WsHandler가 상속.
5
+ * 생성자에서 app을 받아 바로가기 프로퍼티 설정.
6
+ *
7
+ * @see docs/framework/class-design.mm.md (DI 주입 맵)
8
+ */
9
+ export default class Base {
10
+ /**
11
+ * @param {import('./Application.js').default} app
12
+ */
13
+ constructor(app) {
14
+ /** @type {import('./Application.js').default} */
15
+ this.app = app;
16
+ /** @type {object} ModelRegistry */
17
+ this.db = app.db;
18
+ /** @type {import('./Config.js').default} */
19
+ this.config = app.config;
20
+ /** @type {object} Logger */
21
+ this.logger = app.logger;
22
+ }
23
+
24
+ /**
25
+ * 서비스 조회 (DI)
26
+ * @param {string} name
27
+ * @returns {*}
28
+ */
29
+ service(name) {
30
+ return this.app.make(name);
31
+ }
32
+
33
+ /**
34
+ * 이벤트 발행
35
+ * @param {string} event
36
+ * @param {*} [data]
37
+ * @param {object} [opts]
38
+ */
39
+ async emit(event, data, opts) {
40
+ await this.app.emit(event, data, opts);
41
+ }
42
+
43
+ /**
44
+ * 큐 Task 디스패치
45
+ * @param {string|Function} taskOrName
46
+ * @param {object} data
47
+ * @param {object} [opts]
48
+ */
49
+ dispatch(taskOrName, data, opts) {
50
+ this.app.dispatch(taskOrName, data, opts);
51
+ }
52
+
53
+ /** @returns {object} WebSocket 매니저 */
54
+ get ws() { return this.app.ws; }
55
+
56
+ /** @returns {import('../schedule/WorkerPool.js').default} */
57
+ get worker() { return this.app.worker; }
58
+
59
+ /** @returns {import('./CryptoHelper.js').default} */
60
+ get crypto() { return this.app.crypto; }
61
+
62
+ /** @returns {import('./I18nHelper.js').default} */
63
+ get i18n() { return this.app.i18n; }
64
+ }