mega-framework 0.1.7 → 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 (76) hide show
  1. package/README.md +9 -0
  2. package/package.json +3 -3
  3. package/sample/crud/.env +9 -0
  4. package/sample/crud/.env.example +9 -0
  5. package/sample/crud/apps/main/locales/server/en.json +12 -1
  6. package/sample/crud/apps/main/locales/server/ko.json +12 -1
  7. package/sample/crud/mega.config.js +7 -0
  8. package/sample/crud/package.json +2 -2
  9. package/sample/crud/scripts/start-ws-hub.sh +18 -4
  10. package/sample/simple/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  11. package/src/adapters/adapter-options.js +14 -3
  12. package/src/adapters/file-adapter.js +9 -5
  13. package/src/adapters/file-session-adapter.js +4 -3
  14. package/src/adapters/maria-adapter.js +7 -4
  15. package/src/adapters/mega-cache-adapter.js +83 -6
  16. package/src/adapters/mega-db-adapter.js +4 -1
  17. package/src/adapters/mongo-adapter.js +21 -7
  18. package/src/adapters/postgres-adapter.js +8 -4
  19. package/src/adapters/redis-adapter.js +7 -3
  20. package/src/adapters/sqlite-adapter.js +6 -2
  21. package/src/cli/commands/console-cmd.js +3 -1
  22. package/src/cli/commands/scaffold.js +38 -2
  23. package/src/cli/generators/index.js +58 -1
  24. package/src/cli/index.js +88 -59
  25. package/src/cli/watch.js +188 -0
  26. package/src/core/ajv-mapper.js +3 -1
  27. package/src/core/ctx-builder.js +59 -1
  28. package/src/core/envelope.js +9 -2
  29. package/src/core/hub-link.js +24 -14
  30. package/src/core/index.js +1 -1
  31. package/src/core/mega-app.js +55 -45
  32. package/src/core/pipeline.js +8 -6
  33. package/src/core/scope-registry.js +1 -0
  34. package/src/core/security.js +3 -3
  35. package/src/core/session-store.js +14 -1
  36. package/src/core/ws-presence.js +17 -5
  37. package/src/core/ws-roster.js +49 -10
  38. package/src/core/ws-upgrade.js +105 -0
  39. package/src/lib/mega-circuit-breaker.js +5 -3
  40. package/src/lib/mega-health.js +10 -0
  41. package/src/lib/mega-job-queue.js +53 -13
  42. package/src/lib/mega-job.js +8 -1
  43. package/src/lib/mega-metrics.js +28 -1
  44. package/src/lib/mega-plugin.js +2 -2
  45. package/src/lib/mega-worker.js +28 -5
  46. package/src/lib/ws-hub.js +90 -9
  47. package/templates/adr/code.tpl +23 -0
  48. package/types/adapters/adapter-options.d.ts +2 -0
  49. package/types/adapters/file-adapter.d.ts +12 -1
  50. package/types/adapters/file-session-adapter.d.ts +4 -2
  51. package/types/adapters/maria-adapter.d.ts +5 -3
  52. package/types/adapters/mega-cache-adapter.d.ts +27 -1
  53. package/types/adapters/mega-db-adapter.d.ts +4 -1
  54. package/types/adapters/mongo-adapter.d.ts +13 -2
  55. package/types/adapters/postgres-adapter.d.ts +4 -2
  56. package/types/adapters/redis-adapter.d.ts +8 -0
  57. package/types/adapters/sqlite-adapter.d.ts +8 -2
  58. package/types/cli/generators/index.d.ts +11 -1
  59. package/types/cli/index.d.ts +12 -27
  60. package/types/cli/watch.d.ts +59 -0
  61. package/types/core/ctx-builder.d.ts +23 -0
  62. package/types/core/hub-link.d.ts +3 -1
  63. package/types/core/index.d.ts +1 -1
  64. package/types/core/mega-app.d.ts +1 -1
  65. package/types/core/pipeline.d.ts +2 -1
  66. package/types/core/security.d.ts +3 -3
  67. package/types/core/session-store.d.ts +7 -0
  68. package/types/core/ws-roster.d.ts +13 -1
  69. package/types/core/ws-upgrade.d.ts +29 -0
  70. package/types/lib/mega-circuit-breaker.d.ts +4 -2
  71. package/types/lib/mega-health.d.ts +7 -0
  72. package/types/lib/mega-job-queue.d.ts +16 -4
  73. package/types/lib/mega-job.d.ts +8 -1
  74. package/types/lib/mega-plugin.d.ts +1 -1
  75. package/types/lib/mega-worker.d.ts +3 -1
  76. package/types/lib/ws-hub.d.ts +27 -2
@@ -12,6 +12,8 @@ export const DEFAULT_RUN_TIMEOUT_MS: number;
12
12
  * 하트비트가 이 값의 절반마다 lease 를 갱신한다.
13
13
  * @property {number} [maxDeliver=5] - JetStream 최대 전달 횟수(워커 크래시 재전달 backstop).
14
14
  * @property {number} [heartbeatMs] - `working()` 전송 주기(ms). 기본 `max(1000, ackWaitMs/2)`.
15
+ * 양의 정수 + `< ackWaitMs` 필수(생성자 fail-fast) — 이상이면 lease 갱신이 늦어 정상 처리 중
16
+ * 중복 재전달, 0 이하면 working() 플러딩.
15
17
  * @property {string} [streamPrefix='MEGA_JOBS'] - 스트림 이름 접두사.
16
18
  * @property {number} [dlqMaxAgeMs=604800000] - DLQ 스트림 메시지 보존 기한(ms, 디폴트 7일). 초과한 실패
17
19
  * 잡은 NATS 가 자동 만료시킨다(무한 적재 방지, ADR-134). `0` 이면 무제한(끔 — 영구 보존). **신규 DLQ
@@ -73,10 +75,18 @@ export class MegaJobQueue extends EventEmitter<any> {
73
75
  */
74
76
  ensureStream(JobClass: typeof MegaJob): Promise<void>;
75
77
  /**
76
- * 잡을 큐에 넣는다(JetStream publish — 서버에 영속 저장). 스트림이 없으면 만든다.
77
- * @param {typeof MegaJob} JobClass @param {any} payload @returns {Promise<{ seq: number, stream: string, duplicate: boolean }>}
78
- */
79
- enqueue(JobClass: typeof MegaJob, payload: any): Promise<{
78
+ * 잡을 큐에 넣는다(JetStream publish — 서버에 영속 저장). 스트림이 없으면 만든다(subject 별 1회 확인 후 캐시).
79
+ *
80
+ * @param {typeof MegaJob} JobClass @param {any} payload
81
+ * @param {{ msgID?: string }} [opts] - `msgID` = JetStream `Nats-Msg-Id` dedup 키(옵트인). 스트림
82
+ * duplicate window(NATS 기본 2분 — 운영자가 NATS CLI 로 스트림별 조정) 안의 같은 msgID 재발행은
83
+ * 적재되지 않고 `duplicate: true` 로 반환된다. 비즈니스 멱등키(주문 ID 등)를 권장. 미지정 시
84
+ * dedup 없음 — `duplicate` 는 항상 false.
85
+ * @returns {Promise<{ seq: number, stream: string, duplicate: boolean }>}
86
+ */
87
+ enqueue(JobClass: typeof MegaJob, payload: any, { msgID }?: {
88
+ msgID?: string;
89
+ }): Promise<{
80
90
  seq: number;
81
91
  stream: string;
82
92
  duplicate: boolean;
@@ -129,6 +139,8 @@ export type MegaJobQueueOptions = {
129
139
  maxDeliver?: number;
130
140
  /**
131
141
  * - `working()` 전송 주기(ms). 기본 `max(1000, ackWaitMs/2)`.
142
+ * 양의 정수 + `< ackWaitMs` 필수(생성자 fail-fast) — 이상이면 lease 갱신이 늦어 정상 처리 중
143
+ * 중복 재전달, 0 이하면 working() 플러딩.
132
144
  */
133
145
  heartbeatMs?: number;
134
146
  /**
@@ -92,7 +92,14 @@ export class MegaJob {
92
92
  static subject: string | undefined;
93
93
  /** @type {string|undefined} bus 별명(`ctx.bus(alias)`). 워커 배선이 nc 해석에 사용. */
94
94
  static bus: string | undefined;
95
- /** @type {number} 동시 처리 메시지 수(consumer max_ack_pending). 기본 1(순차·안전). */
95
+ /**
96
+ * @type {number} 동시 처리 메시지 수. 기본 1(순차·안전).
97
+ *
98
+ * ⚠️ 이 값은 durable consumer 의 `max_ack_pending` 으로 들어가므로 **워커 그룹(같은 subject 를
99
+ * 소비하는 모든 인스턴스) 전체의 합산 in-flight 상한**이다 — `mega worker` 인스턴스를 늘려도
100
+ * 합산 동시 처리는 이 값을 넘지 못한다. 처리량을 늘리려면 인스턴스 증설과 **함께** concurrency 를
101
+ * 키워야 한다(실측: c=1→c=32 에서 처리량 ~10배).
102
+ */
96
103
  static concurrency: number;
97
104
  /** @type {number} run 실패 시 **추가** 재시도 횟수(첫 시도 제외). 기본 3(OQ-012). */
98
105
  static retries: number;
@@ -53,7 +53,7 @@ export const CORE_API_VERSION: string;
53
53
  * @property {Array<{ path: string, template: string }>} files
54
54
  * @property {string} [description] - `mega help` 가 병기하는 한 줄 설명(선택).
55
55
  */
56
- /** 빌트인 generator 13종 이름 — 플러그인 scaffold 가 점유 금지(빌트인이 우선이라 silent shadow 가 됨).
56
+ /** 빌트인 generator 이름 — 플러그인 scaffold 가 점유 금지(빌트인이 우선이라 silent shadow 가 됨).
57
57
  * cli/generators 의 `GENERATOR_KINDS` 와 동일해야 한다(레이어 역전 회피를 위해 미러 — 동기화는
58
58
  * mega-plugin 단위 테스트가 GENERATOR_KINDS 와 집합 비교로 강제한다). */
59
59
  export const RESERVED_GENERATOR_NAMES: Set<string>;
@@ -37,8 +37,10 @@ export class MegaWorker extends EventEmitter<any> {
37
37
  get mode(): "thread" | "process";
38
38
  /** @returns {number} 풀 크기. */
39
39
  get poolSize(): number;
40
- /** @returns {number} crash 교체 허용 총량. */
40
+ /** @returns {number} 윈도우({@link MegaWorker#restartWindowMs}) 내 crash 교체 허용 횟수. */
41
41
  get maxRestarts(): number;
42
+ /** @returns {number} crash 교체 판정 슬라이딩 윈도우(ms). `static restartWindowMs` 로 조정. */
43
+ get restartWindowMs(): number;
42
44
  /** @returns {boolean} start() 후 stop() 전이면 true. */
43
45
  get isStarted(): boolean;
44
46
  /** @param {'dispatch'|'done'|'fail'|'crash'|'stopped'} event @param {(payload: any) => void} listener @returns {this} */
@@ -69,11 +69,12 @@ export class MegaWsHub {
69
69
  _wss: WebSocketServer | null;
70
70
  /** heartbeat liveness 체크 interval (M3). @type {ReturnType<typeof setInterval> | null} */
71
71
  _livenessTimer: ReturnType<typeof setInterval> | null;
72
- /** 등록된 bridge 연결. connId → { socket, lastSeen, protocolVersion }. @type {Map<string, { socket: import('ws').WebSocket, lastSeen: number, protocolVersion?: number }>} */
72
+ /** 등록된 bridge 연결. connId → { socket, lastSeen, protocolVersion, presenceFanout }. @type {Map<string, { socket: import('ws').WebSocket, lastSeen: number, protocolVersion?: number, presenceFanout?: boolean }>} */
73
73
  _bridges: Map<string, {
74
74
  socket: import("ws").WebSocket;
75
75
  lastSeen: number;
76
76
  protocolVersion?: number;
77
+ presenceFanout?: boolean;
77
78
  }>;
78
79
  /** presence. sessionId → { bridgeConnId, userId, channels:Set, metadata }. @type {Map<string, { bridgeConnId: string, userId: string, channels: Set<string>, metadata: Object }>} */
79
80
  _sessions: Map<string, {
@@ -86,6 +87,13 @@ export class MegaWsHub {
86
87
  _channelSessions: Map<string, Set<string>>;
87
88
  /** userId → sessionId 집합 (DIRECT fan-out, ADR-035). @type {Map<string, Set<string>>} */
88
89
  _userSessions: Map<string, Set<string>>;
90
+ /**
91
+ * 역인덱스: channel → (bridgeConnId → 그 bridge 의 채널 내 세션 수). BROADCAST 의 bridge 선정을
92
+ * 세션 수 무관 O(bridge 수)로 만든다 — 멤버 10k 채널에서 메시지당 수백 µs 가 µs 대로 떨어진다.
93
+ * `_addSession`/`_removeSession` 이 증분 유지(카운트 0 → 키 제거), 메모리는 채널×bridge 수준.
94
+ * @type {Map<string, Map<string, number>>}
95
+ */
96
+ _channelBridges: Map<string, Map<string, number>>;
89
97
  /** hub 식별자. */
90
98
  get hubId(): string;
91
99
  /** 등록된 bridge 수 (테스트/관측용). */
@@ -148,6 +156,12 @@ export class MegaWsHub {
148
156
  * @private
149
157
  */
150
158
  private _addSession;
159
+ /**
160
+ * channel→bridge 역인덱스 증분 갱신. 카운트가 0 이 되면 키를 지워 인덱스가 stale 하지 않게 한다.
161
+ * @param {string} channel @param {string} connId @param {1|-1} delta
162
+ * @private
163
+ */
164
+ private _bumpChannelBridge;
151
165
  /**
152
166
  * presence 에서 세션 제거. 빈 인덱스는 정리.
153
167
  * @param {string} sessionId
@@ -162,12 +176,23 @@ export class MegaWsHub {
162
176
  */
163
177
  private _handleBridgeGone;
164
178
  /**
165
- * origin 을 제외한 모든 등록 bridge 로 송신 (presence 공유용). envelope 는 1회만 직렬화(L5).
179
+ * origin 을 제외한 모든 등록 bridge 로 송신. envelope 는 1회만 직렬화(L5).
180
+ * BULK_LEAVE(bridge-gone 정리 통지)처럼 전 bridge 가 받아야 하는 통지에 쓴다.
166
181
  * @param {string} exceptConnId
167
182
  * @param {Object} envelope
168
183
  * @private
169
184
  */
170
185
  private _fanOutToOthers;
186
+ /**
187
+ * presence(JOIN/LEAVE/METADATA) fan-out — `presence-fanout` capability 를 선언한 bridge 에만 송신.
188
+ * roster 가 redis 로 분리(ADR-177)된 뒤 표준 bridge 는 이 타입들을 no-op 으로 버리므로, 옵트인하지
189
+ * 않은 bridge 에는 보내지 않는다 — 세션 churn × bridge 수 만큼의 죽은 프레임을 제거한다.
190
+ * 구버전 bridge 호환: hub presence 를 실제로 쓰려는 bridge 는 capability 로 명시 선언한다.
191
+ * @param {string} exceptConnId
192
+ * @param {Object} envelope
193
+ * @private
194
+ */
195
+ private _fanOutPresence;
171
196
  /**
172
197
  * 한 채널의 세션을 가진 bridge 들로 fan-out (origin 제외, 중복 bridge 1회). 직렬화 1회(L5).
173
198
  *