@futdevpro/nts-dynamo 1.15.89 → 1.15.90

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 (384) hide show
  1. package/.c8rc.json +26 -26
  2. package/.copilot/patterns.json +7 -7
  3. package/.cursor/rules/__assistant_guide.mdc +30 -30
  4. package/.cursor/rules/_ag_backend-structure.mdc +85 -85
  5. package/.cursor/rules/_ag_backend.mdc +16 -16
  6. package/.cursor/rules/_ag_frontend-structure.mdc +86 -86
  7. package/.cursor/rules/_ag_frontend.mdc +39 -39
  8. package/.cursor/rules/_ag_import-rules.mdc +44 -44
  9. package/.cursor/rules/_ag_naming.mdc +115 -115
  10. package/.cursor/rules/_ag_should-be.mdc +6 -6
  11. package/.cursor/rules/ai_development_guide.md +60 -60
  12. package/.cursor/rules/cursor-rules.md +160 -160
  13. package/.cursor/rules/default-command.mdc +464 -464
  14. package/.cursor/rules/error_code_pattern.md +39 -39
  15. package/.cursor/rules/saved rule mcp server use.md +15 -15
  16. package/.dynamo/logs/cicd-pipeline/output.log +2816 -0
  17. package/.dynamo/logs/cicd-pipeline/status.json +94 -0
  18. package/.vscode/settings.json +10 -10
  19. package/HOWTO.md +15 -15
  20. package/LICENSE +21 -21
  21. package/__documentations/nts-integration-tests-2026-03-17.md +26 -26
  22. package/__documentations/plans/BEDROCK-HYPERPLAN.md +95 -95
  23. package/_specifications/BACKLOG.md +92 -92
  24. package/_specifications/TODO.md +15 -15
  25. package/_specifications/agent.md +138 -138
  26. package/build/_modules/scheduler/_models/scheduler-job.interface.d.ts +34 -0
  27. package/build/_modules/scheduler/_models/scheduler-job.interface.d.ts.map +1 -0
  28. package/build/_modules/scheduler/_models/scheduler-job.interface.js +3 -0
  29. package/build/_modules/scheduler/_models/scheduler-job.interface.js.map +1 -0
  30. package/build/_modules/scheduler/get-scheduler-routing-module.util.d.ts +18 -0
  31. package/build/_modules/scheduler/get-scheduler-routing-module.util.d.ts.map +1 -0
  32. package/build/_modules/scheduler/get-scheduler-routing-module.util.js +31 -0
  33. package/build/_modules/scheduler/get-scheduler-routing-module.util.js.map +1 -0
  34. package/build/_modules/scheduler/index.d.ts +5 -0
  35. package/build/_modules/scheduler/index.d.ts.map +1 -0
  36. package/build/_modules/scheduler/index.js +10 -0
  37. package/build/_modules/scheduler/index.js.map +1 -0
  38. package/build/_modules/scheduler/scheduler.controller.d.ts +25 -0
  39. package/build/_modules/scheduler/scheduler.controller.d.ts.map +1 -0
  40. package/build/_modules/scheduler/scheduler.controller.js +54 -0
  41. package/build/_modules/scheduler/scheduler.controller.js.map +1 -0
  42. package/build/_modules/scheduler/scheduler.service.d.ts +54 -0
  43. package/build/_modules/scheduler/scheduler.service.d.ts.map +1 -0
  44. package/build/_modules/scheduler/scheduler.service.js +164 -0
  45. package/build/_modules/scheduler/scheduler.service.js.map +1 -0
  46. package/eslint.config.js +3 -3
  47. package/nodemon.json +24 -24
  48. package/package.json +10 -1
  49. package/pnpm-workspace.yaml +5 -5
  50. package/scripts/run-coverage-tests.js +28 -28
  51. package/spec/support/helpers/spec-reporter-loader.js +359 -359
  52. package/spec/support/helpers/ts-node-helper.js +93 -93
  53. package/spec/support/jasmine.coverage.json +24 -24
  54. package/spec/support/jasmine.json +24 -24
  55. package/src/_collections/archive.util.spec.ts +57 -57
  56. package/src/_collections/archive.util.ts +18 -18
  57. package/src/_collections/atlas-default-db-options.const.ts +9 -9
  58. package/src/_collections/default-fallback-cache-max-age.const.spec.ts +11 -11
  59. package/src/_collections/default-fallback-cache-max-age.const.ts +2 -2
  60. package/src/_collections/default-not-found-page.const.spec.ts +19 -19
  61. package/src/_collections/default-not-found-page.const.ts +22 -22
  62. package/src/_collections/default-socket-path.const.spec.ts +12 -12
  63. package/src/_collections/default-socket-path.const.ts +2 -2
  64. package/src/_collections/get-environment-settings.util.spec.ts +210 -210
  65. package/src/_collections/get-environment-settings.util.ts +48 -48
  66. package/src/_collections/global-settings.const.ts +109 -109
  67. package/src/_collections/sample.env +21 -21
  68. package/src/_collections/star.controller.spec.ts +224 -224
  69. package/src/_collections/star.controller.ts +129 -129
  70. package/src/_enums/data-model-type.enum.ts +14 -14
  71. package/src/_enums/data-service-function.enum.ts +24 -24
  72. package/src/_enums/predefined-data-types.enum.ts +16 -16
  73. package/src/_enums/route-security.enum.ts +12 -12
  74. package/src/_models/control-models/api-call-params.control-model.spec.ts +152 -152
  75. package/src/_models/control-models/api-call-params.control-model.ts +142 -142
  76. package/src/_models/control-models/app-ext-system-controls.control-model.spec.ts +52 -52
  77. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  78. package/src/_models/control-models/app-params.control-model.spec.ts +225 -225
  79. package/src/_models/control-models/app-params.control-model.ts +136 -136
  80. package/src/_models/control-models/app-system-controls.control-model.spec.ts +31 -31
  81. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  82. package/src/_models/control-models/endpoint-params.control-model.spec.ts +627 -627
  83. package/src/_models/control-models/endpoint-params.control-model.ts +627 -627
  84. package/src/_models/control-models/http-settings.control-model.spec.ts +77 -77
  85. package/src/_models/control-models/http-settings.control-model.ts +37 -37
  86. package/src/_models/control-models/system-control.control-model.spec.ts +27 -27
  87. package/src/_models/control-models/system-control.control-model.ts +12 -12
  88. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  89. package/src/_models/interfaces/environment-settings.interface.ts +59 -59
  90. package/src/_models/interfaces/global-log-settings.interface.ts +171 -171
  91. package/src/_models/interfaces/global-service-settings.interface.ts +47 -47
  92. package/src/_models/interfaces/global-settings.interface.ts +244 -244
  93. package/src/_models/interfaces/routing-module-settings.interface.ts +21 -21
  94. package/src/_models/interfaces/static-client-settings.interface.spec.ts +29 -29
  95. package/src/_models/interfaces/static-client-settings.interface.ts +28 -28
  96. package/src/_models/types/db-update.type.ts +100 -100
  97. package/src/_modules/ai/_models/ai-input-interfaces.ts +117 -117
  98. package/src/_modules/ai/_models/ai-test-generation-result.interface.ts +16 -16
  99. package/src/_modules/ai/_modules/anthropic/_services/aai-user-key.control-service.ts +138 -138
  100. package/src/_modules/ai/_modules/anthropic/index.ts +5 -5
  101. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.spec.ts +242 -242
  102. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.ts +639 -639
  103. package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.spec.ts +295 -295
  104. package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.ts +518 -518
  105. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.spec.ts +209 -209
  106. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.ts +85 -85
  107. package/src/_modules/ai/_modules/document-ai/_enums/dai-compare-result-type.enum.ts +7 -7
  108. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-chunk.data-model.ts +146 -146
  109. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-page.data-model.ts +162 -162
  110. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-document.data-model.ts +99 -99
  111. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-code-chunk.interface.ts +68 -68
  112. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-chunk-compare-result.interface.ts +18 -18
  113. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-page-compare-result.interface.ts +19 -19
  114. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-document-compare-result.interface.ts +25 -25
  115. package/src/_modules/ai/_modules/document-ai/index.ts +30 -30
  116. package/src/_modules/ai/_modules/fdp-ai/_services/fdpai-user-key.control-service.ts +189 -189
  117. package/src/_modules/ai/_modules/fdp-ai/index.ts +5 -5
  118. package/src/_modules/ai/_modules/open-ai/_collections/oai-global-settings.const.ts +9 -9
  119. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests-hu.conts.ts +82 -82
  120. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests.conts.ts +75 -75
  121. package/src/_modules/ai/_modules/open-ai/_enums/oai-gpt-message-role.enum.ts +45 -45
  122. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-global-settings.interface.ts +7 -7
  123. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-gpt-message.interface.ts +7 -7
  124. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-llm-predefined-requests.interface.ts +57 -57
  125. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-doc-chunk-data.service.ts +292 -292
  126. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-document.data-service.spec.ts +342 -342
  127. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.spec.ts +550 -550
  128. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.ts +630 -630
  129. package/src/_modules/ai/_modules/open-ai/_services/oai-embedding.control-service.spec.ts +332 -332
  130. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.spec.ts +462 -462
  131. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.ts +634 -634
  132. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.spec.ts +489 -489
  133. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.tools.spec.ts +173 -173
  134. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +1033 -1033
  135. package/src/_modules/ai/_modules/open-ai/_services/oai-user-key.control-service.ts +157 -157
  136. package/src/_modules/ai/_services/ai-embedding-mock.service.spec.ts +115 -115
  137. package/src/_modules/ai/_services/ai-embedding-mock.service.ts +212 -212
  138. package/src/_modules/ai/_services/ai-embedding-provider.registry.spec.ts +110 -110
  139. package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +110 -110
  140. package/src/_modules/ai/_services/ai-embedding.service-base.spec.ts +98 -98
  141. package/src/_modules/ai/_services/ai-embedding.service-base.ts +48 -48
  142. package/src/_modules/ai/_services/ai-llm-chat.service-base.spec.ts +229 -229
  143. package/src/_modules/ai/_services/ai-llm-chat.service-base.ts +68 -68
  144. package/src/_modules/ai/_services/ai-llm.service-base.spec.ts +250 -250
  145. package/src/_modules/ai/_services/ai-llm.service-base.ts +519 -519
  146. package/src/_modules/ai/_services/ai-provider.service-base.spec.ts +158 -158
  147. package/src/_modules/ai/_services/ai-user-key.service-base.ts +59 -59
  148. package/src/_modules/ai/_services/lmstudio-embedding.control-service.spec.ts +197 -197
  149. package/src/_modules/ai/_services/lmstudio-embedding.control-service.ts +371 -371
  150. package/src/_modules/ai/index.ts +23 -23
  151. package/src/_modules/assistant/_collections/ass-global-settings.const.ts +13 -13
  152. package/src/_modules/assistant/_collections/ass.util.spec.ts +176 -176
  153. package/src/_modules/assistant/_collections/ass.util.ts +50 -50
  154. package/src/_modules/assistant/_models/ass-global-settings.interface.ts +15 -15
  155. package/src/_modules/assistant/_services/ass-io.control-service.spec.ts +140 -140
  156. package/src/_modules/assistant/_services/ass-main.control-service.spec.ts +192 -192
  157. package/src/_modules/assistant/_services/ass-main.control-service.ts +107 -107
  158. package/src/_modules/bot/_collections/bot-default-commands.const.ts +12 -12
  159. package/src/_modules/bot/_collections/bot-global-settings.const.ts +39 -39
  160. package/src/_modules/bot/_models/bot-channel-wrapper.interface.ts +62 -62
  161. package/src/_modules/bot/_models/bot-command.interface.ts +8 -8
  162. package/src/_modules/bot/_models/bot-global-settings.interface.ts +96 -96
  163. package/src/_modules/bot/_models/bot-last-mention-date.interface.ts +6 -6
  164. package/src/_modules/bot/_models/bot-last-message-date.interface.ts +5 -5
  165. package/src/_modules/bot/_models/bot-user-wrapper.interface.ts +41 -41
  166. package/src/_modules/bot/_modules/discord-bot/_models/dib-platform.types.ts +9 -9
  167. package/src/_modules/bot/_modules/discord-bot/_services/dib-messaging-provider.control-service.spec.ts +431 -431
  168. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.spec.ts +160 -160
  169. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.ts +55 -55
  170. package/src/_modules/bot/_modules/dynamo-bot/_models/dyb-platform.types.ts +15 -15
  171. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.spec.ts +374 -374
  172. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.ts +447 -447
  173. package/src/_modules/bot/_modules/dynamo-bot/index.ts +15 -15
  174. package/src/_modules/bot/_modules/slack-bot/_models/slb-platform.types.ts +9 -9
  175. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.spec.ts +344 -344
  176. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.ts +197 -197
  177. package/src/_modules/bot/_modules/teams-bot/_models/teb-platform.types.ts +9 -9
  178. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.spec.ts +345 -345
  179. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.ts +197 -197
  180. package/src/_modules/bot/_services/bot-commands.control-service.spec.ts +116 -116
  181. package/src/_modules/bot/_services/bot-io.control-service.spec.ts +285 -285
  182. package/src/_modules/bot/_services/bot-main.control-service.spec.ts +208 -208
  183. package/src/_modules/bot/_services/bot-messaging-provider.service-base.spec.ts +349 -349
  184. package/src/_modules/bot/_services/bot-routines.control-service.spec.ts +111 -111
  185. package/src/_modules/custom-data/custom-data.controller.spec.ts +49 -49
  186. package/src/_modules/custom-data/custom-data.controller.ts +67 -67
  187. package/src/_modules/custom-data/custom-data.data-service.spec.ts +54 -54
  188. package/src/_modules/custom-data/custom-data.data-service.ts +21 -21
  189. package/src/_modules/custom-data/get-custom-data-routing-module.util.spec.ts +28 -28
  190. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +24 -24
  191. package/src/_modules/custom-data/index.ts +9 -9
  192. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.spec.ts +161 -161
  193. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.ts +203 -203
  194. package/src/_modules/data-readers/_models/interfaces/dynts-sqlite-reader.interface.ts +33 -33
  195. package/src/_modules/data-readers/index.ts +11 -11
  196. package/src/_modules/defaults/_collections/default-endpoints.util.ts +487 -487
  197. package/src/_modules/defaults/_models/default-user.data-model.ts +72 -72
  198. package/src/_modules/defaults/_services/default-auth.service.spec.ts +269 -269
  199. package/src/_modules/defaults/_services/default-auth.service.ts +177 -177
  200. package/src/_modules/defaults/_services/default-socket-events.service.spec.ts +42 -42
  201. package/src/_modules/defaults/_services/default-socket-events.service.ts +61 -61
  202. package/src/_modules/defaults/_services/default-user.data-service.spec.ts +187 -187
  203. package/src/_modules/defaults/_services/default-user.data-service.ts +98 -98
  204. package/src/_modules/defaults/index.ts +17 -17
  205. package/src/_modules/discord-assistant/_collections/dias-global-settings.const.ts +19 -19
  206. package/src/_modules/discord-assistant/_collections/dias.util.spec.ts +366 -366
  207. package/src/_modules/discord-assistant/_collections/dias.util.ts +132 -132
  208. package/src/_modules/discord-assistant/_models/dias-global-settings.interface.ts +19 -19
  209. package/src/_modules/discord-assistant/_models/dias-knowledge.data-model.ts +52 -52
  210. package/src/_modules/discord-assistant/_services/dias-chunk.data-service.ts +177 -177
  211. package/src/_modules/discord-assistant/_services/dias-io.control-service.spec.ts +108 -108
  212. package/src/_modules/discord-assistant/_services/dias-io.control-service.ts +69 -69
  213. package/src/_modules/discord-assistant/_services/dias-main.control-service.spec.ts +22 -22
  214. package/src/_modules/discord-assistant/_services/dias-main.control-service.ts +27 -27
  215. package/src/_modules/discord-assistant/_services/dias.service-base.spec.ts +195 -195
  216. package/src/_modules/discord-assistant/_services/dias.service-base.ts +76 -76
  217. package/src/_modules/discord-assistant/index.ts +38 -38
  218. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.spec.ts +34 -34
  219. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.ts +11 -11
  220. package/src/_modules/discord-assistant-voiced/index.ts +36 -36
  221. package/src/_modules/discord-bot/_collections/dibo-default-commands.const.ts +16 -16
  222. package/src/_modules/discord-bot/_collections/dibo-global-settings.conts.ts +55 -55
  223. package/src/_modules/discord-bot/_collections/dibo-operations.util.spec.ts +214 -214
  224. package/src/_modules/discord-bot/_collections/dibo-operations.util.ts +387 -387
  225. package/src/_modules/discord-bot/_models/dibo-command.interface.ts +12 -12
  226. package/src/_modules/discord-bot/_models/dibo-global-settings.interface.ts +98 -98
  227. package/src/_modules/discord-bot/_models/dibo-last-mention-date.inteface.ts +7 -7
  228. package/src/_modules/discord-bot/_models/dibo-last-message-date.interface.ts +6 -6
  229. package/src/_modules/discord-bot/_services/dibo-commands.control-service.spec.ts +154 -154
  230. package/src/_modules/discord-bot/_services/dibo-commands.control-service.ts +153 -153
  231. package/src/_modules/discord-bot/_services/dibo-io.control-service.spec.ts +264 -264
  232. package/src/_modules/discord-bot/_services/dibo-io.control-service.ts +306 -306
  233. package/src/_modules/discord-bot/_services/dibo-main.control-service.spec.ts +408 -408
  234. package/src/_modules/discord-bot/_services/dibo-main.control-service.ts +487 -487
  235. package/src/_modules/discord-bot/_services/dibo-routines.control-service.spec.ts +105 -105
  236. package/src/_modules/discord-bot/index.ts +36 -36
  237. package/src/_modules/local-vector-search/_enums/lvs-search-mode.enum.ts +35 -35
  238. package/src/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.ts +59 -59
  239. package/src/_modules/local-vector-search/_models/lvs-search-result.interface.ts +17 -17
  240. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.spec.ts +418 -418
  241. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.ts +276 -276
  242. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.spec.ts +480 -480
  243. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.ts +416 -416
  244. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +198 -198
  245. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +146 -146
  246. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.spec.ts +167 -167
  247. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +106 -106
  248. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.spec.ts +507 -507
  249. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.ts +272 -272
  250. package/src/_modules/local-vector-search/index.ts +16 -16
  251. package/src/_modules/logs/index.ts +11 -11
  252. package/src/_modules/mcp/_models/interfaces/dynts-mcp.interface.ts +111 -111
  253. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.spec.ts +142 -142
  254. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.ts +120 -120
  255. package/src/_modules/mcp/_services/dynts-mcp.adapter.ts +168 -168
  256. package/src/_modules/mcp/index.ts +13 -13
  257. package/src/_modules/messaging/README.md +354 -354
  258. package/src/_modules/messaging/_collections/get-messaging-routing-module.util.ts +26 -26
  259. package/src/_modules/messaging/_collections/msg-global-settings.const.ts +22 -22
  260. package/src/_modules/messaging/_collections/msg.util.spec.ts +226 -226
  261. package/src/_modules/messaging/_models/msg-global-settings.interface.ts +37 -37
  262. package/src/_modules/messaging/_services/msg-conversation.data-service.ts +146 -146
  263. package/src/_modules/messaging/_services/msg-events.service.spec.ts +219 -219
  264. package/src/_modules/messaging/_services/msg-events.service.ts +267 -267
  265. package/src/_modules/messaging/_services/msg-integration.control-service.ts +179 -179
  266. package/src/_modules/messaging/_services/msg-main.control-service.spec.ts +147 -147
  267. package/src/_modules/messaging/_services/msg-main.control-service.ts +571 -571
  268. package/src/_modules/messaging/_services/msg-message.data-service.ts +129 -129
  269. package/src/_modules/messaging/_services/msg.controller.spec.ts +201 -201
  270. package/src/_modules/messaging/index.ts +30 -30
  271. package/src/_modules/mock/app-extended-server.mock.ts +201 -201
  272. package/src/_modules/mock/app-integration-test.mock.ts +51 -51
  273. package/src/_modules/mock/app-params.mock.spec.ts +21 -21
  274. package/src/_modules/mock/app-params.mock.ts +9 -9
  275. package/src/_modules/mock/app-server.mock.ts +188 -188
  276. package/src/_modules/mock/auth-service.mock.spec.ts +47 -47
  277. package/src/_modules/mock/auth-service.mock.ts +28 -28
  278. package/src/_modules/mock/controller.mock.spec.ts +26 -26
  279. package/src/_modules/mock/controller.mock.ts +16 -16
  280. package/src/_modules/mock/data-model.mock.spec.ts +111 -111
  281. package/src/_modules/mock/data-model.mock.ts +82 -82
  282. package/src/_modules/mock/email-service-collection.mock.spec.ts +24 -24
  283. package/src/_modules/mock/email-service-collection.mock.ts +15 -15
  284. package/src/_modules/mock/email-service.mock.spec.ts +17 -17
  285. package/src/_modules/mock/email-service.mock.ts +20 -20
  286. package/src/_modules/mock/email-template.mock.html +14 -14
  287. package/src/_modules/mock/endpoint.mock.ts +91 -91
  288. package/src/_modules/mock/socket-client.mock.spec.ts +40 -40
  289. package/src/_modules/mock/socket-client.mock.ts +45 -45
  290. package/src/_modules/mock/socket-server.mock.spec.ts +44 -44
  291. package/src/_modules/mock/socket-server.mock.ts +46 -46
  292. package/src/_modules/oauth2/_routes/oauth2.controller.spec.ts +107 -107
  293. package/src/_modules/oauth2/_routes/oauth2.controller.ts +98 -98
  294. package/src/_modules/oauth2/_services/oauth2.auth-service.spec.ts +254 -254
  295. package/src/_modules/oauth2/_services/oauth2.auth-service.ts +232 -232
  296. package/src/_modules/oauth2/_services/oauth2.control-service.spec.ts +585 -585
  297. package/src/_modules/oauth2/_services/oauth2.control-service.ts +653 -653
  298. package/src/_modules/oauth2/index.ts +17 -17
  299. package/src/_modules/scheduler/_models/scheduler-job.interface.ts +35 -0
  300. package/src/_modules/scheduler/get-scheduler-routing-module.util.ts +33 -0
  301. package/src/_modules/scheduler/index.ts +8 -0
  302. package/src/_modules/scheduler/scheduler.controller.spec.ts +42 -0
  303. package/src/_modules/scheduler/scheduler.controller.ts +69 -0
  304. package/src/_modules/scheduler/scheduler.service.spec.ts +141 -0
  305. package/src/_modules/scheduler/scheduler.service.ts +176 -0
  306. package/src/_modules/scoped-config/_enums/dynts-scoped-config-level.enum.ts +22 -22
  307. package/src/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.ts +81 -81
  308. package/src/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.ts +107 -107
  309. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.spec.ts +306 -306
  310. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.ts +295 -295
  311. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.spec.ts +118 -118
  312. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.ts +105 -105
  313. package/src/_modules/scoped-config/index.ts +17 -17
  314. package/src/_modules/server/errors/errors.control-service.spec.ts +238 -238
  315. package/src/_modules/server/errors/errors.control-service.ts +100 -100
  316. package/src/_modules/server/errors/errors.controller.spec.ts +268 -268
  317. package/src/_modules/server/errors/errors.controller.ts +515 -515
  318. package/src/_modules/server/errors/errors.data-service.spec.ts +480 -480
  319. package/src/_modules/server/index.ts +30 -30
  320. package/src/_modules/server/server-status/server-status-snapshot.control-service.spec.ts +70 -70
  321. package/src/_modules/server/server-status/server-status-snapshot.control-service.ts +17 -17
  322. package/src/_modules/server/server-status/server-status-snapshot.data-service.spec.ts +77 -77
  323. package/src/_modules/server/server-status/server-status-snapshot.data-service.ts +37 -37
  324. package/src/_modules/server/server-status/server-status.control-service.spec.ts +576 -576
  325. package/src/_modules/server/server-status/server-status.control-service.ts +396 -396
  326. package/src/_modules/server/server-status/server-status.controller.spec.ts +255 -255
  327. package/src/_modules/server/server-status/server-status.controller.ts +272 -272
  328. package/src/_modules/socket/_enums/socket-security.enum.ts +11 -11
  329. package/src/_modules/socket/_models/socket-client-service-params.control-model.spec.ts +32 -32
  330. package/src/_modules/socket/_models/socket-client-service-params.control-model.ts +22 -22
  331. package/src/_modules/socket/_models/socket-presence.control-model.spec.ts +164 -164
  332. package/src/_modules/socket/_models/socket-presence.control-model.ts +210 -210
  333. package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +46 -46
  334. package/src/_modules/socket/_models/socket-server-service-params.control-model.ts +22 -22
  335. package/src/_modules/socket/_services/socket-client.service.spec.ts +15 -15
  336. package/src/_modules/socket/_services/socket-client.service.ts +260 -260
  337. package/src/_modules/socket/_services/socket-server.service.spec.ts +11 -11
  338. package/src/_modules/socket/app-extended.integration.spec.ts +85 -85
  339. package/src/_modules/socket/app-extended.server.ts +630 -630
  340. package/src/_modules/socket/index.ts +42 -42
  341. package/src/_modules/test/get-test-routing-module.util.spec.ts +28 -28
  342. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  343. package/src/_modules/test/index.ts +11 -11
  344. package/src/_modules/test/test.controller.spec.ts +72 -72
  345. package/src/_modules/test/test.controller.ts +115 -115
  346. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  347. package/src/_modules/usage/index.ts +15 -15
  348. package/src/_modules/usage/usage.controller.spec.ts +81 -81
  349. package/src/_modules/usage/usage.controller.ts +126 -126
  350. package/src/_modules/usage/usage.data-service.spec.ts +332 -332
  351. package/src/_modules/usage/usage.data-service.ts +185 -185
  352. package/src/_services/base/api.service-base.spec.ts +125 -125
  353. package/src/_services/base/api.service-base.ts +74 -74
  354. package/src/_services/base/archive-data.service.spec.ts +209 -209
  355. package/src/_services/base/archive-data.service.ts +224 -224
  356. package/src/_services/base/data.service.spec.ts +729 -729
  357. package/src/_services/base/data.service.ts +2740 -2740
  358. package/src/_services/base/db.service.spec.ts +73 -73
  359. package/src/_services/base/db.service.ts +1575 -1575
  360. package/src/_services/base/singleton.service-base.spec.ts +28 -28
  361. package/src/_services/base/singleton.service-base.ts +24 -24
  362. package/src/_services/base/singleton.service.spec.ts +114 -114
  363. package/src/_services/base/singleton.service.ts +38 -38
  364. package/src/_services/core/api.service.spec.ts +140 -140
  365. package/src/_services/core/auth.service.spec.ts +159 -159
  366. package/src/_services/core/auth.service.ts +174 -174
  367. package/src/_services/core/email.service.spec.ts +85 -85
  368. package/src/_services/core/email.service.ts +742 -742
  369. package/src/_services/core/global.service.spec.ts +292 -292
  370. package/src/_services/core/global.service.ts +487 -487
  371. package/src/_services/core/memory-guard.service.spec.ts +245 -245
  372. package/src/_services/core/memory-guard.service.ts +481 -481
  373. package/src/_services/core/service-collection.service.spec.ts +46 -46
  374. package/src/_services/core/service-collection.service.ts +6 -6
  375. package/src/_services/route/controller.service.spec.ts +53 -53
  376. package/src/_services/route/controller.service.ts +148 -148
  377. package/src/_services/route/routing-module.service.spec.ts +98 -98
  378. package/src/_services/route/routing-module.service.ts +330 -330
  379. package/src/_services/server/app.server.ts +1941 -1941
  380. package/src/_services/shared.static-service.spec.ts +99 -99
  381. package/src/_services/shared.static-service.ts +78 -78
  382. package/src/index.ts +97 -97
  383. package/tsconfig.app.json +12 -12
  384. package/tsconfig.json +42 -42
@@ -1,481 +1,481 @@
1
- import * as v8 from 'v8';
2
- import { PerformanceObserver } from 'perf_hooks';
3
-
4
- import { DyFM_Error, DyFM_ErrorLevel, DyFM_Log } from '@futdevpro/fsm-dynamo';
5
-
6
- import { DyNTS_global_settings } from '../../_collections/global-settings.const';
7
- import { DyNTS_SingletonServiceBase } from '../base/singleton.service-base';
8
- import { DyNTS_GlobalService } from './global.service';
9
-
10
- /**
11
- * FR-193 (2026-06-15) — bedrock OOM korai-figyelmeztető heap-watchdog.
12
- *
13
- * MIÉRT: a Node heap-OOM (`--max-old-space-size` túllépés, pl. egy óriási JSON.parse)
14
- * a process-t AZONNAL megöli — `process.on('uncaughtException')` NEM kapja el, így a
15
- * crash pillanatában már nincs mód a hibát rögzíteni. Az EGYETLEN megbízható idő a
16
- * rögzítésre a crash ELŐTT van. E watchdog periodikusan figyeli a heap-kihasználtságot,
17
- * és amikor egy konfigurálható küszöböt átlép, TARTÓS rekordot hagy (DyFM_Log +
18
- * `DyNTS_GlobalService.globalErrorHandler` → az Errors-sink, amit minden szerver megkap)
19
- * — így egy OOM-hajlamos szerver NYOMOT hagy mielőtt elszáll, és a kuszob-atlepés
20
- * lathato a dashboard-on / Discord-on.
21
- *
22
- * MIT NEM csinál: NEM állítja le/újraindítja a process-t (az a konténer-szintű
23
- * `restart:` policy dolga), és NEM próbálja elkapni a fatal OOM-ot (nem lehet). Tisztán
24
- * MEGFIGYELÉS + RÖGZÍTÉS + opcionális `onCritical` hook (amivel a fogyasztó terhet dobhat).
25
- *
26
- * Hiszterézis: a küszöböket CSAK állapot-VÁLTÁSkor jelzi (normal→warning→critical), és
27
- * a `recoveryMargin`-nyit a warning ALÁ esve jelez 'recovered'-et — így nem spamel a
28
- * küszöb körül oszcilláló heap.
29
- *
30
- * Használat (opt-in, mint a `DyNTS_Logs_Service`):
31
- * DyNTS_MemoryGuard.getInstance().install(); // default settings
32
- * DyNTS_MemoryGuard.getInstance().install({ onCritical }); // custom hook
33
- * VAGY automatikusan: a base `App` startup feltelepíti, ha
34
- * `DyNTS_global_settings.memoryGuard.enabled === true` (alapértelmezés szerint igen).
35
- */
36
-
37
- /** Egy MemoryGuard esemény — küszöb-átlépés vagy helyreállás. */
38
- export interface DyNTS_MemoryGuard_Event {
39
- /** `warning` / `critical` küszöb-átlépés, vagy `recovered` (vissza normal-ba). */
40
- level: 'warning' | 'critical' | 'recovered';
41
- /** Heap használat MB-ban (`process.memoryUsage().heapUsed`). */
42
- heapUsedMb: number;
43
- /** Heap plafon MB-ban (`v8.getHeapStatistics().heap_size_limit` — a `--max-old-space-size`). */
44
- heapLimitMb: number;
45
- /** heapUsed / heapLimit * 100 (egész %-ra kerekítve). */
46
- heapPct: number;
47
- /** Resident Set Size MB-ban (teljes process-memória, heap + non-heap). */
48
- rssMb: number;
49
- /** External (C++ / Buffer) memória MB-ban. */
50
- externalMb: number;
51
- /** A közelmúlt GC-hányada (GC-ben töltött idő / poll-ablak; 0..1) — a mark-compact-thrash / OOM-precursor jelzője. */
52
- gcFraction: number;
53
- /** Mi váltotta ki az esemény-szintet: a heap-% küszöb, a GC-thrash, vagy mindkettő. */
54
- trigger: 'heap' | 'gc' | 'heap+gc' | 'recovery';
55
- /** Esemény időbélyege (ISO). */
56
- at: string;
57
- }
58
-
59
- /** `install()` opcionális override-jai (a `DyNTS_global_settings.memoryGuard` FÖLÉ). */
60
- export interface DyNTS_MemoryGuard_Config {
61
- /** Poll-intervallum ms-ben. Default: 10000. */
62
- pollIntervalMs?: number;
63
- /** Warning küszöb a heap-plafon %-ában. Default: 85. */
64
- heapWarningThreshold?: number;
65
- /** Critical küszöb a heap-plafon %-ában. Default: 95. */
66
- heapCriticalThreshold?: number;
67
- /** A warning küszöb ALATTI margó %-ban a 'recovered'-hez (hiszterézis). Default: 10. */
68
- recoveryMargin?: number;
69
- /** Megőrzött események max száma (ring-buffer). Default: 100. */
70
- maxHistoryCount?: number;
71
- /** GC-hányad-küszöb a `warning`-hoz (0..1). Default: 0.40. */
72
- gcWarningFraction?: number;
73
- /** GC-hányad-küszöb a `critical`-hoz (0..1). Default: 0.60. */
74
- gcCriticalFraction?: number;
75
- /** `uncaughtException` crash-handler telepítése (teljes-részletű error-entry + tiszta exit). Default: true. */
76
- installCrashHandlers?: boolean;
77
- /** Tartós critical → tiszta exit (a wrapper újraindít). Default: false. */
78
- exitOnSustainedCritical?: boolean;
79
- /** Hány egymást követő critical poll után lép ki. Default: 3. */
80
- sustainedCriticalPolls?: number;
81
- /** A graceful-exit kódja. Default: 137. */
82
- exitCode?: number;
83
- /** Boot-grace ms az `exitOnSustainedCritical`-hez (a guard-install utáni ennyi ms-ben NEM lép ki). Default: 60000. */
84
- bootGraceMs?: number;
85
- /** Hook, amit a `critical` küszöb átlépésekor hívunk (pl. terhelés-dobás). */
86
- onCritical?: (event: DyNTS_MemoryGuard_Event) => void;
87
- }
88
-
89
- type DyNTS_MemoryGuard_State = 'normal' | 'warning' | 'critical';
90
-
91
- const BYTES_PER_MB: number = 1024 * 1024;
92
-
93
- export class DyNTS_MemoryGuard extends DyNTS_SingletonServiceBase {
94
-
95
- static getInstance(): DyNTS_MemoryGuard {
96
- return DyNTS_MemoryGuard.getSingletonInstance() as DyNTS_MemoryGuard;
97
- }
98
-
99
- private timer: ReturnType<typeof setInterval> | null = null;
100
- private installed: boolean = false;
101
- private state: DyNTS_MemoryGuard_State = 'normal';
102
-
103
- // Feloldott (effektív) konfiguráció — install()-kor töltjük.
104
- private pollIntervalMs: number = 10000;
105
- private warnPct: number = 85;
106
- private critPct: number = 95;
107
- private recoveryMargin: number = 10;
108
- private maxHistoryCount: number = 100;
109
- private gcWarnFraction: number = 0.40;
110
- private gcCritFraction: number = 0.60;
111
- private exitOnSustainedCritical: boolean = false;
112
- private sustainedCriticalPolls: number = 3;
113
- private exitCode: number = 137;
114
- private bootGraceMs: number = 60000;
115
- /** A guard-install időbélyege (a boot-grace számításához; 0 = még nem telepített). */
116
- private installedAt: number = 0;
117
- private onCritical?: (event: DyNTS_MemoryGuard_Event) => void;
118
-
119
- private readonly history: DyNTS_MemoryGuard_Event[] = [];
120
- private peakHeapPct: number = 0;
121
- private peakRssMb: number = 0;
122
-
123
- // GC-thrash követés (perf_hooks 'gc') — a GC-ben töltött ms gördülő ablaka + a critical-streak számláló.
124
- private gcObserver: PerformanceObserver | null = null;
125
- private gcEvents: { at: number; ms: number }[] = [];
126
- private criticalStreak: number = 0;
127
- // A telepített crash-handler ref-ek (a teardown eltávolításához).
128
- private crashHandler: ((error: Error) => void) | null = null;
129
-
130
- protected constructor() {
131
- super();
132
- }
133
-
134
- /**
135
- * Elindítja a heap-watchdogot. Idempotens (többszöri hívás no-op). A `config`
136
- * felülírja a `DyNTS_global_settings.memoryGuard` értékeit, ami a beépített
137
- * defaultokat írja felül. SOHA nem dob — egy figyelő-réteg nem dönthet be egy
138
- * szervert (minden hiba try/catch-elt + descriptive-en logolt).
139
- */
140
- install(config?: DyNTS_MemoryGuard_Config): void {
141
- if (this.installed) { return; }
142
-
143
- try {
144
- const g: NonNullable<typeof DyNTS_global_settings.memoryGuard> =
145
- DyNTS_global_settings.memoryGuard ?? { enabled: true };
146
-
147
- this.pollIntervalMs = config?.pollIntervalMs ?? g.pollIntervalMs ?? 10000;
148
- this.warnPct = config?.heapWarningThreshold ?? g.heapWarningThreshold ?? 85;
149
- this.critPct = config?.heapCriticalThreshold ?? g.heapCriticalThreshold ?? 95;
150
- this.recoveryMargin = config?.recoveryMargin ?? g.recoveryMargin ?? 10;
151
- this.maxHistoryCount = config?.maxHistoryCount ?? g.maxHistoryCount ?? 100;
152
- this.gcWarnFraction = config?.gcWarningFraction ?? g.gcWarningFraction ?? 0.40;
153
- this.gcCritFraction = config?.gcCriticalFraction ?? g.gcCriticalFraction ?? 0.60;
154
- this.exitOnSustainedCritical = config?.exitOnSustainedCritical ?? g.exitOnSustainedCritical ?? false;
155
- this.sustainedCriticalPolls = config?.sustainedCriticalPolls ?? g.sustainedCriticalPolls ?? 3;
156
- this.exitCode = config?.exitCode ?? g.exitCode ?? 137;
157
- this.bootGraceMs = config?.bootGraceMs ?? g.bootGraceMs ?? 60000;
158
- this.installedAt = Date.now();
159
- this.onCritical = config?.onCritical;
160
-
161
- this.installed = true;
162
- this.installGcObserver();
163
- // Crash-handler (uncaughtException) — teljes-részletű error-entry a crash ELŐTT, majd tiszta exit.
164
- if (config?.installCrashHandlers ?? g.installCrashHandlers ?? true) {
165
- this.installCrashHandlers();
166
- }
167
- this.timer = setInterval((): void => { this.poll(); }, this.pollIntervalMs);
168
- // unref(): a watchdog-timer NE tartsa életben a process-t önmagában (graceful
169
- // exit-kor a Node ki tud lépni a függő interval ellenére is).
170
- if (typeof this.timer.unref === 'function') { this.timer.unref(); }
171
-
172
- DyFM_Log.info(
173
- `[DyNTS_MemoryGuard] installed — poll ${this.pollIntervalMs}ms, ` +
174
- `warn ${this.warnPct}%/${Math.round(this.gcWarnFraction * 100)}%GC, ` +
175
- `crit ${this.critPct}%/${Math.round(this.gcCritFraction * 100)}%GC (heap limit ` +
176
- `${Math.round(this.getHeapLimitBytes() / BYTES_PER_MB)}MB)` +
177
- (this.exitOnSustainedCritical ? `, graceful-exit@${this.sustainedCriticalPolls} critical polls (boot-grace ${this.bootGraceMs}ms)` : ''),
178
- );
179
- } catch (err: unknown) {
180
- // Telepítési hiba SEM lehet fatal — a guard hiánya nem ér annyit, hogy a
181
- // szerver-indulást megakassza.
182
- DyFM_Log.warn('[DyNTS_MemoryGuard] install failed (non-fatal):', err);
183
- }
184
- }
185
-
186
- /** A heap-plafon byte-ban (`--max-old-space-size`). Külön metódus a spec-mockoláshoz. */
187
- private getHeapLimitBytes(): number {
188
- return v8.getHeapStatistics().heap_size_limit;
189
- }
190
-
191
- /**
192
- * Egy poll-ciklus: kiolvassa a heap-kihasználtságot, frissíti a csúcsokat, és a
193
- * hiszterézis-állapotgép szerint esemény-átlépéskor rögzít + logol. SOHA nem dob.
194
- */
195
- private poll(): void {
196
- try {
197
- const limitBytes: number = this.getHeapLimitBytes();
198
- if (!limitBytes || limitBytes <= 0) { return; }
199
-
200
- const mu: NodeJS.MemoryUsage = process.memoryUsage();
201
- const pct: number = (mu.heapUsed / limitBytes) * 100;
202
- const gcFraction: number = this.recentGcFraction();
203
-
204
- const rssMb: number = mu.rss / BYTES_PER_MB;
205
- if (pct > this.peakHeapPct) { this.peakHeapPct = pct; }
206
- if (rssMb > this.peakRssMb) { this.peakRssMb = rssMb; }
207
-
208
- // A szintet a heap-% ÉS a GC-hányad közül a ROSSZABB dönti el (a GC-thrash a %-küszöb ALATT is OOM-ot jelez).
209
- const heapCrit: boolean = pct >= this.critPct;
210
- const gcCrit: boolean = gcFraction >= this.gcCritFraction;
211
- const heapWarn: boolean = pct >= this.warnPct;
212
- const gcWarn: boolean = gcFraction >= this.gcWarnFraction;
213
-
214
- // Hiszterézis-állapotgép — esemény CSAK állapot-váltáskor.
215
- const prev: DyNTS_MemoryGuard_State = this.state;
216
- let next: DyNTS_MemoryGuard_State = prev;
217
-
218
- if (heapCrit || gcCrit) {
219
- next = 'critical';
220
- } else if ((heapWarn || gcWarn) && prev === 'normal') {
221
- next = 'warning';
222
- } else if (pct <= this.warnPct - this.recoveryMargin && gcFraction < this.gcWarnFraction) {
223
- next = 'normal';
224
- }
225
- // Egyébként marad a jelenlegi állapot (hiszterézis-sáv) — nincs re-emit.
226
-
227
- // Sustained-critical streak + opcionális graceful-exit (állapot-váltástól FÜGGETLENÜL számolva). A boot-grace
228
- // alatt (install + bootGraceMs) NEM lép ki — a nehéz-boot (pool-hidratáló) appok eleve GC-thrash-elnek a boot
229
- // alatt, így a sustained-exit boot-loop-ot okozna; a grace UTÁN a tartós critical a VALÓDI runtime-OOM-ot kezeli.
230
- if (next === 'critical') {
231
- this.criticalStreak += 1;
232
- const pastBootGrace: boolean = (Date.now() - this.installedAt) >= this.bootGraceMs;
233
- if (this.exitOnSustainedCritical && pastBootGrace && this.criticalStreak >= this.sustainedCriticalPolls) {
234
- this.gracefulExit({ pct: pct, gcFraction: gcFraction, limitBytes: limitBytes, mu: mu });
235
- return;
236
- }
237
- } else {
238
- this.criticalStreak = 0;
239
- }
240
-
241
- if (next === prev) { return; }
242
- this.state = next;
243
-
244
- const trigger: DyNTS_MemoryGuard_Event['trigger'] =
245
- next === 'normal' ? 'recovery'
246
- : (heapCrit || heapWarn) && (gcCrit || gcWarn) ? 'heap+gc'
247
- : (gcCrit || gcWarn) ? 'gc' : 'heap';
248
-
249
- const event: DyNTS_MemoryGuard_Event = {
250
- level: next === 'normal' ? 'recovered' : next,
251
- heapUsedMb: Math.round(mu.heapUsed / BYTES_PER_MB),
252
- heapLimitMb: Math.round(limitBytes / BYTES_PER_MB),
253
- heapPct: Math.round(pct),
254
- rssMb: Math.round(rssMb),
255
- externalMb: Math.round((mu.external ?? 0) / BYTES_PER_MB),
256
- gcFraction: Math.round(gcFraction * 100) / 100,
257
- trigger: trigger,
258
- at: new Date().toISOString(),
259
- };
260
-
261
- this.recordEvent(event);
262
- } catch (err: unknown) {
263
- // Poll-hiba sosem fatal — a következő tick újrapróbálja.
264
- DyFM_Log.warn('[DyNTS_MemoryGuard] poll error (non-fatal):', err);
265
- }
266
- }
267
-
268
- /** Esemény ring-bufferbe + log + (warning/critical esetén) tartós error-sink. */
269
- private recordEvent(event: DyNTS_MemoryGuard_Event): void {
270
- this.history.push(event);
271
- while (this.history.length > this.maxHistoryCount) { this.history.shift(); }
272
-
273
- const summary: string =
274
- `heap ${event.heapPct}% (${event.heapUsedMb}/${event.heapLimitMb}MB), rss ${event.rssMb}MB, ` +
275
- `GC ${Math.round(event.gcFraction * 100)}% [${event.trigger}]`;
276
-
277
- if (event.level === 'recovered') {
278
- DyFM_Log.success(`[DyNTS_MemoryGuard] RECOVERED — ${summary}`);
279
- return;
280
- }
281
-
282
- if (event.level === 'critical') {
283
- DyFM_Log.H_error(`[DyNTS_MemoryGuard] CRITICAL heap pressure — ${summary} — OOM imminent`);
284
- } else {
285
- DyFM_Log.warn(`[DyNTS_MemoryGuard] WARNING heap pressure — ${summary}`);
286
- }
287
-
288
- // Tartós rögzítés a központi error-sinkbe (Errors-rendszer + Discord, ha bekötve).
289
- try {
290
- DyNTS_GlobalService.globalErrorHandler?.(
291
- new DyFM_Error({
292
- errorCode:
293
- `${DyNTS_global_settings.systemShortCodeName ?? 'DyNTS'}|DyNTS-MG0-HEAP-${event.level.toUpperCase()}`,
294
- message:
295
- `Heap pressure ${event.level.toUpperCase()}: ${summary}. ` +
296
- `Process approaching the --max-old-space-size ceiling — OOM crash risk.`,
297
- additionalContent: { memoryGuardEvent: event, peakHeapPct: Math.round(this.peakHeapPct) },
298
- systemVersion: DyNTS_global_settings.systemVersion,
299
- level: event.level === 'critical' ? DyFM_ErrorLevel.critical : DyFM_ErrorLevel.warning,
300
- }),
301
- );
302
- } catch (sinkErr: unknown) {
303
- DyFM_Log.warn('[DyNTS_MemoryGuard] error-sink record failed (non-fatal):', sinkErr);
304
- }
305
-
306
- if (event.level === 'critical' && this.onCritical) {
307
- try {
308
- this.onCritical(event);
309
- } catch (hookErr: unknown) {
310
- DyFM_Log.warn('[DyNTS_MemoryGuard] onCritical hook threw (non-fatal):', hookErr);
311
- }
312
- }
313
- }
314
-
315
- /**
316
- * A pillanatnyi heap-nyomás (heap-% + GC-hányad → szint) — a FOGYASZTÓK (pl. egy API-controller) ezzel
317
- * gate-elhetik a nehéz műveleteket (load-shed). Olvasás-only, mellékhatás nélkül; nem igényli az install-t.
318
- */
319
- pressure(): {
320
- level: 'ok' | 'warning' | 'critical';
321
- heapUsedMb: number; heapLimitMb: number; heapPct: number; rssMb: number; gcFraction: number;
322
- } {
323
- const limitBytes: number = this.getHeapLimitBytes();
324
- const mu: NodeJS.MemoryUsage = process.memoryUsage();
325
- const pct: number = limitBytes > 0 ? (mu.heapUsed / limitBytes) * 100 : 0;
326
- const gcFraction: number = this.recentGcFraction();
327
- const level: 'ok' | 'warning' | 'critical' =
328
- (pct >= this.critPct || gcFraction >= this.gcCritFraction) ? 'critical'
329
- : (pct >= this.warnPct || gcFraction >= this.gcWarnFraction) ? 'warning' : 'ok';
330
- return {
331
- level: level,
332
- heapUsedMb: Math.round(mu.heapUsed / BYTES_PER_MB),
333
- heapLimitMb: Math.round(limitBytes / BYTES_PER_MB),
334
- heapPct: Math.round(pct),
335
- rssMb: Math.round(mu.rss / BYTES_PER_MB),
336
- gcFraction: Math.round(gcFraction * 100) / 100,
337
- };
338
- }
339
-
340
- /** `true`, ha a NEHÉZ műveleteket le kell shed-elni (critical nyomás) — a fogyasztó load-shed gate-jének. */
341
- shouldShedLoad(): boolean {
342
- return this.installed && this.pressure().level === 'critical';
343
- }
344
-
345
- /** A GC-hányad az utolsó `pollIntervalMs` ablakban (GC-ms / ablak-ms; clamp [0..1]). Nincs GC-observer → 0. */
346
- private recentGcFraction(): number {
347
- const windowMs: number = this.pollIntervalMs > 0 ? this.pollIntervalMs : 10000;
348
- const cutoff: number = Date.now() - windowMs;
349
- this.gcEvents = this.gcEvents.filter((event: { at: number; ms: number }): boolean => event.at >= cutoff);
350
- const gcMs: number = this.gcEvents.reduce((sum: number, event: { at: number; ms: number }): number => sum + event.ms, 0);
351
- return Math.min(gcMs / windowMs, 1);
352
- }
353
-
354
- /** A 'gc' perf-entry-k figyelője — a GC-ben töltött időt gyűjti a gördülő ablakhoz. Best-effort (nem kritikus). */
355
- private installGcObserver(): void {
356
- try {
357
- this.gcObserver = new PerformanceObserver((list): void => {
358
- const now: number = Date.now();
359
- for (const entry of list.getEntries()) {
360
- this.gcEvents.push({ at: now, ms: entry.duration });
361
- }
362
- });
363
- this.gcObserver.observe({ entryTypes: ['gc'] });
364
- } catch (err: unknown) {
365
- // A GC-observer hiánya nem kritikus — a heap-%-jel marad. Best-effort.
366
- DyFM_Log.warn('[DyNTS_MemoryGuard] gc-observer install failed (non-fatal):', err);
367
- }
368
- }
369
-
370
- /**
371
- * Process-szintű `uncaughtException` crash-handler: a (különben néma) crash ELŐTT TELJES-RÉSZLETŰ error-entry
372
- * (message + stack + memória-snapshot) az error-sinkbe, majd tiszta exit (a wrapper/restart-policy újraindít).
373
- * Az `unhandledRejection`-t a base App már rögzíti; ez az eddig-fedetlen `uncaughtException`-t fedi le.
374
- */
375
- private installCrashHandlers(): void {
376
- this.crashHandler = (error: Error): void => {
377
- try {
378
- const mu: NodeJS.MemoryUsage = process.memoryUsage();
379
- DyFM_Log.H_error('[DyNTS_MemoryGuard] FATAL uncaughtException:', error?.stack ?? error);
380
- DyNTS_GlobalService.globalErrorHandler?.(
381
- new DyFM_Error({
382
- errorCode: `${DyNTS_global_settings.systemShortCodeName ?? 'DyNTS'}|DyNTS-MG0-UNCAUGHT`,
383
- message: `Uncaught Exception (process-fatal): ${error?.message ?? String(error)}`,
384
- error: error,
385
- additionalContent: {
386
- stack: error?.stack,
387
- heapUsedMb: Math.round(mu.heapUsed / BYTES_PER_MB),
388
- rssMb: Math.round(mu.rss / BYTES_PER_MB),
389
- peakHeapPct: Math.round(this.peakHeapPct),
390
- recentMemoryGuardEvents: this.history.slice(-5),
391
- },
392
- systemVersion: DyNTS_global_settings.systemVersion,
393
- level: DyFM_ErrorLevel.critical,
394
- }),
395
- );
396
- } catch {
397
- // a rögzítés se buktasson — best-effort.
398
- }
399
- // uncaughtException → a process undefined állapotban van: rögzítés UTÁN tiszta exit (a log/sink flush-re haladék).
400
- setTimeout((): void => { process.exit(1); }, 250).unref?.();
401
- };
402
- process.on('uncaughtException', this.crashHandler);
403
- }
404
-
405
- /** Tartós-critical graceful-exit: végső error-entry + tiszta exit (a wrapper újraindít a kriptikus 134 helyett). */
406
- private gracefulExit(set: { pct: number; gcFraction: number; limitBytes: number; mu: NodeJS.MemoryUsage }): void {
407
- const summary: string =
408
- `heap ${Math.round(set.pct)}% (${Math.round(set.mu.heapUsed / BYTES_PER_MB)}/` +
409
- `${Math.round(set.limitBytes / BYTES_PER_MB)}MB), GC ${Math.round(set.gcFraction * 100)}%`;
410
- DyFM_Log.H_error(`[DyNTS_MemoryGuard] SUSTAINED CRITICAL — ${summary} — graceful exit(${this.exitCode}) a kriptikus OOM (exit 134) helyett.`);
411
- try {
412
- DyNTS_GlobalService.globalErrorHandler?.(
413
- new DyFM_Error({
414
- errorCode: `${DyNTS_global_settings.systemShortCodeName ?? 'DyNTS'}|DyNTS-MG0-GRACEFUL-EXIT`,
415
- message: `Sustained critical heap pressure (${this.criticalStreak} polls) — graceful exit to let the wrapper/restart-policy recover. ${summary}.`,
416
- additionalContent: { peakHeapPct: Math.round(this.peakHeapPct), peakRssMb: Math.round(this.peakRssMb), recentEvents: this.history.slice(-5) },
417
- systemVersion: DyNTS_global_settings.systemVersion,
418
- level: DyFM_ErrorLevel.critical,
419
- }),
420
- );
421
- } catch {
422
- // best-effort
423
- }
424
- setTimeout((): void => { process.exit(this.exitCode); }, 250).unref?.();
425
- }
426
-
427
- /** A megőrzött események (legrégebbi → legújabb). Csak olvasásra. */
428
- getHistory(): DyNTS_MemoryGuard_Event[] {
429
- return this.history.slice();
430
- }
431
-
432
- /** Pillanatnyi állapot + csúcsok diagnosztikához (pl. egy status-endpoint). */
433
- getStatus(): {
434
- installed: boolean;
435
- state: DyNTS_MemoryGuard_State;
436
- peakHeapPct: number;
437
- peakRssMb: number;
438
- eventCount: number;
439
- } {
440
- return {
441
- installed: this.installed,
442
- state: this.state,
443
- peakHeapPct: Math.round(this.peakHeapPct),
444
- peakRssMb: Math.round(this.peakRssMb),
445
- eventCount: this.history.length,
446
- };
447
- }
448
-
449
- /** Telepítve van-e a watchdog. */
450
- isInstalled(): boolean {
451
- return this.installed;
452
- }
453
-
454
- /**
455
- * Leállítja a watchdogot + visszaállítja az állapotot. Graceful shutdown-hoz ÉS a
456
- * spec-ek `afterEach`-éhez (a singleton-state ne szivárogjon a tesztek közt).
457
- */
458
- _teardownForTesting(): void {
459
- if (this.timer) {
460
- clearInterval(this.timer);
461
- this.timer = null;
462
- }
463
- if (this.gcObserver) {
464
- try { this.gcObserver.disconnect(); } catch { /* best-effort */ }
465
- this.gcObserver = null;
466
- }
467
- if (this.crashHandler) {
468
- try { process.removeListener('uncaughtException', this.crashHandler); } catch { /* best-effort */ }
469
- this.crashHandler = null;
470
- }
471
- this.installed = false;
472
- this.state = 'normal';
473
- this.history.length = 0;
474
- this.peakHeapPct = 0;
475
- this.peakRssMb = 0;
476
- this.gcEvents = [];
477
- this.criticalStreak = 0;
478
- this.installedAt = 0;
479
- this.onCritical = undefined;
480
- }
481
- }
1
+ import * as v8 from 'v8';
2
+ import { PerformanceObserver } from 'perf_hooks';
3
+
4
+ import { DyFM_Error, DyFM_ErrorLevel, DyFM_Log } from '@futdevpro/fsm-dynamo';
5
+
6
+ import { DyNTS_global_settings } from '../../_collections/global-settings.const';
7
+ import { DyNTS_SingletonServiceBase } from '../base/singleton.service-base';
8
+ import { DyNTS_GlobalService } from './global.service';
9
+
10
+ /**
11
+ * FR-193 (2026-06-15) — bedrock OOM korai-figyelmeztető heap-watchdog.
12
+ *
13
+ * MIÉRT: a Node heap-OOM (`--max-old-space-size` túllépés, pl. egy óriási JSON.parse)
14
+ * a process-t AZONNAL megöli — `process.on('uncaughtException')` NEM kapja el, így a
15
+ * crash pillanatában már nincs mód a hibát rögzíteni. Az EGYETLEN megbízható idő a
16
+ * rögzítésre a crash ELŐTT van. E watchdog periodikusan figyeli a heap-kihasználtságot,
17
+ * és amikor egy konfigurálható küszöböt átlép, TARTÓS rekordot hagy (DyFM_Log +
18
+ * `DyNTS_GlobalService.globalErrorHandler` → az Errors-sink, amit minden szerver megkap)
19
+ * — így egy OOM-hajlamos szerver NYOMOT hagy mielőtt elszáll, és a kuszob-atlepés
20
+ * lathato a dashboard-on / Discord-on.
21
+ *
22
+ * MIT NEM csinál: NEM állítja le/újraindítja a process-t (az a konténer-szintű
23
+ * `restart:` policy dolga), és NEM próbálja elkapni a fatal OOM-ot (nem lehet). Tisztán
24
+ * MEGFIGYELÉS + RÖGZÍTÉS + opcionális `onCritical` hook (amivel a fogyasztó terhet dobhat).
25
+ *
26
+ * Hiszterézis: a küszöböket CSAK állapot-VÁLTÁSkor jelzi (normal→warning→critical), és
27
+ * a `recoveryMargin`-nyit a warning ALÁ esve jelez 'recovered'-et — így nem spamel a
28
+ * küszöb körül oszcilláló heap.
29
+ *
30
+ * Használat (opt-in, mint a `DyNTS_Logs_Service`):
31
+ * DyNTS_MemoryGuard.getInstance().install(); // default settings
32
+ * DyNTS_MemoryGuard.getInstance().install({ onCritical }); // custom hook
33
+ * VAGY automatikusan: a base `App` startup feltelepíti, ha
34
+ * `DyNTS_global_settings.memoryGuard.enabled === true` (alapértelmezés szerint igen).
35
+ */
36
+
37
+ /** Egy MemoryGuard esemény — küszöb-átlépés vagy helyreállás. */
38
+ export interface DyNTS_MemoryGuard_Event {
39
+ /** `warning` / `critical` küszöb-átlépés, vagy `recovered` (vissza normal-ba). */
40
+ level: 'warning' | 'critical' | 'recovered';
41
+ /** Heap használat MB-ban (`process.memoryUsage().heapUsed`). */
42
+ heapUsedMb: number;
43
+ /** Heap plafon MB-ban (`v8.getHeapStatistics().heap_size_limit` — a `--max-old-space-size`). */
44
+ heapLimitMb: number;
45
+ /** heapUsed / heapLimit * 100 (egész %-ra kerekítve). */
46
+ heapPct: number;
47
+ /** Resident Set Size MB-ban (teljes process-memória, heap + non-heap). */
48
+ rssMb: number;
49
+ /** External (C++ / Buffer) memória MB-ban. */
50
+ externalMb: number;
51
+ /** A közelmúlt GC-hányada (GC-ben töltött idő / poll-ablak; 0..1) — a mark-compact-thrash / OOM-precursor jelzője. */
52
+ gcFraction: number;
53
+ /** Mi váltotta ki az esemény-szintet: a heap-% küszöb, a GC-thrash, vagy mindkettő. */
54
+ trigger: 'heap' | 'gc' | 'heap+gc' | 'recovery';
55
+ /** Esemény időbélyege (ISO). */
56
+ at: string;
57
+ }
58
+
59
+ /** `install()` opcionális override-jai (a `DyNTS_global_settings.memoryGuard` FÖLÉ). */
60
+ export interface DyNTS_MemoryGuard_Config {
61
+ /** Poll-intervallum ms-ben. Default: 10000. */
62
+ pollIntervalMs?: number;
63
+ /** Warning küszöb a heap-plafon %-ában. Default: 85. */
64
+ heapWarningThreshold?: number;
65
+ /** Critical küszöb a heap-plafon %-ában. Default: 95. */
66
+ heapCriticalThreshold?: number;
67
+ /** A warning küszöb ALATTI margó %-ban a 'recovered'-hez (hiszterézis). Default: 10. */
68
+ recoveryMargin?: number;
69
+ /** Megőrzött események max száma (ring-buffer). Default: 100. */
70
+ maxHistoryCount?: number;
71
+ /** GC-hányad-küszöb a `warning`-hoz (0..1). Default: 0.40. */
72
+ gcWarningFraction?: number;
73
+ /** GC-hányad-küszöb a `critical`-hoz (0..1). Default: 0.60. */
74
+ gcCriticalFraction?: number;
75
+ /** `uncaughtException` crash-handler telepítése (teljes-részletű error-entry + tiszta exit). Default: true. */
76
+ installCrashHandlers?: boolean;
77
+ /** Tartós critical → tiszta exit (a wrapper újraindít). Default: false. */
78
+ exitOnSustainedCritical?: boolean;
79
+ /** Hány egymást követő critical poll után lép ki. Default: 3. */
80
+ sustainedCriticalPolls?: number;
81
+ /** A graceful-exit kódja. Default: 137. */
82
+ exitCode?: number;
83
+ /** Boot-grace ms az `exitOnSustainedCritical`-hez (a guard-install utáni ennyi ms-ben NEM lép ki). Default: 60000. */
84
+ bootGraceMs?: number;
85
+ /** Hook, amit a `critical` küszöb átlépésekor hívunk (pl. terhelés-dobás). */
86
+ onCritical?: (event: DyNTS_MemoryGuard_Event) => void;
87
+ }
88
+
89
+ type DyNTS_MemoryGuard_State = 'normal' | 'warning' | 'critical';
90
+
91
+ const BYTES_PER_MB: number = 1024 * 1024;
92
+
93
+ export class DyNTS_MemoryGuard extends DyNTS_SingletonServiceBase {
94
+
95
+ static getInstance(): DyNTS_MemoryGuard {
96
+ return DyNTS_MemoryGuard.getSingletonInstance() as DyNTS_MemoryGuard;
97
+ }
98
+
99
+ private timer: ReturnType<typeof setInterval> | null = null;
100
+ private installed: boolean = false;
101
+ private state: DyNTS_MemoryGuard_State = 'normal';
102
+
103
+ // Feloldott (effektív) konfiguráció — install()-kor töltjük.
104
+ private pollIntervalMs: number = 10000;
105
+ private warnPct: number = 85;
106
+ private critPct: number = 95;
107
+ private recoveryMargin: number = 10;
108
+ private maxHistoryCount: number = 100;
109
+ private gcWarnFraction: number = 0.40;
110
+ private gcCritFraction: number = 0.60;
111
+ private exitOnSustainedCritical: boolean = false;
112
+ private sustainedCriticalPolls: number = 3;
113
+ private exitCode: number = 137;
114
+ private bootGraceMs: number = 60000;
115
+ /** A guard-install időbélyege (a boot-grace számításához; 0 = még nem telepített). */
116
+ private installedAt: number = 0;
117
+ private onCritical?: (event: DyNTS_MemoryGuard_Event) => void;
118
+
119
+ private readonly history: DyNTS_MemoryGuard_Event[] = [];
120
+ private peakHeapPct: number = 0;
121
+ private peakRssMb: number = 0;
122
+
123
+ // GC-thrash követés (perf_hooks 'gc') — a GC-ben töltött ms gördülő ablaka + a critical-streak számláló.
124
+ private gcObserver: PerformanceObserver | null = null;
125
+ private gcEvents: { at: number; ms: number }[] = [];
126
+ private criticalStreak: number = 0;
127
+ // A telepített crash-handler ref-ek (a teardown eltávolításához).
128
+ private crashHandler: ((error: Error) => void) | null = null;
129
+
130
+ protected constructor() {
131
+ super();
132
+ }
133
+
134
+ /**
135
+ * Elindítja a heap-watchdogot. Idempotens (többszöri hívás no-op). A `config`
136
+ * felülírja a `DyNTS_global_settings.memoryGuard` értékeit, ami a beépített
137
+ * defaultokat írja felül. SOHA nem dob — egy figyelő-réteg nem dönthet be egy
138
+ * szervert (minden hiba try/catch-elt + descriptive-en logolt).
139
+ */
140
+ install(config?: DyNTS_MemoryGuard_Config): void {
141
+ if (this.installed) { return; }
142
+
143
+ try {
144
+ const g: NonNullable<typeof DyNTS_global_settings.memoryGuard> =
145
+ DyNTS_global_settings.memoryGuard ?? { enabled: true };
146
+
147
+ this.pollIntervalMs = config?.pollIntervalMs ?? g.pollIntervalMs ?? 10000;
148
+ this.warnPct = config?.heapWarningThreshold ?? g.heapWarningThreshold ?? 85;
149
+ this.critPct = config?.heapCriticalThreshold ?? g.heapCriticalThreshold ?? 95;
150
+ this.recoveryMargin = config?.recoveryMargin ?? g.recoveryMargin ?? 10;
151
+ this.maxHistoryCount = config?.maxHistoryCount ?? g.maxHistoryCount ?? 100;
152
+ this.gcWarnFraction = config?.gcWarningFraction ?? g.gcWarningFraction ?? 0.40;
153
+ this.gcCritFraction = config?.gcCriticalFraction ?? g.gcCriticalFraction ?? 0.60;
154
+ this.exitOnSustainedCritical = config?.exitOnSustainedCritical ?? g.exitOnSustainedCritical ?? false;
155
+ this.sustainedCriticalPolls = config?.sustainedCriticalPolls ?? g.sustainedCriticalPolls ?? 3;
156
+ this.exitCode = config?.exitCode ?? g.exitCode ?? 137;
157
+ this.bootGraceMs = config?.bootGraceMs ?? g.bootGraceMs ?? 60000;
158
+ this.installedAt = Date.now();
159
+ this.onCritical = config?.onCritical;
160
+
161
+ this.installed = true;
162
+ this.installGcObserver();
163
+ // Crash-handler (uncaughtException) — teljes-részletű error-entry a crash ELŐTT, majd tiszta exit.
164
+ if (config?.installCrashHandlers ?? g.installCrashHandlers ?? true) {
165
+ this.installCrashHandlers();
166
+ }
167
+ this.timer = setInterval((): void => { this.poll(); }, this.pollIntervalMs);
168
+ // unref(): a watchdog-timer NE tartsa életben a process-t önmagában (graceful
169
+ // exit-kor a Node ki tud lépni a függő interval ellenére is).
170
+ if (typeof this.timer.unref === 'function') { this.timer.unref(); }
171
+
172
+ DyFM_Log.info(
173
+ `[DyNTS_MemoryGuard] installed — poll ${this.pollIntervalMs}ms, ` +
174
+ `warn ${this.warnPct}%/${Math.round(this.gcWarnFraction * 100)}%GC, ` +
175
+ `crit ${this.critPct}%/${Math.round(this.gcCritFraction * 100)}%GC (heap limit ` +
176
+ `${Math.round(this.getHeapLimitBytes() / BYTES_PER_MB)}MB)` +
177
+ (this.exitOnSustainedCritical ? `, graceful-exit@${this.sustainedCriticalPolls} critical polls (boot-grace ${this.bootGraceMs}ms)` : ''),
178
+ );
179
+ } catch (err: unknown) {
180
+ // Telepítési hiba SEM lehet fatal — a guard hiánya nem ér annyit, hogy a
181
+ // szerver-indulást megakassza.
182
+ DyFM_Log.warn('[DyNTS_MemoryGuard] install failed (non-fatal):', err);
183
+ }
184
+ }
185
+
186
+ /** A heap-plafon byte-ban (`--max-old-space-size`). Külön metódus a spec-mockoláshoz. */
187
+ private getHeapLimitBytes(): number {
188
+ return v8.getHeapStatistics().heap_size_limit;
189
+ }
190
+
191
+ /**
192
+ * Egy poll-ciklus: kiolvassa a heap-kihasználtságot, frissíti a csúcsokat, és a
193
+ * hiszterézis-állapotgép szerint esemény-átlépéskor rögzít + logol. SOHA nem dob.
194
+ */
195
+ private poll(): void {
196
+ try {
197
+ const limitBytes: number = this.getHeapLimitBytes();
198
+ if (!limitBytes || limitBytes <= 0) { return; }
199
+
200
+ const mu: NodeJS.MemoryUsage = process.memoryUsage();
201
+ const pct: number = (mu.heapUsed / limitBytes) * 100;
202
+ const gcFraction: number = this.recentGcFraction();
203
+
204
+ const rssMb: number = mu.rss / BYTES_PER_MB;
205
+ if (pct > this.peakHeapPct) { this.peakHeapPct = pct; }
206
+ if (rssMb > this.peakRssMb) { this.peakRssMb = rssMb; }
207
+
208
+ // A szintet a heap-% ÉS a GC-hányad közül a ROSSZABB dönti el (a GC-thrash a %-küszöb ALATT is OOM-ot jelez).
209
+ const heapCrit: boolean = pct >= this.critPct;
210
+ const gcCrit: boolean = gcFraction >= this.gcCritFraction;
211
+ const heapWarn: boolean = pct >= this.warnPct;
212
+ const gcWarn: boolean = gcFraction >= this.gcWarnFraction;
213
+
214
+ // Hiszterézis-állapotgép — esemény CSAK állapot-váltáskor.
215
+ const prev: DyNTS_MemoryGuard_State = this.state;
216
+ let next: DyNTS_MemoryGuard_State = prev;
217
+
218
+ if (heapCrit || gcCrit) {
219
+ next = 'critical';
220
+ } else if ((heapWarn || gcWarn) && prev === 'normal') {
221
+ next = 'warning';
222
+ } else if (pct <= this.warnPct - this.recoveryMargin && gcFraction < this.gcWarnFraction) {
223
+ next = 'normal';
224
+ }
225
+ // Egyébként marad a jelenlegi állapot (hiszterézis-sáv) — nincs re-emit.
226
+
227
+ // Sustained-critical streak + opcionális graceful-exit (állapot-váltástól FÜGGETLENÜL számolva). A boot-grace
228
+ // alatt (install + bootGraceMs) NEM lép ki — a nehéz-boot (pool-hidratáló) appok eleve GC-thrash-elnek a boot
229
+ // alatt, így a sustained-exit boot-loop-ot okozna; a grace UTÁN a tartós critical a VALÓDI runtime-OOM-ot kezeli.
230
+ if (next === 'critical') {
231
+ this.criticalStreak += 1;
232
+ const pastBootGrace: boolean = (Date.now() - this.installedAt) >= this.bootGraceMs;
233
+ if (this.exitOnSustainedCritical && pastBootGrace && this.criticalStreak >= this.sustainedCriticalPolls) {
234
+ this.gracefulExit({ pct: pct, gcFraction: gcFraction, limitBytes: limitBytes, mu: mu });
235
+ return;
236
+ }
237
+ } else {
238
+ this.criticalStreak = 0;
239
+ }
240
+
241
+ if (next === prev) { return; }
242
+ this.state = next;
243
+
244
+ const trigger: DyNTS_MemoryGuard_Event['trigger'] =
245
+ next === 'normal' ? 'recovery'
246
+ : (heapCrit || heapWarn) && (gcCrit || gcWarn) ? 'heap+gc'
247
+ : (gcCrit || gcWarn) ? 'gc' : 'heap';
248
+
249
+ const event: DyNTS_MemoryGuard_Event = {
250
+ level: next === 'normal' ? 'recovered' : next,
251
+ heapUsedMb: Math.round(mu.heapUsed / BYTES_PER_MB),
252
+ heapLimitMb: Math.round(limitBytes / BYTES_PER_MB),
253
+ heapPct: Math.round(pct),
254
+ rssMb: Math.round(rssMb),
255
+ externalMb: Math.round((mu.external ?? 0) / BYTES_PER_MB),
256
+ gcFraction: Math.round(gcFraction * 100) / 100,
257
+ trigger: trigger,
258
+ at: new Date().toISOString(),
259
+ };
260
+
261
+ this.recordEvent(event);
262
+ } catch (err: unknown) {
263
+ // Poll-hiba sosem fatal — a következő tick újrapróbálja.
264
+ DyFM_Log.warn('[DyNTS_MemoryGuard] poll error (non-fatal):', err);
265
+ }
266
+ }
267
+
268
+ /** Esemény ring-bufferbe + log + (warning/critical esetén) tartós error-sink. */
269
+ private recordEvent(event: DyNTS_MemoryGuard_Event): void {
270
+ this.history.push(event);
271
+ while (this.history.length > this.maxHistoryCount) { this.history.shift(); }
272
+
273
+ const summary: string =
274
+ `heap ${event.heapPct}% (${event.heapUsedMb}/${event.heapLimitMb}MB), rss ${event.rssMb}MB, ` +
275
+ `GC ${Math.round(event.gcFraction * 100)}% [${event.trigger}]`;
276
+
277
+ if (event.level === 'recovered') {
278
+ DyFM_Log.success(`[DyNTS_MemoryGuard] RECOVERED — ${summary}`);
279
+ return;
280
+ }
281
+
282
+ if (event.level === 'critical') {
283
+ DyFM_Log.H_error(`[DyNTS_MemoryGuard] CRITICAL heap pressure — ${summary} — OOM imminent`);
284
+ } else {
285
+ DyFM_Log.warn(`[DyNTS_MemoryGuard] WARNING heap pressure — ${summary}`);
286
+ }
287
+
288
+ // Tartós rögzítés a központi error-sinkbe (Errors-rendszer + Discord, ha bekötve).
289
+ try {
290
+ DyNTS_GlobalService.globalErrorHandler?.(
291
+ new DyFM_Error({
292
+ errorCode:
293
+ `${DyNTS_global_settings.systemShortCodeName ?? 'DyNTS'}|DyNTS-MG0-HEAP-${event.level.toUpperCase()}`,
294
+ message:
295
+ `Heap pressure ${event.level.toUpperCase()}: ${summary}. ` +
296
+ `Process approaching the --max-old-space-size ceiling — OOM crash risk.`,
297
+ additionalContent: { memoryGuardEvent: event, peakHeapPct: Math.round(this.peakHeapPct) },
298
+ systemVersion: DyNTS_global_settings.systemVersion,
299
+ level: event.level === 'critical' ? DyFM_ErrorLevel.critical : DyFM_ErrorLevel.warning,
300
+ }),
301
+ );
302
+ } catch (sinkErr: unknown) {
303
+ DyFM_Log.warn('[DyNTS_MemoryGuard] error-sink record failed (non-fatal):', sinkErr);
304
+ }
305
+
306
+ if (event.level === 'critical' && this.onCritical) {
307
+ try {
308
+ this.onCritical(event);
309
+ } catch (hookErr: unknown) {
310
+ DyFM_Log.warn('[DyNTS_MemoryGuard] onCritical hook threw (non-fatal):', hookErr);
311
+ }
312
+ }
313
+ }
314
+
315
+ /**
316
+ * A pillanatnyi heap-nyomás (heap-% + GC-hányad → szint) — a FOGYASZTÓK (pl. egy API-controller) ezzel
317
+ * gate-elhetik a nehéz műveleteket (load-shed). Olvasás-only, mellékhatás nélkül; nem igényli az install-t.
318
+ */
319
+ pressure(): {
320
+ level: 'ok' | 'warning' | 'critical';
321
+ heapUsedMb: number; heapLimitMb: number; heapPct: number; rssMb: number; gcFraction: number;
322
+ } {
323
+ const limitBytes: number = this.getHeapLimitBytes();
324
+ const mu: NodeJS.MemoryUsage = process.memoryUsage();
325
+ const pct: number = limitBytes > 0 ? (mu.heapUsed / limitBytes) * 100 : 0;
326
+ const gcFraction: number = this.recentGcFraction();
327
+ const level: 'ok' | 'warning' | 'critical' =
328
+ (pct >= this.critPct || gcFraction >= this.gcCritFraction) ? 'critical'
329
+ : (pct >= this.warnPct || gcFraction >= this.gcWarnFraction) ? 'warning' : 'ok';
330
+ return {
331
+ level: level,
332
+ heapUsedMb: Math.round(mu.heapUsed / BYTES_PER_MB),
333
+ heapLimitMb: Math.round(limitBytes / BYTES_PER_MB),
334
+ heapPct: Math.round(pct),
335
+ rssMb: Math.round(mu.rss / BYTES_PER_MB),
336
+ gcFraction: Math.round(gcFraction * 100) / 100,
337
+ };
338
+ }
339
+
340
+ /** `true`, ha a NEHÉZ műveleteket le kell shed-elni (critical nyomás) — a fogyasztó load-shed gate-jének. */
341
+ shouldShedLoad(): boolean {
342
+ return this.installed && this.pressure().level === 'critical';
343
+ }
344
+
345
+ /** A GC-hányad az utolsó `pollIntervalMs` ablakban (GC-ms / ablak-ms; clamp [0..1]). Nincs GC-observer → 0. */
346
+ private recentGcFraction(): number {
347
+ const windowMs: number = this.pollIntervalMs > 0 ? this.pollIntervalMs : 10000;
348
+ const cutoff: number = Date.now() - windowMs;
349
+ this.gcEvents = this.gcEvents.filter((event: { at: number; ms: number }): boolean => event.at >= cutoff);
350
+ const gcMs: number = this.gcEvents.reduce((sum: number, event: { at: number; ms: number }): number => sum + event.ms, 0);
351
+ return Math.min(gcMs / windowMs, 1);
352
+ }
353
+
354
+ /** A 'gc' perf-entry-k figyelője — a GC-ben töltött időt gyűjti a gördülő ablakhoz. Best-effort (nem kritikus). */
355
+ private installGcObserver(): void {
356
+ try {
357
+ this.gcObserver = new PerformanceObserver((list): void => {
358
+ const now: number = Date.now();
359
+ for (const entry of list.getEntries()) {
360
+ this.gcEvents.push({ at: now, ms: entry.duration });
361
+ }
362
+ });
363
+ this.gcObserver.observe({ entryTypes: ['gc'] });
364
+ } catch (err: unknown) {
365
+ // A GC-observer hiánya nem kritikus — a heap-%-jel marad. Best-effort.
366
+ DyFM_Log.warn('[DyNTS_MemoryGuard] gc-observer install failed (non-fatal):', err);
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Process-szintű `uncaughtException` crash-handler: a (különben néma) crash ELŐTT TELJES-RÉSZLETŰ error-entry
372
+ * (message + stack + memória-snapshot) az error-sinkbe, majd tiszta exit (a wrapper/restart-policy újraindít).
373
+ * Az `unhandledRejection`-t a base App már rögzíti; ez az eddig-fedetlen `uncaughtException`-t fedi le.
374
+ */
375
+ private installCrashHandlers(): void {
376
+ this.crashHandler = (error: Error): void => {
377
+ try {
378
+ const mu: NodeJS.MemoryUsage = process.memoryUsage();
379
+ DyFM_Log.H_error('[DyNTS_MemoryGuard] FATAL uncaughtException:', error?.stack ?? error);
380
+ DyNTS_GlobalService.globalErrorHandler?.(
381
+ new DyFM_Error({
382
+ errorCode: `${DyNTS_global_settings.systemShortCodeName ?? 'DyNTS'}|DyNTS-MG0-UNCAUGHT`,
383
+ message: `Uncaught Exception (process-fatal): ${error?.message ?? String(error)}`,
384
+ error: error,
385
+ additionalContent: {
386
+ stack: error?.stack,
387
+ heapUsedMb: Math.round(mu.heapUsed / BYTES_PER_MB),
388
+ rssMb: Math.round(mu.rss / BYTES_PER_MB),
389
+ peakHeapPct: Math.round(this.peakHeapPct),
390
+ recentMemoryGuardEvents: this.history.slice(-5),
391
+ },
392
+ systemVersion: DyNTS_global_settings.systemVersion,
393
+ level: DyFM_ErrorLevel.critical,
394
+ }),
395
+ );
396
+ } catch {
397
+ // a rögzítés se buktasson — best-effort.
398
+ }
399
+ // uncaughtException → a process undefined állapotban van: rögzítés UTÁN tiszta exit (a log/sink flush-re haladék).
400
+ setTimeout((): void => { process.exit(1); }, 250).unref?.();
401
+ };
402
+ process.on('uncaughtException', this.crashHandler);
403
+ }
404
+
405
+ /** Tartós-critical graceful-exit: végső error-entry + tiszta exit (a wrapper újraindít a kriptikus 134 helyett). */
406
+ private gracefulExit(set: { pct: number; gcFraction: number; limitBytes: number; mu: NodeJS.MemoryUsage }): void {
407
+ const summary: string =
408
+ `heap ${Math.round(set.pct)}% (${Math.round(set.mu.heapUsed / BYTES_PER_MB)}/` +
409
+ `${Math.round(set.limitBytes / BYTES_PER_MB)}MB), GC ${Math.round(set.gcFraction * 100)}%`;
410
+ DyFM_Log.H_error(`[DyNTS_MemoryGuard] SUSTAINED CRITICAL — ${summary} — graceful exit(${this.exitCode}) a kriptikus OOM (exit 134) helyett.`);
411
+ try {
412
+ DyNTS_GlobalService.globalErrorHandler?.(
413
+ new DyFM_Error({
414
+ errorCode: `${DyNTS_global_settings.systemShortCodeName ?? 'DyNTS'}|DyNTS-MG0-GRACEFUL-EXIT`,
415
+ message: `Sustained critical heap pressure (${this.criticalStreak} polls) — graceful exit to let the wrapper/restart-policy recover. ${summary}.`,
416
+ additionalContent: { peakHeapPct: Math.round(this.peakHeapPct), peakRssMb: Math.round(this.peakRssMb), recentEvents: this.history.slice(-5) },
417
+ systemVersion: DyNTS_global_settings.systemVersion,
418
+ level: DyFM_ErrorLevel.critical,
419
+ }),
420
+ );
421
+ } catch {
422
+ // best-effort
423
+ }
424
+ setTimeout((): void => { process.exit(this.exitCode); }, 250).unref?.();
425
+ }
426
+
427
+ /** A megőrzött események (legrégebbi → legújabb). Csak olvasásra. */
428
+ getHistory(): DyNTS_MemoryGuard_Event[] {
429
+ return this.history.slice();
430
+ }
431
+
432
+ /** Pillanatnyi állapot + csúcsok diagnosztikához (pl. egy status-endpoint). */
433
+ getStatus(): {
434
+ installed: boolean;
435
+ state: DyNTS_MemoryGuard_State;
436
+ peakHeapPct: number;
437
+ peakRssMb: number;
438
+ eventCount: number;
439
+ } {
440
+ return {
441
+ installed: this.installed,
442
+ state: this.state,
443
+ peakHeapPct: Math.round(this.peakHeapPct),
444
+ peakRssMb: Math.round(this.peakRssMb),
445
+ eventCount: this.history.length,
446
+ };
447
+ }
448
+
449
+ /** Telepítve van-e a watchdog. */
450
+ isInstalled(): boolean {
451
+ return this.installed;
452
+ }
453
+
454
+ /**
455
+ * Leállítja a watchdogot + visszaállítja az állapotot. Graceful shutdown-hoz ÉS a
456
+ * spec-ek `afterEach`-éhez (a singleton-state ne szivárogjon a tesztek közt).
457
+ */
458
+ _teardownForTesting(): void {
459
+ if (this.timer) {
460
+ clearInterval(this.timer);
461
+ this.timer = null;
462
+ }
463
+ if (this.gcObserver) {
464
+ try { this.gcObserver.disconnect(); } catch { /* best-effort */ }
465
+ this.gcObserver = null;
466
+ }
467
+ if (this.crashHandler) {
468
+ try { process.removeListener('uncaughtException', this.crashHandler); } catch { /* best-effort */ }
469
+ this.crashHandler = null;
470
+ }
471
+ this.installed = false;
472
+ this.state = 'normal';
473
+ this.history.length = 0;
474
+ this.peakHeapPct = 0;
475
+ this.peakRssMb = 0;
476
+ this.gcEvents = [];
477
+ this.criticalStreak = 0;
478
+ this.installedAt = 0;
479
+ this.onCritical = undefined;
480
+ }
481
+ }