@futdevpro/nts-dynamo 1.15.91 → 1.15.94
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.
- package/.c8rc.json +26 -26
- package/.copilot/patterns.json +7 -7
- package/.cursor/rules/__assistant_guide.mdc +30 -30
- package/.cursor/rules/_ag_backend-structure.mdc +85 -85
- package/.cursor/rules/_ag_backend.mdc +16 -16
- package/.cursor/rules/_ag_frontend-structure.mdc +86 -86
- package/.cursor/rules/_ag_frontend.mdc +39 -39
- package/.cursor/rules/_ag_import-rules.mdc +44 -44
- package/.cursor/rules/_ag_naming.mdc +115 -115
- package/.cursor/rules/_ag_should-be.mdc +6 -6
- package/.cursor/rules/ai_development_guide.md +60 -60
- package/.cursor/rules/cursor-rules.md +160 -160
- package/.cursor/rules/default-command.mdc +464 -464
- package/.cursor/rules/error_code_pattern.md +39 -39
- package/.cursor/rules/saved rule mcp server use.md +15 -15
- package/.vscode/settings.json +10 -10
- package/HOWTO.md +15 -15
- package/LICENSE +21 -21
- package/__documentations/nts-integration-tests-2026-03-17.md +26 -26
- package/__documentations/plans/BEDROCK-HYPERPLAN.md +95 -95
- package/_specifications/BACKLOG.md +92 -92
- package/_specifications/TODO.md +15 -15
- package/_specifications/agent.md +138 -138
- package/build/_models/interfaces/db-query-options.interface.d.ts +33 -0
- package/build/_models/interfaces/db-query-options.interface.d.ts.map +1 -0
- package/build/_models/interfaces/db-query-options.interface.js +3 -0
- package/build/_models/interfaces/db-query-options.interface.js.map +1 -0
- package/build/_services/base/data.service.d.ts +3 -1
- package/build/_services/base/data.service.d.ts.map +1 -1
- package/build/_services/base/data.service.js +5 -2
- package/build/_services/base/data.service.js.map +1 -1
- package/build/_services/base/db.service.d.ts +3 -1
- package/build/_services/base/db.service.d.ts.map +1 -1
- package/build/_services/base/db.service.js +23 -4
- package/build/_services/base/db.service.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/eslint.config.js +3 -3
- package/nodemon.json +24 -24
- package/package.json +2 -2
- package/pnpm-workspace.yaml +5 -5
- package/scripts/run-coverage-tests.js +28 -28
- package/spec/support/helpers/spec-reporter-loader.js +359 -359
- package/spec/support/helpers/ts-node-helper.js +93 -93
- package/spec/support/jasmine.coverage.json +24 -24
- package/spec/support/jasmine.json +24 -24
- package/src/_collections/archive.util.spec.ts +57 -57
- package/src/_collections/archive.util.ts +18 -18
- package/src/_collections/atlas-default-db-options.const.ts +9 -9
- package/src/_collections/default-fallback-cache-max-age.const.spec.ts +11 -11
- package/src/_collections/default-fallback-cache-max-age.const.ts +2 -2
- package/src/_collections/default-not-found-page.const.spec.ts +19 -19
- package/src/_collections/default-not-found-page.const.ts +22 -22
- package/src/_collections/default-socket-path.const.spec.ts +12 -12
- package/src/_collections/default-socket-path.const.ts +2 -2
- package/src/_collections/get-environment-settings.util.spec.ts +210 -210
- package/src/_collections/get-environment-settings.util.ts +48 -48
- package/src/_collections/global-settings.const.ts +109 -109
- package/src/_collections/sample.env +21 -21
- package/src/_collections/star.controller.spec.ts +224 -224
- package/src/_collections/star.controller.ts +129 -129
- package/src/_enums/data-model-type.enum.ts +14 -14
- package/src/_enums/data-service-function.enum.ts +24 -24
- package/src/_enums/predefined-data-types.enum.ts +16 -16
- package/src/_enums/route-security.enum.ts +12 -12
- package/src/_models/control-models/api-call-params.control-model.spec.ts +152 -152
- package/src/_models/control-models/api-call-params.control-model.ts +142 -142
- package/src/_models/control-models/app-ext-system-controls.control-model.spec.ts +52 -52
- package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
- package/src/_models/control-models/app-params.control-model.spec.ts +225 -225
- package/src/_models/control-models/app-params.control-model.ts +136 -136
- package/src/_models/control-models/app-system-controls.control-model.spec.ts +31 -31
- package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
- package/src/_models/control-models/endpoint-params.control-model.spec.ts +627 -627
- package/src/_models/control-models/endpoint-params.control-model.ts +627 -627
- package/src/_models/control-models/http-settings.control-model.spec.ts +77 -77
- package/src/_models/control-models/http-settings.control-model.ts +37 -37
- package/src/_models/control-models/system-control.control-model.spec.ts +27 -27
- package/src/_models/control-models/system-control.control-model.ts +12 -12
- package/src/_models/interfaces/certification-settings.interface.ts +7 -7
- package/src/_models/interfaces/db-query-options.interface.ts +33 -0
- package/src/_models/interfaces/environment-settings.interface.ts +59 -59
- package/src/_models/interfaces/global-log-settings.interface.ts +171 -171
- package/src/_models/interfaces/global-service-settings.interface.ts +47 -47
- package/src/_models/interfaces/global-settings.interface.ts +244 -244
- package/src/_models/interfaces/routing-module-settings.interface.ts +21 -21
- package/src/_models/interfaces/static-client-settings.interface.spec.ts +29 -29
- package/src/_models/interfaces/static-client-settings.interface.ts +28 -28
- package/src/_models/types/db-update.type.ts +100 -100
- package/src/_modules/ai/_models/ai-input-interfaces.ts +117 -117
- package/src/_modules/ai/_models/ai-test-generation-result.interface.ts +16 -16
- package/src/_modules/ai/_modules/anthropic/_services/aai-user-key.control-service.ts +138 -138
- package/src/_modules/ai/_modules/anthropic/index.ts +5 -5
- package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.spec.ts +242 -242
- package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.ts +639 -639
- package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.spec.ts +295 -295
- package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.ts +518 -518
- package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.spec.ts +209 -209
- package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.ts +85 -85
- package/src/_modules/ai/_modules/document-ai/_enums/dai-compare-result-type.enum.ts +7 -7
- package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-chunk.data-model.ts +146 -146
- package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-page.data-model.ts +162 -162
- package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-document.data-model.ts +99 -99
- package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-code-chunk.interface.ts +68 -68
- package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-chunk-compare-result.interface.ts +18 -18
- package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-page-compare-result.interface.ts +19 -19
- package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-document-compare-result.interface.ts +25 -25
- package/src/_modules/ai/_modules/document-ai/index.ts +30 -30
- package/src/_modules/ai/_modules/fdp-ai/_services/fdpai-user-key.control-service.ts +189 -189
- package/src/_modules/ai/_modules/fdp-ai/index.ts +5 -5
- package/src/_modules/ai/_modules/open-ai/_collections/oai-global-settings.const.ts +9 -9
- package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests-hu.conts.ts +82 -82
- package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests.conts.ts +75 -75
- package/src/_modules/ai/_modules/open-ai/_enums/oai-gpt-message-role.enum.ts +45 -45
- package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-global-settings.interface.ts +7 -7
- package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-gpt-message.interface.ts +7 -7
- package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-llm-predefined-requests.interface.ts +57 -57
- package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-doc-chunk-data.service.ts +292 -292
- package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-document.data-service.spec.ts +342 -342
- package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.spec.ts +550 -550
- package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.ts +630 -630
- package/src/_modules/ai/_modules/open-ai/_services/oai-embedding.control-service.spec.ts +332 -332
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.spec.ts +462 -462
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.ts +634 -634
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.spec.ts +489 -489
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.tools.spec.ts +173 -173
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +1033 -1033
- package/src/_modules/ai/_modules/open-ai/_services/oai-user-key.control-service.ts +157 -157
- package/src/_modules/ai/_services/ai-embedding-mock.service.spec.ts +115 -115
- package/src/_modules/ai/_services/ai-embedding-mock.service.ts +212 -212
- package/src/_modules/ai/_services/ai-embedding-provider.registry.spec.ts +110 -110
- package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +110 -110
- package/src/_modules/ai/_services/ai-embedding.service-base.spec.ts +98 -98
- package/src/_modules/ai/_services/ai-embedding.service-base.ts +48 -48
- package/src/_modules/ai/_services/ai-llm-chat.service-base.spec.ts +229 -229
- package/src/_modules/ai/_services/ai-llm-chat.service-base.ts +68 -68
- package/src/_modules/ai/_services/ai-llm.service-base.spec.ts +250 -250
- package/src/_modules/ai/_services/ai-llm.service-base.ts +519 -519
- package/src/_modules/ai/_services/ai-provider.service-base.spec.ts +158 -158
- package/src/_modules/ai/_services/ai-user-key.service-base.ts +59 -59
- package/src/_modules/ai/_services/lmstudio-embedding.control-service.spec.ts +197 -197
- package/src/_modules/ai/_services/lmstudio-embedding.control-service.ts +371 -371
- package/src/_modules/ai/index.ts +23 -23
- package/src/_modules/assistant/_collections/ass-global-settings.const.ts +13 -13
- package/src/_modules/assistant/_collections/ass.util.spec.ts +176 -176
- package/src/_modules/assistant/_collections/ass.util.ts +50 -50
- package/src/_modules/assistant/_models/ass-global-settings.interface.ts +15 -15
- package/src/_modules/assistant/_services/ass-io.control-service.spec.ts +140 -140
- package/src/_modules/assistant/_services/ass-main.control-service.spec.ts +192 -192
- package/src/_modules/assistant/_services/ass-main.control-service.ts +107 -107
- package/src/_modules/bot/_collections/bot-default-commands.const.ts +12 -12
- package/src/_modules/bot/_collections/bot-global-settings.const.ts +39 -39
- package/src/_modules/bot/_models/bot-channel-wrapper.interface.ts +62 -62
- package/src/_modules/bot/_models/bot-command.interface.ts +8 -8
- package/src/_modules/bot/_models/bot-global-settings.interface.ts +96 -96
- package/src/_modules/bot/_models/bot-last-mention-date.interface.ts +6 -6
- package/src/_modules/bot/_models/bot-last-message-date.interface.ts +5 -5
- package/src/_modules/bot/_models/bot-user-wrapper.interface.ts +41 -41
- package/src/_modules/bot/_modules/discord-bot/_models/dib-platform.types.ts +9 -9
- package/src/_modules/bot/_modules/discord-bot/_services/dib-messaging-provider.control-service.spec.ts +431 -431
- package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.spec.ts +160 -160
- package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.ts +55 -55
- package/src/_modules/bot/_modules/dynamo-bot/_models/dyb-platform.types.ts +15 -15
- package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.spec.ts +374 -374
- package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.ts +447 -447
- package/src/_modules/bot/_modules/dynamo-bot/index.ts +15 -15
- package/src/_modules/bot/_modules/slack-bot/_models/slb-platform.types.ts +9 -9
- package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.spec.ts +344 -344
- package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.ts +197 -197
- package/src/_modules/bot/_modules/teams-bot/_models/teb-platform.types.ts +9 -9
- package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.spec.ts +345 -345
- package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.ts +197 -197
- package/src/_modules/bot/_services/bot-commands.control-service.spec.ts +116 -116
- package/src/_modules/bot/_services/bot-io.control-service.spec.ts +285 -285
- package/src/_modules/bot/_services/bot-main.control-service.spec.ts +208 -208
- package/src/_modules/bot/_services/bot-messaging-provider.service-base.spec.ts +349 -349
- package/src/_modules/bot/_services/bot-routines.control-service.spec.ts +111 -111
- package/src/_modules/custom-data/custom-data.controller.spec.ts +49 -49
- package/src/_modules/custom-data/custom-data.controller.ts +67 -67
- package/src/_modules/custom-data/custom-data.data-service.spec.ts +54 -54
- package/src/_modules/custom-data/custom-data.data-service.ts +21 -21
- package/src/_modules/custom-data/get-custom-data-routing-module.util.spec.ts +28 -28
- package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +24 -24
- package/src/_modules/custom-data/index.ts +9 -9
- package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.spec.ts +161 -161
- package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.ts +203 -203
- package/src/_modules/data-readers/_models/interfaces/dynts-sqlite-reader.interface.ts +33 -33
- package/src/_modules/data-readers/index.ts +11 -11
- package/src/_modules/defaults/_collections/default-endpoints.util.ts +487 -487
- package/src/_modules/defaults/_models/default-user.data-model.ts +72 -72
- package/src/_modules/defaults/_services/default-auth.service.spec.ts +269 -269
- package/src/_modules/defaults/_services/default-auth.service.ts +177 -177
- package/src/_modules/defaults/_services/default-socket-events.service.spec.ts +42 -42
- package/src/_modules/defaults/_services/default-socket-events.service.ts +61 -61
- package/src/_modules/defaults/_services/default-user.data-service.spec.ts +187 -187
- package/src/_modules/defaults/_services/default-user.data-service.ts +98 -98
- package/src/_modules/defaults/index.ts +17 -17
- package/src/_modules/discord-assistant/_collections/dias-global-settings.const.ts +19 -19
- package/src/_modules/discord-assistant/_collections/dias.util.spec.ts +366 -366
- package/src/_modules/discord-assistant/_collections/dias.util.ts +132 -132
- package/src/_modules/discord-assistant/_models/dias-global-settings.interface.ts +19 -19
- package/src/_modules/discord-assistant/_models/dias-knowledge.data-model.ts +52 -52
- package/src/_modules/discord-assistant/_services/dias-chunk.data-service.ts +177 -177
- package/src/_modules/discord-assistant/_services/dias-io.control-service.spec.ts +108 -108
- package/src/_modules/discord-assistant/_services/dias-io.control-service.ts +69 -69
- package/src/_modules/discord-assistant/_services/dias-main.control-service.spec.ts +22 -22
- package/src/_modules/discord-assistant/_services/dias-main.control-service.ts +27 -27
- package/src/_modules/discord-assistant/_services/dias.service-base.spec.ts +195 -195
- package/src/_modules/discord-assistant/_services/dias.service-base.ts +76 -76
- package/src/_modules/discord-assistant/index.ts +38 -38
- package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.spec.ts +34 -34
- package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.ts +11 -11
- package/src/_modules/discord-assistant-voiced/index.ts +36 -36
- package/src/_modules/discord-bot/_collections/dibo-default-commands.const.ts +16 -16
- package/src/_modules/discord-bot/_collections/dibo-global-settings.conts.ts +55 -55
- package/src/_modules/discord-bot/_collections/dibo-operations.util.spec.ts +214 -214
- package/src/_modules/discord-bot/_collections/dibo-operations.util.ts +387 -387
- package/src/_modules/discord-bot/_models/dibo-command.interface.ts +12 -12
- package/src/_modules/discord-bot/_models/dibo-global-settings.interface.ts +98 -98
- package/src/_modules/discord-bot/_models/dibo-last-mention-date.inteface.ts +7 -7
- package/src/_modules/discord-bot/_models/dibo-last-message-date.interface.ts +6 -6
- package/src/_modules/discord-bot/_services/dibo-commands.control-service.spec.ts +154 -154
- package/src/_modules/discord-bot/_services/dibo-commands.control-service.ts +153 -153
- package/src/_modules/discord-bot/_services/dibo-io.control-service.spec.ts +264 -264
- package/src/_modules/discord-bot/_services/dibo-io.control-service.ts +306 -306
- package/src/_modules/discord-bot/_services/dibo-main.control-service.spec.ts +408 -408
- package/src/_modules/discord-bot/_services/dibo-main.control-service.ts +487 -487
- package/src/_modules/discord-bot/_services/dibo-routines.control-service.spec.ts +105 -105
- package/src/_modules/discord-bot/index.ts +36 -36
- package/src/_modules/local-vector-search/_enums/lvs-search-mode.enum.ts +35 -35
- package/src/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.ts +59 -59
- package/src/_modules/local-vector-search/_models/lvs-search-result.interface.ts +17 -17
- package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.spec.ts +418 -418
- package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.ts +276 -276
- package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.spec.ts +480 -480
- package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.ts +416 -416
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +198 -198
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +146 -146
- package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.spec.ts +167 -167
- package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +106 -106
- package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.spec.ts +507 -507
- package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.ts +272 -272
- package/src/_modules/local-vector-search/index.ts +16 -16
- package/src/_modules/logs/index.ts +11 -11
- package/src/_modules/mcp/_models/interfaces/dynts-mcp.interface.ts +111 -111
- package/src/_modules/mcp/_services/dynts-mcp-server.service-base.spec.ts +142 -142
- package/src/_modules/mcp/_services/dynts-mcp-server.service-base.ts +120 -120
- package/src/_modules/mcp/_services/dynts-mcp.adapter.ts +168 -168
- package/src/_modules/mcp/index.ts +13 -13
- package/src/_modules/messaging/README.md +354 -354
- package/src/_modules/messaging/_collections/get-messaging-routing-module.util.ts +26 -26
- package/src/_modules/messaging/_collections/msg-global-settings.const.ts +22 -22
- package/src/_modules/messaging/_collections/msg.util.spec.ts +226 -226
- package/src/_modules/messaging/_models/msg-global-settings.interface.ts +37 -37
- package/src/_modules/messaging/_services/msg-conversation.data-service.ts +146 -146
- package/src/_modules/messaging/_services/msg-events.service.spec.ts +219 -219
- package/src/_modules/messaging/_services/msg-events.service.ts +267 -267
- package/src/_modules/messaging/_services/msg-integration.control-service.ts +179 -179
- package/src/_modules/messaging/_services/msg-main.control-service.spec.ts +147 -147
- package/src/_modules/messaging/_services/msg-main.control-service.ts +571 -571
- package/src/_modules/messaging/_services/msg-message.data-service.ts +129 -129
- package/src/_modules/messaging/_services/msg.controller.spec.ts +201 -201
- package/src/_modules/messaging/index.ts +30 -30
- package/src/_modules/mock/app-extended-server.mock.ts +201 -201
- package/src/_modules/mock/app-integration-test.mock.ts +51 -51
- package/src/_modules/mock/app-params.mock.spec.ts +21 -21
- package/src/_modules/mock/app-params.mock.ts +9 -9
- package/src/_modules/mock/app-server.mock.ts +188 -188
- package/src/_modules/mock/auth-service.mock.spec.ts +47 -47
- package/src/_modules/mock/auth-service.mock.ts +28 -28
- package/src/_modules/mock/controller.mock.spec.ts +26 -26
- package/src/_modules/mock/controller.mock.ts +16 -16
- package/src/_modules/mock/data-model.mock.spec.ts +111 -111
- package/src/_modules/mock/data-model.mock.ts +82 -82
- package/src/_modules/mock/email-service-collection.mock.spec.ts +24 -24
- package/src/_modules/mock/email-service-collection.mock.ts +15 -15
- package/src/_modules/mock/email-service.mock.spec.ts +17 -17
- package/src/_modules/mock/email-service.mock.ts +20 -20
- package/src/_modules/mock/email-template.mock.html +14 -14
- package/src/_modules/mock/endpoint.mock.ts +91 -91
- package/src/_modules/mock/socket-client.mock.spec.ts +40 -40
- package/src/_modules/mock/socket-client.mock.ts +45 -45
- package/src/_modules/mock/socket-server.mock.spec.ts +44 -44
- package/src/_modules/mock/socket-server.mock.ts +46 -46
- package/src/_modules/oauth2/_routes/oauth2.controller.spec.ts +107 -107
- package/src/_modules/oauth2/_routes/oauth2.controller.ts +98 -98
- package/src/_modules/oauth2/_services/oauth2.auth-service.spec.ts +254 -254
- package/src/_modules/oauth2/_services/oauth2.auth-service.ts +232 -232
- package/src/_modules/oauth2/_services/oauth2.control-service.spec.ts +585 -585
- package/src/_modules/oauth2/_services/oauth2.control-service.ts +653 -653
- package/src/_modules/oauth2/index.ts +17 -17
- package/src/_modules/scheduler/_models/scheduler-job.interface.ts +35 -35
- package/src/_modules/scheduler/get-scheduler-routing-module.util.ts +33 -33
- package/src/_modules/scheduler/index.ts +8 -8
- package/src/_modules/scheduler/scheduler.controller.spec.ts +42 -42
- package/src/_modules/scheduler/scheduler.controller.ts +69 -69
- package/src/_modules/scheduler/scheduler.service.spec.ts +141 -141
- package/src/_modules/scheduler/scheduler.service.ts +176 -176
- package/src/_modules/scoped-config/_enums/dynts-scoped-config-level.enum.ts +22 -22
- package/src/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.ts +81 -81
- package/src/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.ts +107 -107
- package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.spec.ts +306 -306
- package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.ts +295 -295
- package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.spec.ts +118 -118
- package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.ts +105 -105
- package/src/_modules/scoped-config/index.ts +17 -17
- package/src/_modules/server/errors/errors.control-service.spec.ts +238 -238
- package/src/_modules/server/errors/errors.control-service.ts +100 -100
- package/src/_modules/server/errors/errors.controller.spec.ts +268 -268
- package/src/_modules/server/errors/errors.controller.ts +515 -515
- package/src/_modules/server/errors/errors.data-service.spec.ts +480 -480
- package/src/_modules/server/index.ts +30 -30
- package/src/_modules/server/server-status/server-status-snapshot.control-service.spec.ts +70 -70
- package/src/_modules/server/server-status/server-status-snapshot.control-service.ts +17 -17
- package/src/_modules/server/server-status/server-status-snapshot.data-service.spec.ts +77 -77
- package/src/_modules/server/server-status/server-status-snapshot.data-service.ts +37 -37
- package/src/_modules/server/server-status/server-status.control-service.spec.ts +576 -576
- package/src/_modules/server/server-status/server-status.control-service.ts +396 -396
- package/src/_modules/server/server-status/server-status.controller.spec.ts +255 -255
- package/src/_modules/server/server-status/server-status.controller.ts +272 -272
- package/src/_modules/socket/_enums/socket-security.enum.ts +11 -11
- package/src/_modules/socket/_models/socket-client-service-params.control-model.spec.ts +32 -32
- package/src/_modules/socket/_models/socket-client-service-params.control-model.ts +22 -22
- package/src/_modules/socket/_models/socket-presence.control-model.spec.ts +164 -164
- package/src/_modules/socket/_models/socket-presence.control-model.ts +210 -210
- package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +46 -46
- package/src/_modules/socket/_models/socket-server-service-params.control-model.ts +22 -22
- package/src/_modules/socket/_services/socket-client.service.spec.ts +15 -15
- package/src/_modules/socket/_services/socket-client.service.ts +260 -260
- package/src/_modules/socket/_services/socket-server.service.spec.ts +11 -11
- package/src/_modules/socket/app-extended.integration.spec.ts +85 -85
- package/src/_modules/socket/app-extended.server.ts +630 -630
- package/src/_modules/socket/index.ts +42 -42
- package/src/_modules/test/get-test-routing-module.util.spec.ts +28 -28
- package/src/_modules/test/get-test-routing-module.util.ts +23 -23
- package/src/_modules/test/index.ts +11 -11
- package/src/_modules/test/test.controller.spec.ts +72 -72
- package/src/_modules/test/test.controller.ts +115 -115
- package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
- package/src/_modules/usage/index.ts +15 -15
- package/src/_modules/usage/usage.controller.spec.ts +81 -81
- package/src/_modules/usage/usage.controller.ts +126 -126
- package/src/_modules/usage/usage.data-service.spec.ts +332 -332
- package/src/_modules/usage/usage.data-service.ts +185 -185
- package/src/_services/base/api.service-base.spec.ts +125 -125
- package/src/_services/base/api.service-base.ts +74 -74
- package/src/_services/base/archive-data.service.spec.ts +209 -209
- package/src/_services/base/archive-data.service.ts +224 -224
- package/src/_services/base/data.service.spec.ts +729 -729
- package/src/_services/base/data.service.ts +2744 -2740
- package/src/_services/base/db.service.spec.ts +160 -73
- package/src/_services/base/db.service.ts +1592 -1575
- package/src/_services/base/singleton.service-base.spec.ts +28 -28
- package/src/_services/base/singleton.service-base.ts +24 -24
- package/src/_services/base/singleton.service.spec.ts +114 -114
- package/src/_services/base/singleton.service.ts +38 -38
- package/src/_services/core/api.service.spec.ts +140 -140
- package/src/_services/core/auth.service.spec.ts +159 -159
- package/src/_services/core/auth.service.ts +174 -174
- package/src/_services/core/email.service.spec.ts +85 -85
- package/src/_services/core/email.service.ts +742 -742
- package/src/_services/core/global.service.spec.ts +292 -292
- package/src/_services/core/global.service.ts +487 -487
- package/src/_services/core/memory-guard.service.spec.ts +245 -245
- package/src/_services/core/memory-guard.service.ts +481 -481
- package/src/_services/core/service-collection.service.spec.ts +46 -46
- package/src/_services/core/service-collection.service.ts +6 -6
- package/src/_services/route/controller.service.spec.ts +53 -53
- package/src/_services/route/controller.service.ts +148 -148
- package/src/_services/route/routing-module.service.spec.ts +98 -98
- package/src/_services/route/routing-module.service.ts +330 -330
- package/src/_services/server/app.server.ts +1941 -1941
- package/src/_services/shared.static-service.spec.ts +99 -99
- package/src/_services/shared.static-service.ts +78 -78
- package/src/index.ts +100 -99
- package/tsconfig.app.json +12 -12
- package/tsconfig.json +42 -42
- package/.dynamo/logs/cicd-pipeline/output.log +0 -2856
- package/.dynamo/logs/cicd-pipeline/status.json +0 -74
|
@@ -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
|
+
}
|