mega-framework 0.1.6 → 0.1.8

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 (248) hide show
  1. package/README.md +9 -0
  2. package/bin/mega-ws-hub.js +2 -2
  3. package/package.json +33 -9
  4. package/sample/crud/.env +10 -1
  5. package/sample/crud/.env.example +10 -1
  6. package/sample/crud/.mega/journal/history/20260612092543-create-users.json +261 -0
  7. package/sample/crud/.mega/journal/snapshot.json +261 -0
  8. package/sample/crud/apps/main/controllers/auth-controller.js +22 -14
  9. package/sample/crud/apps/main/controllers/web-controller.js +7 -5
  10. package/sample/crud/apps/main/locales/server/en.json +12 -1
  11. package/sample/crud/apps/main/locales/server/ko.json +12 -1
  12. package/sample/crud/apps/main/migrations/20260606000001-create-users.js +91 -13
  13. package/sample/crud/apps/main/migrations/20260606000002-create-boards.js +165 -0
  14. package/sample/crud/apps/main/migrations/20260606000003-create-logs.js +107 -0
  15. package/sample/crud/apps/main/models/log-partition-model.js +105 -0
  16. package/sample/crud/apps/main/models/note-model.js +79 -0
  17. package/sample/crud/apps/main/models/user-level-model.js +24 -0
  18. package/sample/crud/apps/main/models/user-model.js +146 -0
  19. package/sample/crud/apps/main/models/user-type-model.js +21 -0
  20. package/sample/crud/apps/main/models/wallet-model.js +24 -0
  21. package/sample/crud/apps/main/routes/users.js +55 -10
  22. package/sample/crud/apps/main/schedules/log-partition-schedule.js +33 -0
  23. package/sample/crud/apps/main/services/auth-service.js +39 -24
  24. package/sample/crud/apps/main/services/log-partition-service.js +101 -0
  25. package/sample/crud/apps/main/services/note-service.js +6 -6
  26. package/sample/crud/apps/main/services/redis-demo-service.js +3 -3
  27. package/sample/crud/apps/main/services/user-service.js +62 -21
  28. package/sample/crud/apps/main/views/auth/login.ejs +6 -6
  29. package/sample/crud/apps/main/views/auth/register.ejs +46 -5
  30. package/sample/crud/apps/main/views/users/edit.ejs +42 -5
  31. package/sample/crud/apps/main/views/users/list.ejs +6 -2
  32. package/sample/crud/apps/main/views/users/new.ejs +56 -4
  33. package/sample/crud/docs/log_partition_design.mm.md +23 -0
  34. package/sample/crud/mega.config.js +10 -2
  35. package/sample/crud/package.json +3 -3
  36. package/sample/crud/scripts/start-ws-hub.sh +20 -6
  37. package/sample/simple/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  38. package/sample/simple/package.json +2 -2
  39. package/src/adapters/adapter-manager.js +2 -1
  40. package/src/adapters/adapter-options.js +44 -3
  41. package/src/adapters/file-adapter.js +9 -5
  42. package/src/adapters/file-session-adapter.js +4 -3
  43. package/src/adapters/maria-adapter.js +33 -7
  44. package/src/adapters/mega-cache-adapter.js +83 -6
  45. package/src/adapters/mega-db-adapter.js +10 -1
  46. package/src/adapters/mongo-adapter.js +40 -8
  47. package/src/adapters/postgres-adapter.js +33 -6
  48. package/src/adapters/redis-adapter.js +7 -3
  49. package/src/adapters/sqlite-adapter.js +26 -3
  50. package/src/cli/commands/console-cmd.js +3 -1
  51. package/src/cli/commands/new.js +13 -3
  52. package/src/cli/commands/scaffold.js +173 -33
  53. package/src/cli/generators/index.js +140 -3
  54. package/src/cli/index.js +437 -155
  55. package/src/cli/watch.js +188 -0
  56. package/src/core/ajv-mapper.js +30 -3
  57. package/src/core/boot.js +464 -245
  58. package/src/core/cluster-metrics.js +13 -4
  59. package/src/core/ctx-builder.js +65 -3
  60. package/src/core/envelope.js +119 -12
  61. package/src/core/hub-link.js +89 -18
  62. package/src/core/i18n.js +11 -1
  63. package/src/core/index.js +7 -3
  64. package/src/core/mega-app.js +253 -505
  65. package/src/core/mega-cluster.js +4 -1
  66. package/src/core/mega-server.js +40 -9
  67. package/src/core/migration/dialect-registry.js +107 -0
  68. package/src/core/migration/dialects/README.md +62 -0
  69. package/src/core/migration/dialects/maria.js +496 -0
  70. package/src/core/migration/dialects/mongo.js +824 -0
  71. package/src/core/migration/dialects/postgres.js +563 -0
  72. package/src/core/migration/dialects/sqlite.js +476 -0
  73. package/src/core/migration/differ.js +456 -0
  74. package/src/core/migration/generate.js +508 -0
  75. package/src/core/migration/journal.js +167 -0
  76. package/src/core/migration/model-scan.js +84 -0
  77. package/src/core/migration/mongo-migration-db.js +97 -0
  78. package/src/core/migration/schema-builder.js +400 -0
  79. package/src/core/migration/schema-validator.js +315 -0
  80. package/src/core/migration-lock.js +205 -0
  81. package/src/core/migration-runner.js +166 -38
  82. package/src/core/multipart.js +28 -5
  83. package/src/core/pipeline.js +131 -0
  84. package/src/core/router.js +70 -65
  85. package/src/core/scope-registry.js +1 -0
  86. package/src/core/security.js +70 -12
  87. package/src/core/session-store.js +14 -1
  88. package/src/core/workers-manager.js +12 -1
  89. package/src/core/ws-cluster.js +10 -3
  90. package/src/core/ws-message.js +48 -4
  91. package/src/core/ws-presence.js +636 -0
  92. package/src/core/ws-roster.js +50 -8
  93. package/src/core/ws-upgrade.js +223 -12
  94. package/src/index.js +1 -1
  95. package/src/lib/hub-protocol.js +29 -0
  96. package/src/lib/mega-circuit-breaker.js +5 -3
  97. package/src/lib/mega-health.js +35 -4
  98. package/src/lib/mega-job-queue.js +151 -34
  99. package/src/lib/mega-job.js +37 -1
  100. package/src/lib/mega-metrics.js +31 -13
  101. package/src/lib/mega-plugin.js +34 -3
  102. package/src/lib/mega-schedule.js +40 -22
  103. package/src/lib/mega-shutdown.js +114 -39
  104. package/src/lib/mega-tracing.js +66 -19
  105. package/src/lib/mega-worker.js +33 -6
  106. package/src/lib/otel-resource.js +36 -0
  107. package/src/{cli → lib}/ws-hub.js +139 -15
  108. package/src/models/crud-sql-builder.js +133 -0
  109. package/src/models/mega-model.js +82 -2
  110. package/src/models/model-crud.js +483 -0
  111. package/src/models/mongo-crud.js +285 -0
  112. package/templates/adr/code.tpl +23 -0
  113. package/templates/model/code-mongo.tpl +35 -0
  114. package/templates/model/code.tpl +15 -1
  115. package/templates/model/test-mongo.tpl +38 -0
  116. package/templates/model/test.tpl +4 -0
  117. package/types/adapters/adapter-manager.d.ts +95 -0
  118. package/types/adapters/adapter-options.d.ts +93 -0
  119. package/types/adapters/file-adapter.d.ts +105 -0
  120. package/types/adapters/file-session-adapter.d.ts +103 -0
  121. package/types/adapters/index.d.ts +20 -0
  122. package/types/adapters/maria-adapter.d.ts +117 -0
  123. package/types/adapters/mega-adapter.d.ts +215 -0
  124. package/types/adapters/mega-bus-adapter.d.ts +45 -0
  125. package/types/adapters/mega-cache-adapter.d.ts +73 -0
  126. package/types/adapters/mega-db-adapter.d.ts +50 -0
  127. package/types/adapters/mega-lock-adapter.d.ts +62 -0
  128. package/types/adapters/mega-log-sink-adapter.d.ts +15 -0
  129. package/types/adapters/mega-session-adapter.d.ts +32 -0
  130. package/types/adapters/mongo-adapter.d.ts +150 -0
  131. package/types/adapters/nats-adapter.d.ts +108 -0
  132. package/types/adapters/postgres-adapter.d.ts +141 -0
  133. package/types/adapters/redis-adapter.d.ts +78 -0
  134. package/types/adapters/redis-session-adapter.d.ts +82 -0
  135. package/types/adapters/redlock-adapter.d.ts +149 -0
  136. package/types/adapters/registry.d.ts +46 -0
  137. package/types/adapters/sqlite-adapter.d.ts +112 -0
  138. package/types/auth/index.d.ts +24 -0
  139. package/types/cli/commands/console-cmd.d.ts +37 -0
  140. package/types/cli/commands/new.d.ts +16 -0
  141. package/types/cli/commands/routes.d.ts +36 -0
  142. package/types/cli/commands/scaffold.d.ts +78 -0
  143. package/types/cli/commands/test-cmd.d.ts +14 -0
  144. package/types/cli/generators/index.d.ts +122 -0
  145. package/types/cli/index.d.ts +234 -0
  146. package/types/cli/template-engine.d.ts +40 -0
  147. package/types/cli/watch.d.ts +59 -0
  148. package/types/core/ajv-mapper.d.ts +27 -0
  149. package/types/core/boot.d.ts +233 -0
  150. package/types/core/cluster-metrics.d.ts +52 -0
  151. package/types/core/config-loader.d.ts +13 -0
  152. package/types/core/config-validator.d.ts +30 -0
  153. package/types/core/ctx-builder.d.ts +103 -0
  154. package/types/core/envelope.d.ts +79 -0
  155. package/types/core/error-mapper.d.ts +17 -0
  156. package/types/core/formbody.d.ts +41 -0
  157. package/types/core/hub-link.d.ts +266 -0
  158. package/types/core/i18n.d.ts +178 -0
  159. package/types/core/index.d.ts +28 -0
  160. package/types/core/mega-app.d.ts +529 -0
  161. package/types/core/mega-cluster.d.ts +104 -0
  162. package/types/core/mega-server.d.ts +91 -0
  163. package/types/core/mega-service.d.ts +31 -0
  164. package/types/core/migration/dialect-registry.d.ts +22 -0
  165. package/types/core/migration/dialects/maria.d.ts +99 -0
  166. package/types/core/migration/dialects/mongo.d.ts +89 -0
  167. package/types/core/migration/dialects/postgres.d.ts +117 -0
  168. package/types/core/migration/dialects/sqlite.d.ts +111 -0
  169. package/types/core/migration/differ.d.ts +47 -0
  170. package/types/core/migration/generate.d.ts +56 -0
  171. package/types/core/migration/journal.d.ts +52 -0
  172. package/types/core/migration/model-scan.d.ts +19 -0
  173. package/types/core/migration/mongo-migration-db.d.ts +7 -0
  174. package/types/core/migration/schema-builder.d.ts +197 -0
  175. package/types/core/migration/schema-validator.d.ts +20 -0
  176. package/types/core/migration-lock.d.ts +33 -0
  177. package/types/core/migration-runner.d.ts +101 -0
  178. package/types/core/multipart.d.ts +86 -0
  179. package/types/core/openapi.d.ts +62 -0
  180. package/types/core/pipeline.d.ts +93 -0
  181. package/types/core/router.d.ts +159 -0
  182. package/types/core/routes-loader.d.ts +21 -0
  183. package/types/core/scope-registry.d.ts +14 -0
  184. package/types/core/security.d.ts +77 -0
  185. package/types/core/services-loader.d.ts +27 -0
  186. package/types/core/session-cleanup-schedule.d.ts +19 -0
  187. package/types/core/session-store.d.ts +25 -0
  188. package/types/core/session.d.ts +77 -0
  189. package/types/core/static-assets.d.ts +73 -0
  190. package/types/core/template.d.ts +106 -0
  191. package/types/core/workers-manager.d.ts +79 -0
  192. package/types/core/ws-cluster.d.ts +208 -0
  193. package/types/core/ws-compression.d.ts +112 -0
  194. package/types/core/ws-controller.d.ts +65 -0
  195. package/types/core/ws-message.d.ts +106 -0
  196. package/types/core/ws-presence.d.ts +273 -0
  197. package/types/core/ws-roster.d.ts +108 -0
  198. package/types/core/ws-upgrade.d.ts +260 -0
  199. package/types/errors/config-error.d.ts +10 -0
  200. package/types/errors/http-errors.d.ts +120 -0
  201. package/types/errors/index.d.ts +3 -0
  202. package/types/errors/mega-error.d.ts +32 -0
  203. package/types/index.d.ts +39 -0
  204. package/types/lib/asp/config.d.ts +49 -0
  205. package/types/lib/asp/crypto.d.ts +43 -0
  206. package/types/lib/asp/errors.d.ts +30 -0
  207. package/types/lib/asp/nonce-cache.d.ts +52 -0
  208. package/types/lib/asp/plugin.d.ts +30 -0
  209. package/types/lib/asp/ws-terminator.d.ts +45 -0
  210. package/types/lib/env-mapper.d.ts +14 -0
  211. package/types/lib/hub-protocol.d.ts +106 -0
  212. package/types/lib/index.d.ts +22 -0
  213. package/types/lib/logger/telegram-core.d.ts +104 -0
  214. package/types/lib/logger/telegram-transport.d.ts +45 -0
  215. package/types/lib/mega-brute-force.d.ts +66 -0
  216. package/types/lib/mega-circuit-breaker.d.ts +243 -0
  217. package/types/lib/mega-cron.d.ts +66 -0
  218. package/types/lib/mega-hash.d.ts +32 -0
  219. package/types/lib/mega-health.d.ts +48 -0
  220. package/types/lib/mega-job-queue.d.ts +188 -0
  221. package/types/lib/mega-job-worker.d.ts +130 -0
  222. package/types/lib/mega-job.d.ts +145 -0
  223. package/types/lib/mega-logger.d.ts +45 -0
  224. package/types/lib/mega-metrics.d.ts +285 -0
  225. package/types/lib/mega-plugin.d.ts +245 -0
  226. package/types/lib/mega-retry.d.ts +85 -0
  227. package/types/lib/mega-schedule.d.ts +260 -0
  228. package/types/lib/mega-shutdown.d.ts +135 -0
  229. package/types/lib/mega-tracing.d.ts +224 -0
  230. package/types/lib/mega-worker.d.ts +129 -0
  231. package/types/lib/otel-resource.d.ts +16 -0
  232. package/types/lib/worker-runner/process-entry.d.ts +1 -0
  233. package/types/lib/worker-runner/task-dispatch.d.ts +28 -0
  234. package/types/lib/worker-runner/thread-entry.d.ts +1 -0
  235. package/types/lib/ws-hub.d.ts +259 -0
  236. package/types/models/crud-sql-builder.d.ts +48 -0
  237. package/types/models/index.d.ts +1 -0
  238. package/types/models/mega-model.d.ts +138 -0
  239. package/types/models/model-crud.d.ts +82 -0
  240. package/types/models/mongo-crud.d.ts +59 -0
  241. package/types/test/index.d.ts +84 -0
  242. package/.env +0 -127
  243. package/sample/crud/apps/main/migrations/20260606000002-add-auth-to-users.js +0 -30
  244. package/sample/crud/apps/main/models/note.js +0 -71
  245. package/sample/crud/apps/main/models/user.js +0 -86
  246. package/sample/crud/package-lock.json +0 -5665
  247. package/sample/crud/yarn.lock +0 -2142
  248. package/sample/simple/package-lock.json +0 -1851
@@ -0,0 +1,93 @@
1
+ /**
2
+ * 미들웨어를 Fastify 가 async preHandler 로 인식하는 arity-2 래퍼로 감싸고, canonical ctx 를
3
+ * 3번째 인자로 주입한다 — 핸들러·글로벌 미들웨어와 동일한 `(req, reply, ctx)` 계약(ADR-134/184).
4
+ * ctx 는 **lazy 프록시**(ADR-214) — 미들웨어가 실제로 속성을 만질 때만 빌드되고, 요청당 캐싱이라
5
+ * 같은 요청의 모든 미들웨어·핸들러가 동일 ctx 객체를 공유한다.
6
+ *
7
+ * @param {Function} fn - `(req, reply, ctx?)` 미들웨어 (arity-2 도 하위 호환 — 3번째 인자 무시).
8
+ * @param {import('./mega-app.js').MegaApp | null} [app] - ctx 의 어댑터·서비스 접근자 출처(없으면 null).
9
+ * @returns {(req: import('fastify').FastifyRequest, reply: import('fastify').FastifyReply) => Promise<any>}
10
+ */
11
+ export function wrapPreHandler(fn: Function, app?: import("./mega-app.js").MegaApp | null): (req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply) => Promise<any>;
12
+ /**
13
+ * transform 배열을 단일 preSerialization 함수로 합성한다 — 순차 await, 각 변환의 반환값이
14
+ * 다음 변환의 payload 가 된다(raw data 만 다룸 — envelope 는 이후 단계, ADR-091).
15
+ *
16
+ * @param {Function[]} transforms
17
+ * @returns {(req: import('fastify').FastifyRequest, reply: import('fastify').FastifyReply, payload: any) => Promise<any>}
18
+ */
19
+ export function composeTransform(transforms: Function[]): (req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply, payload: any) => Promise<any>;
20
+ /**
21
+ * after 배열을 단일 onResponse 함수로 합성한다 — 응답 전송 후 side-effect 전용.
22
+ * 개별 after 의 throw 는 warn 로그 후 다음 after 로 진행(응답 영향 없음, ADR-091 / P4).
23
+ *
24
+ * @param {Function[]} afters
25
+ * @param {{ method: string, path: string }} route - warn 로그 식별용.
26
+ * @returns {(req: import('fastify').FastifyRequest, reply: import('fastify').FastifyReply) => Promise<void>}
27
+ */
28
+ export function composeAfter(afters: Function[], { method, path }: {
29
+ method: string;
30
+ path: string;
31
+ }): (req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply) => Promise<void>;
32
+ /**
33
+ * @typedef {object} HttpPipeline
34
+ * @property {(req: import('fastify').FastifyRequest, reply: import('fastify').FastifyReply) => Promise<any>} handler -
35
+ * canonical `(req, reply, ctx)` 주입이 적용된 Fastify 핸들러.
36
+ * @property {Function[]} [preHandler] - before 래퍼 배열 (before 없으면 생략).
37
+ * @property {Function} [preSerialization] - transform 합성 (transform 없으면 생략).
38
+ * @property {Function} [onResponse] - after 합성 (after 없으면 생략).
39
+ * @property {() => { method: string, path: string, before: string[], transform: string[], after: string[] }} describe -
40
+ * 체인 introspection — 단계별 미들웨어 이름 목록(익명은 `(anonymous)`).
41
+ */
42
+ /**
43
+ * 라우트 1개의 HTTP 라이프사이클을 합성한다. Router._registerHttp 가 Fastify routeOpts 로 옮긴다.
44
+ *
45
+ * @param {object} args
46
+ * @param {import('./mega-app.js').MegaApp | null} args.app - ctx 출처 (standalone Router 면 null).
47
+ * @param {string} args.method - HTTP 메서드 (소문자).
48
+ * @param {string} args.path - 라우트 경로.
49
+ * @param {Function} args.handler - 사용자 핸들러 (inline 또는 static method ref, ADR-074).
50
+ * @param {Function[]} [args.before]
51
+ * @param {Function[]} [args.transform]
52
+ * @param {Function[]} [args.after]
53
+ * @returns {HttpPipeline}
54
+ */
55
+ export function buildHttpPipeline({ app, method, path, handler, before, transform, after }: {
56
+ app: import("./mega-app.js").MegaApp | null;
57
+ method: string;
58
+ path: string;
59
+ handler: Function;
60
+ before?: Function[];
61
+ transform?: Function[];
62
+ after?: Function[];
63
+ }): HttpPipeline;
64
+ export type HttpPipeline = {
65
+ /**
66
+ * -
67
+ * canonical `(req, reply, ctx)` 주입이 적용된 Fastify 핸들러.
68
+ */
69
+ handler: (req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply) => Promise<any>;
70
+ /**
71
+ * - before 래퍼 배열 (before 없으면 생략).
72
+ */
73
+ preHandler?: Function[];
74
+ /**
75
+ * - transform 합성 (transform 없으면 생략).
76
+ */
77
+ preSerialization?: Function;
78
+ /**
79
+ * - after 합성 (after 없으면 생략).
80
+ */
81
+ onResponse?: Function;
82
+ /**
83
+ * -
84
+ * 체인 introspection — 단계별 미들웨어 이름 목록(익명은 `(anonymous)`).
85
+ */
86
+ describe: () => {
87
+ method: string;
88
+ path: string;
89
+ before: string[];
90
+ transform: string[];
91
+ after: string[];
92
+ };
93
+ };
@@ -0,0 +1,159 @@
1
+ /**
2
+ * 라우트 등록 옵션 (router.http.*(path, handler, opts) / router.ws(path, Channel, opts)).
3
+ * @typedef {object} HttpRouteOpts
4
+ * @property {Function[]} [before] - preHandler 단계 미들웨어 (인증·rateLimit·검증).
5
+ * @property {Function[]} [transform] - preSerialization 이른 단계 변환 (HTTP 전용).
6
+ * @property {Function[]} [after] - onResponse 단계 side-effect (HTTP 전용).
7
+ * @property {Object} [schema] - Fastify schema (ADR-019).
8
+ * @property {{ tags?: string[], summary?: string, description?: string, deprecated?: boolean }} [openapi] -
9
+ * OpenAPI 라우트 메타 (ADR-070/140). 라우트 schema 의 비검증 메타 필드로 병합돼 명세에 반영(openapi 옵트인 시).
10
+ */
11
+ /** 라우트 등록 시 잘못된 형식 시 throw. */
12
+ export class MegaRouteError extends MegaError {
13
+ }
14
+ /**
15
+ * Router 인스턴스 — MegaApp 부팅 시 각 라우트 파일마다 생성·전달.
16
+ *
17
+ * HTTP 핸들러 등록 + before/transform/after 미들웨어 wiring.
18
+ * WS 는 hub 통합 시 등록 시그니처만 보존, 실제 핸들러는 inline /
19
+ * Static method ref 패턴 지원.
20
+ */
21
+ export class Router {
22
+ /**
23
+ * @param {Object} ctx
24
+ * @param {import('fastify').FastifyInstance} ctx.fastify - 등록 대상 Fastify
25
+ * @param {string} ctx.appName - 디버그·에러 메시지용 앱 이름
26
+ * @param {string} [ctx.sourceFile] - 라우트 파일 경로 (에러 메시지용)
27
+ * @param {import('./mega-app.js').MegaApp} [ctx.app] - 바인딩 앱. HTTP 핸들러 ctx 의 `db/cache/bus`
28
+ * 접근자(ADR-102) 출처. 미지정(standalone Router)이면 ctx 에 어댑터 접근자가 빠진다.
29
+ */
30
+ constructor(ctx: {
31
+ fastify: import("fastify").FastifyInstance;
32
+ appName: string;
33
+ sourceFile?: string;
34
+ app?: import("./mega-app.js").MegaApp;
35
+ });
36
+ _fastify: import("fastify").FastifyInstance<import("fastify").RawServerDefault, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>;
37
+ _appName: string;
38
+ _sourceFile: string;
39
+ /** @type {import('./mega-app.js').MegaApp | null} */
40
+ _app: import("./mega-app.js").MegaApp | null;
41
+ /** @type {Function[]} 파일 레벨 transform — 이 Router(=라우트 파일)로 이후 등록되는 라우트에 합성 (ADR-194). */
42
+ _fileTransforms: Function[];
43
+ /** @type {Function[]} 파일 레벨 after — 위와 동일 스코프 (ADR-194). */
44
+ _fileAfters: Function[];
45
+ http: Readonly<Record<string, (path: string, handler: Function, opts?: HttpRouteOpts) => void>>;
46
+ /**
47
+ * 바인딩된 MegaApp (standalone Router 면 null).
48
+ *
49
+ * 라우트 모듈(`export default (router) => {}`)이 앱 표면에 닿는 통로다 — 특히 WS `before(req)`
50
+ * upgrade 인증은 Fastify 밖이라 `req.session` 이 없어, `router.app.sessionStore` 로 세션 스토어를
51
+ * 얻어 {@link import('./session.js').readSession} 으로 신원을 확인한다(ADR-159).
52
+ *
53
+ * @returns {import('./mega-app.js').MegaApp | null}
54
+ */
55
+ get app(): import("./mega-app.js").MegaApp | null;
56
+ /**
57
+ * HTTP 프록시: router.http.get(...), router.http.post(...) 등 7개 메서드.
58
+ * @private
59
+ */
60
+ private _buildHttpProxy;
61
+ /**
62
+ * 실제 HTTP 라우트 등록 — Fastify route() + before/transform/after wiring.
63
+ * @param {string} method - HTTP 메서드 (소문자).
64
+ * @param {string} path - 라우트 경로.
65
+ * @param {Function} handler - 핸들러 (inline async fn 또는 static method ref).
66
+ * @param {HttpRouteOpts} opts
67
+ * @private
68
+ */
69
+ private _registerHttp;
70
+ /**
71
+ * WebSocket 라우트 등록 (실 upgrade wiring).
72
+ *
73
+ * `ChannelClass` 가 {@link MegaWebSocketController} 를 상속하는지 **부팅 시 검증** (ADR-089 fail-fast).
74
+ * 등록된 라우트는 `fastify._megaWsRoutes` 에 보관되고, MegaApp 이 listen 시점에 HTTP `'upgrade'`
75
+ * 이벤트에 연결한다 (core/ws-upgrade.js).
76
+ *
77
+ * WS 는 `before`(upgrade 인증) 만 지원한다 — `transform`/`after` 는 HTTP 전용 (ADR-074/091).
78
+ *
79
+ * `schemas` 는 **메시지 type 별 payload JSON Schema** 맵이다 (`{ [type]: jsonSchema }`, M2).
80
+ * 등록 시점에 AJV 로 사전 컴파일되며, 수신 메시지의 `type` 에 해당 스키마가 있으면 dispatch
81
+ * 직전에 `payload` 를 검증한다. 실패 시 `ws.invalid_payload` error envelope 응답(연결 유지) —
82
+ * HTTP AJV 매핑(ADR-090)과 동일한 fail-closed 정책. 스키마 없는 type 은 그대로 통과.
83
+ *
84
+ * @param {string} path - WS 경로 (= ASP namespace, 예: '/ws/chat').
85
+ * @param {Function} ChannelClass - MegaWebSocketController 를 상속한 채널 클래스.
86
+ * @param {{ before?: Function[], schemas?: Object, transform?: unknown, after?: unknown }} [opts]
87
+ * - schemas: `{ [messageType]: JSONSchema }` — type 별 payload 스키마 (AJV 사전 컴파일됨).
88
+ * @throws {MegaRouteError} path/ChannelClass 형식 위반, 상속 위반, 또는 schemas 형식·컴파일 실패.
89
+ */
90
+ ws(path: string, ChannelClass: Function, opts?: {
91
+ before?: Function[];
92
+ schemas?: Object;
93
+ transform?: unknown;
94
+ after?: unknown;
95
+ }): void;
96
+ /**
97
+ * 파일 전체 적용 미들웨어 (router.use, ADR-194 — stage 3종).
98
+ *
99
+ * - `stage: 'before'`(디폴트) — fastify preHandler 훅 등록. 실행 순서: 전역 → 앱 → 파일(use)
100
+ * → 라우트(before opts) → handler. 라우트 `before` 와 동일하게 arity-2 래퍼 + ctx 주입 —
101
+ * raw 로 넘기면 arity-3 미들웨어(`requireAuth` 등 canonical `(req, reply, ctx)`)가 Fastify
102
+ * async hook arity 검사(`FST_ERR_HOOK_INVALID_ASYNC_HANDLER`)에 걸려 부팅이 거부된다(ADR-156).
103
+ * - `stage: 'transform'` — 응답 raw data 변환 `(req, reply, payload) => payload`. 이 Router
104
+ * (=라우트 파일)로 **이후 등록되는** 라우트의 transform 체인 뒤에 합성된다(라우트 → 파일,
105
+ * ADR-021). envelope wrap 전 단계.
106
+ * - `stage: 'after'` — 응답 전송 후 side-effect `(req, reply)`. 동일 스코프로 after 체인 뒤에
107
+ * 합성(라우트 → 파일). throw 는 warn 로그 후 무시(ADR-091).
108
+ *
109
+ * 세 stage 모두 **호출 이후 등록되는 라우트**에만 적용된다 — 파일 상단(라우트 등록 전)에서
110
+ * 호출할 것.
111
+ *
112
+ * @param {Function} middleware
113
+ * @param {{ stage?: 'before' | 'transform' | 'after' }} [opts]
114
+ */
115
+ use(middleware: Function, opts?: {
116
+ stage?: "before" | "transform" | "after";
117
+ }): void;
118
+ /**
119
+ * @param {unknown} arr - 검증 대상 (Function[] 기대).
120
+ * @param {string} name - 옵션 이름 (before/transform/after).
121
+ * @param {string} method - HTTP 메서드 또는 'ws'.
122
+ * @param {string} path - 라우트 경로.
123
+ * @returns {Function[]}
124
+ * @private
125
+ */
126
+ private _validateMiddlewareArray;
127
+ }
128
+ /**
129
+ * 라우트 등록 옵션 (router.http.*(path, handler, opts) / router.ws(path, Channel, opts)).
130
+ */
131
+ export type HttpRouteOpts = {
132
+ /**
133
+ * - preHandler 단계 미들웨어 (인증·rateLimit·검증).
134
+ */
135
+ before?: Function[];
136
+ /**
137
+ * - preSerialization 이른 단계 변환 (HTTP 전용).
138
+ */
139
+ transform?: Function[];
140
+ /**
141
+ * - onResponse 단계 side-effect (HTTP 전용).
142
+ */
143
+ after?: Function[];
144
+ /**
145
+ * - Fastify schema (ADR-019).
146
+ */
147
+ schema?: Object;
148
+ /**
149
+ * -
150
+ * OpenAPI 라우트 메타 (ADR-070/140). 라우트 schema 의 비검증 메타 필드로 병합돼 명세에 반영(openapi 옵트인 시).
151
+ */
152
+ openapi?: {
153
+ tags?: string[];
154
+ summary?: string;
155
+ description?: string;
156
+ deprecated?: boolean;
157
+ };
158
+ };
159
+ import { MegaError } from '../errors/mega-error.js';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 앱의 routes/ 폴더 전체 스캔 → 각 파일을 Router 와 함께 실행.
3
+ *
4
+ * @param {Object} opts
5
+ * @param {import('fastify').FastifyInstance} opts.fastify
6
+ * @param {string} opts.appName
7
+ * @param {string} opts.routesDir - apps/<name>/routes 절대 경로
8
+ * @param {import('./mega-app.js').MegaApp} [opts.app] - 바인딩 앱 (HTTP ctx 의 db/cache/bus 접근자 출처, ADR-102)
9
+ * @returns {Promise<{ filesLoaded: number, routes: Array<{file: string}> }>}
10
+ */
11
+ export function loadRoutes({ fastify, appName, routesDir, app }: {
12
+ fastify: import("fastify").FastifyInstance;
13
+ appName: string;
14
+ routesDir: string;
15
+ app?: import("./mega-app.js").MegaApp;
16
+ }): Promise<{
17
+ filesLoaded: number;
18
+ routes: Array<{
19
+ file: string;
20
+ }>;
21
+ }>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * mega.config.js (Global) / apps/<name>/app.config.js (App + Shared-Reference) 의
3
+ * 키 분리 규약 (ADR-061). 알 수 없는 키 + 잘못된 스코프 → 부팅 throw.
4
+ *
5
+ * @module core/scope-registry
6
+ */
7
+ /** mega.config.js 최상위에만 허용되는 키 */
8
+ export const GLOBAL_ONLY_KEYS: readonly string[];
9
+ /** apps/<name>/app.config.js 에만 허용되는 키 */
10
+ export const APP_ONLY_KEYS: readonly string[];
11
+ /** Shared-Reference 키 — 전역 services 의 키만 참조 가능 */
12
+ export const SHARED_REFERENCE_KEYS: readonly string[];
13
+ export const ALL_GLOBAL_KEYS: readonly string[];
14
+ export const ALL_APP_KEYS: readonly string[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 보안 플러그인 자동 등록 결과 요약(디버그·테스트용).
3
+ * @typedef {Object} SecuritySummary
4
+ * @property {boolean} helmet
5
+ * @property {boolean} cors
6
+ * @property {boolean} rateLimit
7
+ * @property {boolean} csrf
8
+ * @property {boolean} asp - HTTP ASP 등록 여부.
9
+ * @property {'redis'|'memory'|null} rateLimitStore - rate-limit 백엔드(미등록이면 null).
10
+ */
11
+ /**
12
+ * Fastify 인스턴스에 보안 플러그인 4종 + ASP HTTP 를 자동 등록한다.
13
+ *
14
+ * ⚠️ **호출 순서** — 반드시 `/health` 라우트 등록 "이전"에 호출해야 한다. @fastify/rate-limit 은 onRoute
15
+ * hook 으로 라우트별 제한을 붙이는데, onRoute 는 플러그인 등록 "이후" 라우트만 잡는다. health 보다 먼저
16
+ * 등록해야 health 의 `config.rateLimit: false` 면제가 실효한다.
17
+ *
18
+ * @param {import('fastify').FastifyInstance} fastify - 대상 앱 Fastify 인스턴스.
19
+ * @param {Object} opts
20
+ * @param {string} opts.appName - 앱 이름(로그·에러 메시지용).
21
+ * @param {string[]} [opts.hosts] - 앱 도메인 목록(CSRF Origin 검증 allowlist, ADR-051).
22
+ * @param {Object|false} [opts.helmet] - fastify-helmet 옵션. false=미등록, undefined=디폴트 ON.
23
+ * @param {Object|false} [opts.cors] - fastify-cors 옵션. false=미등록, undefined=origin:false(교차출처 거부).
24
+ * @param {Object|false} [opts.rateLimit] - fastify-rate-limit 옵션. false=미등록, undefined=디폴트(1000/min, ADR-214).
25
+ * @param {Object|false} [opts.csrf] - fastify-csrf-protection 옵션. false=미등록, undefined=디폴트 ON.
26
+ * @param {{ masterSecret?: string, http?: { enabledPaths?: string[], driftMs?: number, headerSignal?: string, timestampHeader?: string, nonceCache?: any } }} [opts.asp] -
27
+ * ASP 설정. `masterSecret` + `http.enabledPaths`(비어있지 않음)가 둘 다 있을 때만 HTTP ASP 등록.
28
+ * @param {(() => import('ioredis').Redis | null)} [opts.resolveRateStore] - rate-limit Redis 백엔드 해석기.
29
+ * `caches.rate` 별명을 ioredis 인스턴스로 돌려주면 분산 카운팅(ADR-048), null 이면 in-memory 폴백.
30
+ * @param {{ debug?: Function, warn?: Function }} [opts.logger] - 길목 debug 로그(선택).
31
+ * @returns {SecuritySummary}
32
+ */
33
+ export function registerSecurityPlugins(fastify: import("fastify").FastifyInstance, opts: {
34
+ appName: string;
35
+ hosts?: string[];
36
+ helmet?: Object | false;
37
+ cors?: Object | false;
38
+ rateLimit?: Object | false;
39
+ csrf?: Object | false;
40
+ asp?: {
41
+ masterSecret?: string;
42
+ http?: {
43
+ enabledPaths?: string[];
44
+ driftMs?: number;
45
+ headerSignal?: string;
46
+ timestampHeader?: string;
47
+ nonceCache?: any;
48
+ };
49
+ };
50
+ resolveRateStore?: (() => import("ioredis").Redis | null);
51
+ logger?: {
52
+ debug?: Function;
53
+ warn?: Function;
54
+ };
55
+ }): SecuritySummary;
56
+ /** rate-limit 기본값 (IP 당 1000 req/min — ADR-048 의 100/min 은 평상 트래픽도 429 를 만드는 운영 함정이라 상향, ADR-214). 사용자 config 로 완전 교체(ADR-073). */
57
+ export const DEFAULT_RATE_LIMIT: Readonly<{
58
+ max: 1000;
59
+ timeWindow: "1 minute";
60
+ }>;
61
+ /**
62
+ * 보안 플러그인 자동 등록 결과 요약(디버그·테스트용).
63
+ */
64
+ export type SecuritySummary = {
65
+ helmet: boolean;
66
+ cors: boolean;
67
+ rateLimit: boolean;
68
+ csrf: boolean;
69
+ /**
70
+ * - HTTP ASP 등록 여부.
71
+ */
72
+ asp: boolean;
73
+ /**
74
+ * - rate-limit 백엔드(미등록이면 null).
75
+ */
76
+ rateLimitStore: "redis" | "memory" | null;
77
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 서비스 파일명 → DI 이름 도출. `-service` suffix 를 떼고 kebab→camelCase 한다
3
+ * (`user-service.js` → `user`, `audit-log-service.js` → `auditLog`, docs/03 §149 정본).
4
+ * suffix 가 없으면 파일명 stem 을 그대로 camelCase 한다(`user.js` → `user`).
5
+ *
6
+ * @param {string} fileName - `.js` 포함 파일명.
7
+ * @returns {string} ctx.services.<name> 키.
8
+ */
9
+ export function serviceNameFromFile(fileName: string): string;
10
+ /**
11
+ * 앱의 services/ 폴더 전체 스캔 → 각 파일의 default export(MegaService 서브클래스)를
12
+ * `name → Class` 레지스트리(Map)로 만든다. 부팅 시 1회 실행되며, 요청별 인스턴스화는
13
+ * `ctx.services` lazy proxy 가 이 레지스트리를 보고 수행한다(ADR-148).
14
+ *
15
+ * routes-loader 와 동일하게 flat readdir(하위 폴더 미스캔) + `.test.js` 제외 + 정렬한다.
16
+ * 폴더 부재(ENOENT)는 정상(서비스 없는 앱) — 빈 Map 반환.
17
+ *
18
+ * @param {Object} opts
19
+ * @param {string} opts.servicesDir - apps/<name>/services 절대 경로.
20
+ * @param {string} opts.appName - 앱 이름(에러 메시지용).
21
+ * @returns {Promise<Map<string, Function>>} name → 서비스 클래스.
22
+ * @throws {MegaConfigError} 로드 실패 / default export 없음 / MegaService 미상속 / 이름 중복.
23
+ */
24
+ export function loadServices({ servicesDir, appName }: {
25
+ servicesDir: string;
26
+ appName: string;
27
+ }): Promise<Map<string, Function>>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 만료 세션 정리 `MegaSchedule` 서브클래스를 만든다.
3
+ *
4
+ * @param {import('../adapters/mega-session-adapter.js').MegaSessionAdapter} store - 정리할 세션 스토어(연결됨).
5
+ * @param {object} [opts]
6
+ * @param {string} [opts.cron='0 * * * *'] - cron 표현식(기본 매시 정각).
7
+ * @param {string} [opts.timezone] - IANA 타임존.
8
+ * @param {import('../lib/mega-schedule.js').MegaScheduleLock} [opts.lock] - 분산 중복방지 락(멀티 인스턴스).
9
+ * @param {string} [opts.name='SessionCleanup'] - 스케줄 이름(MegaScheduler 등록 키).
10
+ * @returns {typeof MegaSchedule}
11
+ * @throws {Error} store 가 없거나 cleanup 메서드가 없을 때.
12
+ */
13
+ export function createSessionCleanupSchedule(store: import("../adapters/mega-session-adapter.js").MegaSessionAdapter, opts?: {
14
+ cron?: string;
15
+ timezone?: string;
16
+ lock?: import("../lib/mega-schedule.js").MegaScheduleLock;
17
+ name?: string;
18
+ }): typeof MegaSchedule;
19
+ import { MegaSchedule } from '../lib/mega-schedule.js';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 세션 스토어 어댑터를 만든다(connect 는 호출자 책임 — MegaApp 가 부팅 시 connect).
3
+ *
4
+ * @param {{ driver?: string, [k: string]: any }} storeConfig - `session.store` 설정.
5
+ * `driver` ('file'|'redis') + driver 별 옵션(basePath / url / keyPrefix / ttlMs …).
6
+ * @param {{ ttlMs?: number }} [defaults] - 미들웨어가 주입하는 기본값(store 에 ttlMs 미지정 시 사용).
7
+ * @returns {import('../adapters/mega-session-adapter.js').MegaSessionAdapter}
8
+ * @throws {MegaConfigError} `session.invalid_store` - storeConfig 누락/형식 오류.
9
+ * @throws {MegaConfigError} `session.unknown_driver` - 미지원 driver.
10
+ */
11
+ export function createSessionStore(storeConfig: {
12
+ driver?: string;
13
+ [k: string]: any;
14
+ }, defaults?: {
15
+ ttlMs?: number;
16
+ }): import("../adapters/mega-session-adapter.js").MegaSessionAdapter;
17
+ /**
18
+ * file driver 만료 스캔 cleanup 기본 주기(1시간, ms) — ADR-046 의 "file 모드 cleanup 자동" 약속 구현(ADR-215).
19
+ * 어댑터 자체 디폴트는 0(off)이라, 팩토리 경유 생성(=프레임워크 부팅 경로)에서만 기본 주기를 주입한다.
20
+ * 만료 파일의 lazy 삭제는 같은 sid 재접근 시에만 일어나므로, 주기 스캔이 없으면 미접근 만료 세션이
21
+ * 디스크에 무한 누적된다(G5 audit M-3). `cleanupIntervalMs: 0` 명시로 옵트아웃(외부 cron/scheduler 운용 시).
22
+ */
23
+ export const DEFAULT_FILE_CLEANUP_INTERVAL_MS: 3600000;
24
+ /** 지원 세션 driver 목록(테스트·문서용). */
25
+ export const SESSION_STORE_DRIVERS: readonly string[];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * ULID sid 생성 — 48-bit timestamp + 80-bit randomness = 26자 Crockford base32 (ADR-046).
3
+ * (ws-message 의 generateMessageId 는 "envelope 전용"이라 세션용 독립 helper 를 둔다. 정식 MegaIdGen 은 OQ-014.)
4
+ *
5
+ * @param {number} [timestamp] - epoch ms(테스트 주입용).
6
+ * @returns {string} 26자 ULID.
7
+ */
8
+ export function generateSid(timestamp?: number): string;
9
+ /**
10
+ * Fastify 인스턴스에 세션 미들웨어를 등록한다.
11
+ *
12
+ * @param {import('fastify').FastifyInstance} fastify
13
+ * @param {object} opts
14
+ * @param {import('../adapters/mega-session-adapter.js').MegaSessionAdapter} opts.store - 연결된 세션 스토어.
15
+ * @param {string} opts.secret - 쿠키 HMAC 서명 시크릿(global `server.sessionSecret`).
16
+ * @param {number} [opts.ttlMs] - 세션 TTL(ms). 기본 24시간.
17
+ * @param {boolean} [opts.rolling] - rolling TTL(기본 true, ADR-046).
18
+ * @param {{ name?: string, path?: string, httpOnly?: boolean, secure?: boolean|'auto', sameSite?: string }} [opts.cookie]
19
+ * @param {string} [opts.driver] - 스토어 driver 명(트레이싱용; 미지정 시 store.getStats().driver).
20
+ * @param {{ debug?: Function, warn?: Function }} [opts.logger]
21
+ * @returns {void}
22
+ * @throws {Error} secret 누락 — 서명 불가(부팅 abort).
23
+ */
24
+ export function registerSession(fastify: import("fastify").FastifyInstance, opts: {
25
+ store: import("../adapters/mega-session-adapter.js").MegaSessionAdapter;
26
+ secret: string;
27
+ ttlMs?: number;
28
+ rolling?: boolean;
29
+ cookie?: {
30
+ name?: string;
31
+ path?: string;
32
+ httpOnly?: boolean;
33
+ secure?: boolean | "auto";
34
+ sameSite?: string;
35
+ };
36
+ driver?: string;
37
+ logger?: {
38
+ debug?: Function;
39
+ warn?: Function;
40
+ };
41
+ }): void;
42
+ /**
43
+ * Fastify 요청 파이프라인 밖(raw `IncomingMessage`)에서 세션을 **읽기 전용**으로 복원한다.
44
+ *
45
+ * HTTP `'upgrade'` 핸드셰이크는 Fastify 의 onRequest 훅을 타지 않아 `req.session` 이 없다. 그래서
46
+ * WS 라우트의 `before(req)` 인증은 이 helper 로 세션 신원을 확인한다 — {@link registerSession} 의
47
+ * onRequest 가 하는 경로(쿠키 → unsign → `store.load`)를 그대로 재사용하되, rolling touch·쿠키 발급·
48
+ * 변경 저장은 하지 않는다(upgrade 응답엔 set-cookie 를 실을 수 없고, 읽기만 필요하므로).
49
+ *
50
+ * 보안상 동일 규칙을 따른다: 쿠키가 없거나 서명 위조면 `null`(익명), 스토어에 없으면(만료/삭제) `null`.
51
+ * 절대 silent 하게 통과시키지 않는다(fail-closed) — 신원이 없으면 `before` 가 401 로 거부한다.
52
+ *
53
+ * @param {{ headers?: Record<string, any> }} req - raw `IncomingMessage`(또는 `headers.cookie` 를 가진 객체).
54
+ * @param {object} opts
55
+ * @param {import('../adapters/mega-session-adapter.js').MegaSessionAdapter} opts.store - 연결된 세션 스토어(`app.sessionStore`).
56
+ * @param {string} opts.secret - 쿠키 HMAC 서명 시크릿(global `server.sessionSecret`).
57
+ * @param {string} [opts.cookieName] - 세션 쿠키 이름(기본 `'mega.sid'` — {@link registerSession} 디폴트와 일치).
58
+ * @returns {Promise<{ sid: string, data: Record<string, any> } | null>} 유효 세션이면 `{ sid, data }`, 아니면 `null`.
59
+ * @throws {Error} `secret`/`store` 누락 — 서명 검증·로드가 불가하므로 fail-fast(silent 진행 금지).
60
+ * @example
61
+ * // WS before 인증 — 로그인한 세션만 upgrade 허용
62
+ * async function wsRequireAuth(req) {
63
+ * const sess = await readSession(req, { store: app.sessionStore, secret })
64
+ * if (sess?.data.userId == null) return false // 401
65
+ * return { userId: String(sess.data.userId), sessionId: sess.sid } // ctx.auth
66
+ * }
67
+ */
68
+ export function readSession(req: {
69
+ headers?: Record<string, any>;
70
+ }, { store, secret, cookieName }?: {
71
+ store: import("../adapters/mega-session-adapter.js").MegaSessionAdapter;
72
+ secret: string;
73
+ cookieName?: string;
74
+ }): Promise<{
75
+ sid: string;
76
+ data: Record<string, any>;
77
+ } | null>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * 정적 자산 자동 등록 결과 요약(디버그·테스트용).
3
+ * @typedef {Object} StaticAssetsSummary
4
+ * @property {boolean} enabled - 등록 여부.
5
+ * @property {string|null} root - 서빙 루트 절대경로(미등록 시 null).
6
+ * @property {string} prefix - URL prefix.
7
+ */
8
+ /**
9
+ * `staticAssets` config 를 **순수 정규화**한다(파일시스템 접근 없음 — dir 실존 검사는 register 가 담당).
10
+ *
11
+ * - falsy / `enabled !== true` → `null`(미옵트인).
12
+ * - 그 외 → `{ dir, prefix, cacheControlHeader, dotfiles }`(prefix 디폴트 `/static`).
13
+ *
14
+ * `cacheControl`(정본: 문자열 = `Cache-Control` 헤더값, 예 `'public, max-age=3600'`)은 `cacheControlHeader`
15
+ * 로 전달. `dotfiles`(디폴트 false=차단)는 `'allow'`/`'deny'` 로 변환.
16
+ *
17
+ * @param {unknown} staticAssets - `MegaStaticAssetsAppConfig`.
18
+ * @returns {{ dir: string, prefix: string, cacheControlHeader: string|null, dotfiles: 'allow'|'ignore' } | null}
19
+ */
20
+ export function normalizeStaticAssets(staticAssets: unknown): {
21
+ dir: string;
22
+ prefix: string;
23
+ cacheControlHeader: string | null;
24
+ dotfiles: "allow" | "ignore";
25
+ } | null;
26
+ /**
27
+ * Fastify 인스턴스에 `@fastify/static` 을 옵트인 등록한다.
28
+ *
29
+ * `staticAssets.enabled !== true` 면 **미등록**(정적 라우트 없음). 보안 플러그인·템플릿 등록 이후·`/health`
30
+ * 등록 이전에 호출한다(라우트 등록이므로 보안 onRoute hook 뒤여야 면제/제한이 일관). 멀티앱은 각자 Fastify
31
+ * 인스턴스라 prefix 충돌 없음.
32
+ *
33
+ * `enabled:true` 인데 `dir` 누락/미존재면 — 프로덕션 부팅 throw(`config.static_dir_not_found`), dev warn+skip
34
+ * (ADR-071, 04-data-models §3 검증표). `@fastify/static` 자체도 root 미존재 시 throw 하지만, dev 에선 warn 후
35
+ * 건너뛰어 잘못된 dir 하나로 앱 전체가 죽지 않게 한다.
36
+ *
37
+ * @param {import('fastify').FastifyInstance} fastify - 대상 앱 Fastify 인스턴스.
38
+ * @param {Object} opts
39
+ * @param {unknown} opts.staticAssets - `MegaStaticAssetsAppConfig`. `enabled:true` + 실존 `dir` 일 때만 등록.
40
+ * @param {string} [opts.appName] - 앱 이름(로그용).
41
+ * @param {boolean} [opts.isProduction] - 프로덕션 여부. 미지정 시 `NODE_ENV==='production'`.
42
+ * @param {{ debug?: Function, warn?: Function }} [opts.logger] - 흐름 길목 debug/warn 로그(선택).
43
+ * @returns {StaticAssetsSummary}
44
+ * @throws {MegaConfigError} 프로덕션에서 `enabled:true` + `dir` 누락/미존재.
45
+ */
46
+ export function registerStaticAssets(fastify: import("fastify").FastifyInstance, { staticAssets, appName, isProduction, logger }?: {
47
+ staticAssets: unknown;
48
+ appName?: string;
49
+ isProduction?: boolean;
50
+ logger?: {
51
+ debug?: Function;
52
+ warn?: Function;
53
+ };
54
+ }): StaticAssetsSummary;
55
+ /** 정적 자산 prefix 디폴트 (04-data-models §2 정본). */
56
+ export const DEFAULT_STATIC_PREFIX: "/static";
57
+ /**
58
+ * 정적 자산 자동 등록 결과 요약(디버그·테스트용).
59
+ */
60
+ export type StaticAssetsSummary = {
61
+ /**
62
+ * - 등록 여부.
63
+ */
64
+ enabled: boolean;
65
+ /**
66
+ * - 서빙 루트 절대경로(미등록 시 null).
67
+ */
68
+ root: string | null;
69
+ /**
70
+ * - URL prefix.
71
+ */
72
+ prefix: string;
73
+ };