@futdevpro/fsm-dynamo 1.16.38 → 1.16.40

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 (707) hide show
  1. package/.c8rc.json +26 -26
  2. package/.cursor/rules/__assistant_guide.mdc +30 -30
  3. package/.cursor/rules/__main.mdc +64 -64
  4. package/.cursor/rules/_ag_backend-structure.mdc +85 -85
  5. package/.cursor/rules/_ag_backend.mdc +16 -16
  6. package/.cursor/rules/_ag_debug.mdc +7 -7
  7. package/.cursor/rules/_ag_documentation_writing_rules.mdc +372 -372
  8. package/.cursor/rules/_ag_file-refactoring.mdc +113 -113
  9. package/.cursor/rules/_ag_fixes_rules.mdc +5 -5
  10. package/.cursor/rules/_ag_frontend-structure.mdc +86 -86
  11. package/.cursor/rules/_ag_frontend.mdc +39 -39
  12. package/.cursor/rules/_ag_import-rules.mdc +44 -44
  13. package/.cursor/rules/_ag_naming.mdc +115 -115
  14. package/.cursor/rules/_ag_running_commands.mdc +4 -4
  15. package/.cursor/rules/_ag_server-controller.mdc +5 -5
  16. package/.cursor/rules/_ag_should-be.mdc +6 -6
  17. package/.cursor/rules/_ag_swearing.mdc +47 -47
  18. package/.cursor/rules/ai_development_guide.md +60 -60
  19. package/.cursor/rules/ai_directives.md +114 -114
  20. package/.cursor/rules/cursor-rules.md +160 -160
  21. package/.cursor/rules/default-command.mdc +464 -464
  22. package/.cursor/rules/error_code_pattern.md +39 -39
  23. package/.cursor/rules/saved rule mcp server use.md +15 -15
  24. package/.dynamo/pipeline.cicd.config.json +157 -157
  25. package/.dynamo/version-bump.config.json +5 -5
  26. package/.github/workflows/main.yml +439 -439
  27. package/.husky/pre-commit +2 -2
  28. package/.vscode/settings.json +11 -11
  29. package/LICENSE +21 -21
  30. package/README.md +701 -693
  31. package/__documentations/2026-05-17-oai-compatible-providers-howto.md +282 -282
  32. package/__documentations/2026-06-01-fr047-fc-foundation.md +48 -48
  33. package/__documentations/2026-06-01-fr047-fr048-code-review.md +153 -153
  34. package/__documentations/2026-06-01-fr047-p1-function-calling-vocabulary.md +75 -75
  35. package/__documentations/2026-06-05-secret-mask-url-cred-any-scheme.md +37 -37
  36. package/__documentations/2026-06-06-ngx-models-merge-completion.md +100 -100
  37. package/__documentations/2026-06-22-dyfm-error-extends-error.md +109 -109
  38. package/__documentations/plans/00-MASTER-ngx-models-fsm-merge.md +75 -75
  39. package/__documentations/plans/01-naming-convention-DyNM-to-DyFM.md +103 -103
  40. package/__documentations/plans/02-consumer-rollout.md +84 -84
  41. package/_specifications/BACKLOG.md +43 -43
  42. package/build/_modules/crypto/_models/artifact-signature.interface.d.ts +37 -0
  43. package/build/_modules/crypto/_models/artifact-signature.interface.d.ts.map +1 -0
  44. package/build/_modules/crypto/_models/artifact-signature.interface.js +3 -0
  45. package/build/_modules/crypto/_models/artifact-signature.interface.js.map +1 -0
  46. package/build/_modules/crypto/_services/artifact-signature.control-service.d.ts +24 -0
  47. package/build/_modules/crypto/_services/artifact-signature.control-service.d.ts.map +1 -0
  48. package/build/_modules/crypto/_services/artifact-signature.control-service.js +281 -0
  49. package/build/_modules/crypto/_services/artifact-signature.control-service.js.map +1 -0
  50. package/build/_modules/crypto/index.d.ts +2 -0
  51. package/build/_modules/crypto/index.d.ts.map +1 -1
  52. package/build/_modules/crypto/index.js +2 -0
  53. package/build/_modules/crypto/index.js.map +1 -1
  54. package/build/_modules/socket/_models/sck-socket-event.control-model.d.ts +4 -6
  55. package/build/_modules/socket/_models/sck-socket-event.control-model.d.ts.map +1 -1
  56. package/build/_modules/socket/_models/sck-socket-event.control-model.js +9 -7
  57. package/build/_modules/socket/_models/sck-socket-event.control-model.js.map +1 -1
  58. package/eslint.config.js +4 -4
  59. package/nodemon.json +22 -22
  60. package/package.json +306 -306
  61. package/pnpm-workspace.yaml +2 -2
  62. package/scripts/_capture-out/ai-error-capture.json +398 -398
  63. package/scripts/ai-error-capture.js +171 -171
  64. package/scripts/ai-model-registry-check.js +132 -132
  65. package/scripts/crypto/CRYPTO-STABILITY-SOLUTION.md +196 -196
  66. package/scripts/crypto/README.md +113 -113
  67. package/scripts/crypto/demo-crypto-stability.js +120 -120
  68. package/scripts/crypto/stress-test-crypto.js +379 -379
  69. package/scripts/run-coverage-tests.js +24 -24
  70. package/scripts/verify-build-completeness.js +62 -62
  71. package/spec/support/helpers/ts-node-helper.js +9 -9
  72. package/spec/support/jasmine.coverage.json +23 -23
  73. package/spec/support/jasmine.json +24 -24
  74. package/src/_collections/constants/data-sizes.const.ts +40 -40
  75. package/src/_collections/constants/error-defaults.const.ts +20 -20
  76. package/src/_collections/constants/global-settings.const.spec.ts +32 -32
  77. package/src/_collections/constants/global-settings.const.ts +29 -29
  78. package/src/_collections/constants/numbers.const.ts +40 -40
  79. package/src/_collections/constants/times.const.ts +45 -45
  80. package/src/_collections/utils/array.util.spec.ts +306 -306
  81. package/src/_collections/utils/array.util.ts +813 -813
  82. package/src/_collections/utils/async.util.spec.ts +398 -398
  83. package/src/_collections/utils/async.util.ts +208 -208
  84. package/src/_collections/utils/data.util.spec.ts +345 -345
  85. package/src/_collections/utils/data.util.ts +226 -226
  86. package/src/_collections/utils/extract-error-message.util.spec.ts +204 -204
  87. package/src/_collections/utils/extract-error-message.util.ts +223 -223
  88. package/src/_collections/utils/json-error-helper.util.spec.ts +521 -521
  89. package/src/_collections/utils/json-error-helper.util.ts +301 -301
  90. package/src/_collections/utils/log.util.spec.ts +975 -975
  91. package/src/_collections/utils/log.util.ts +667 -667
  92. package/src/_collections/utils/math/box-bounds.spec.ts +73 -73
  93. package/src/_collections/utils/math/box-bounds.util.ts +183 -183
  94. package/src/_collections/utils/math/math.util.spec.ts +94 -94
  95. package/src/_collections/utils/math/math.util.ts +141 -141
  96. package/src/_collections/utils/math/random.util.spec.ts +82 -82
  97. package/src/_collections/utils/math/random.util.ts +139 -139
  98. package/src/_collections/utils/math/trigonometry.util.spec.ts +57 -57
  99. package/src/_collections/utils/math/trigonometry.util.ts +102 -102
  100. package/src/_collections/utils/math/vector2.util.spec.ts +94 -94
  101. package/src/_collections/utils/math/vector2.util.ts +653 -653
  102. package/src/_collections/utils/object.util.spec.ts +646 -646
  103. package/src/_collections/utils/object.util.ts +739 -739
  104. package/src/_collections/utils/regex/password-regex.util.spec.ts +51 -51
  105. package/src/_collections/utils/regex/password-regex.util.ts +65 -65
  106. package/src/_collections/utils/regex/regex.util.spec.ts +42 -42
  107. package/src/_collections/utils/regex/regex.util.ts +6 -6
  108. package/src/_collections/utils/regex/username-regex.util.spec.ts +61 -61
  109. package/src/_collections/utils/regex/username-regex.util.ts +35 -35
  110. package/src/_collections/utils/require-env.util.spec.ts +231 -231
  111. package/src/_collections/utils/require-env.util.ts +138 -138
  112. package/src/_collections/utils/round-list.util.spec.ts +79 -79
  113. package/src/_collections/utils/round-list.util.ts +162 -162
  114. package/src/_collections/utils/secret-mask.util.spec.ts +66 -66
  115. package/src/_collections/utils/secret-mask.util.ts +262 -262
  116. package/src/_collections/utils/stack.util.spec.ts +372 -372
  117. package/src/_collections/utils/stack.util.ts +164 -164
  118. package/src/_collections/utils/string-case.util.spec.ts +441 -441
  119. package/src/_collections/utils/string-case.util.ts +362 -362
  120. package/src/_collections/utils/string.util.spec.ts +975 -975
  121. package/src/_collections/utils/string.util.ts +449 -449
  122. package/src/_collections/utils/time.util.spec.ts +50 -50
  123. package/src/_collections/utils/time.util.ts +481 -481
  124. package/src/_collections/utils/type-cloning-facility.util.spec.ts +51 -51
  125. package/src/_collections/utils/type-cloning-facility.util.ts +168 -168
  126. package/src/_collections/utils/utilities.util.spec.ts +201 -201
  127. package/src/_collections/utils/utilities.util.ts +68 -68
  128. package/src/_collections/utils/uuid.util.spec.ts +30 -30
  129. package/src/_collections/utils/uuid.util.ts +50 -50
  130. package/src/_enums/basic-property-type.enum.ts +20 -20
  131. package/src/_enums/data-model-type.enum.ts +29 -29
  132. package/src/_enums/environment-flag.enum.ts +28 -28
  133. package/src/_enums/error-level.enum.ts +43 -43
  134. package/src/_enums/error-status.enum.ts +30 -30
  135. package/src/_enums/http/http-call-type.enum.ts +43 -43
  136. package/src/_enums/http/http-event-type.enum.ts +40 -40
  137. package/src/_enums/http/http-response-type.enum.ts +18 -18
  138. package/src/_enums/log-style.enum.ts +44 -44
  139. package/src/_enums/server-connection-status.enum.ts +6 -6
  140. package/src/_enums/time/day-of-week.enum.ts +55 -55
  141. package/src/_enums/time/month.enum.ts +25 -25
  142. package/src/_enums/time/relative-date.enum.ts +24 -24
  143. package/src/_models/control-models/data-model-params.control-model.spec.ts +156 -156
  144. package/src/_models/control-models/data-model-params.control-model.ts +362 -362
  145. package/src/_models/control-models/data-property-params.control-model.spec.ts +93 -93
  146. package/src/_models/control-models/data-property-params.control-model.ts +210 -210
  147. package/src/_models/control-models/error.control-model.spec.ts +975 -975
  148. package/src/_models/control-models/error.control-model.ts +1280 -1280
  149. package/src/_models/control-models/http/http-error-response.control-model.spec.ts +116 -116
  150. package/src/_models/control-models/http/http-error-response.control-model.ts +52 -52
  151. package/src/_models/control-models/http/http-headers.control-model.spec.ts +25 -25
  152. package/src/_models/control-models/http/http-headers.control-model.ts +124 -124
  153. package/src/_models/control-models/http/http-response.model-base.spec.ts +46 -46
  154. package/src/_models/control-models/http/http-response.model-base.ts +57 -57
  155. package/src/_models/control-models/poll.control-model.spec.ts +63 -63
  156. package/src/_models/control-models/poll.control-model.ts +151 -151
  157. package/src/_models/control-models/range-value.control-model.spec.ts +187 -187
  158. package/src/_models/control-models/range-value.control-model.ts +289 -289
  159. package/src/_models/control-models/server-status.control-model.spec.ts +66 -66
  160. package/src/_models/control-models/server-status.control-model.ts +94 -94
  161. package/src/_models/control-models/service-endpoint-settings-base.control-model.spec.ts +145 -145
  162. package/src/_models/control-models/service-endpoint-settings-base.control-model.ts +186 -186
  163. package/src/_models/data-models/errors.data-model.spec.ts +71 -71
  164. package/src/_models/data-models/errors.data-model.ts +200 -200
  165. package/src/_models/data-models/metadata.data-model.spec.ts +184 -184
  166. package/src/_models/data-models/metadata.data-model.ts +153 -153
  167. package/src/_models/interfaces/box-bounds.interface.ts +7 -7
  168. package/src/_models/interfaces/environment/global-log-settings.interface.ts +86 -86
  169. package/src/_models/interfaces/environment/global-settings.interface.ts +69 -69
  170. package/src/_models/interfaces/error-defaults.interface.ts +18 -18
  171. package/src/_models/interfaces/paged.interface.ts +12 -12
  172. package/src/_models/interfaces/random-weight.interface.ts +7 -7
  173. package/src/_models/interfaces/route-settings.interface.ts +15 -15
  174. package/src/_models/interfaces/search-query.interface.ts +23 -23
  175. package/src/_models/interfaces/search-result.interface.ts +5 -5
  176. package/src/_models/interfaces/server-error-statistics.interface.ts +24 -24
  177. package/src/_models/interfaces/vector2.interface.ts +20 -20
  178. package/src/_models/types/db-filter.type.ts +110 -110
  179. package/src/_models/types/db-sort.type.ts +4 -4
  180. package/src/_models/types/ds-filter.type.ts +68 -68
  181. package/src/_models/types/ds-sort.type.ts +21 -21
  182. package/src/_modules/ai/_collections/ai-model-ref.util.ts +88 -88
  183. package/src/_modules/ai/_collections/ai-model-registry.util.spec.ts +37 -37
  184. package/src/_modules/ai/_collections/ai-model-registry.util.ts +30 -30
  185. package/src/_modules/ai/_enums/ai-message-role.enum.ts +7 -7
  186. package/src/_modules/ai/_enums/ai-model-type.enum.ts +7 -7
  187. package/src/_modules/ai/_enums/ai-provider.enum.ts +7 -7
  188. package/src/_modules/ai/_models/ai-call-settings.interface.ts +84 -84
  189. package/src/_modules/ai/_models/ai-config.interface.ts +14 -14
  190. package/src/_modules/ai/_models/ai-embedding-request.interface.ts +20 -20
  191. package/src/_modules/ai/_models/ai-embedding-response.interface.ts +26 -26
  192. package/src/_modules/ai/_models/ai-llm-request.interface.ts +24 -24
  193. package/src/_modules/ai/_models/ai-llm-response.interface.ts +30 -30
  194. package/src/_modules/ai/_models/ai-message.interface.ts +12 -12
  195. package/src/_modules/ai/_models/ai-model-capabilities.interface.ts +35 -35
  196. package/src/_modules/ai/_models/ai-model-info.interface.ts +63 -63
  197. package/src/_modules/ai/_models/ai-model-settings-schema.interface.ts +52 -52
  198. package/src/_modules/ai/_models/ai-provider-capabilities.interface.ts +28 -28
  199. package/src/_modules/ai/_models/ai-settings.interface.ts +17 -17
  200. package/src/_modules/ai/_models/ai-tool-call.interface.ts +18 -18
  201. package/src/_modules/ai/_models/ai-tool-handler.type.ts +11 -11
  202. package/src/_modules/ai/_models/ai-tool-result.interface.ts +16 -16
  203. package/src/_modules/ai/_models/ai-tool.interface.ts +16 -16
  204. package/src/_modules/ai/_models/ai-user-provider-config.control-model.ts +29 -29
  205. package/src/_modules/ai/_modules/anthropic/_collections/aai-models.const.ts +135 -135
  206. package/src/_modules/ai/_modules/anthropic/_enums/aai-model.enum.ts +19 -19
  207. package/src/_modules/ai/_modules/anthropic/_models/aai-call-settings.control-model.spec.ts +28 -28
  208. package/src/_modules/ai/_modules/anthropic/_models/aai-call-settings.control-model.ts +23 -23
  209. package/src/_modules/ai/_modules/anthropic/_models/aai-client-options.interface.ts +10 -10
  210. package/src/_modules/ai/_modules/anthropic/_models/aai-settings.control-model.spec.ts +22 -22
  211. package/src/_modules/ai/_modules/anthropic/_models/aai-settings.control-model.ts +27 -27
  212. package/src/_modules/ai/_modules/anthropic/_models/aai-user-provider-config.control-model.ts +20 -20
  213. package/src/_modules/ai/_modules/anthropic/index.ts +14 -14
  214. package/src/_modules/ai/_modules/document-ai/_models/dai-embedding-info.interface.ts +12 -12
  215. package/src/_modules/ai/_modules/document-ai/_models/dai-vector-search-params.interface.ts +22 -22
  216. package/src/_modules/ai/_modules/document-ai/index.ts +4 -4
  217. package/src/_modules/ai/_modules/fdp-ai/_collections/fdpai-models.const.ts +40 -40
  218. package/src/_modules/ai/_modules/fdp-ai/_enums/fdpai-model.enum.ts +6 -6
  219. package/src/_modules/ai/_modules/fdp-ai/_models/fdpai-call-settings.control-model.ts +19 -19
  220. package/src/_modules/ai/_modules/fdp-ai/_models/fdpai-client-options.interface.ts +11 -11
  221. package/src/_modules/ai/_modules/fdp-ai/_models/fdpai-settings.control-model.ts +24 -24
  222. package/src/_modules/ai/_modules/fdp-ai/_models/fdpai-user-provider-config.control-model.ts +20 -20
  223. package/src/_modules/ai/_modules/fdp-ai/index.ts +11 -11
  224. package/src/_modules/ai/_modules/google-ai/_collections/gai-models.const.ts +161 -161
  225. package/src/_modules/ai/_modules/google-ai/_enums/gai-model.enum.ts +16 -16
  226. package/src/_modules/ai/_modules/google-ai/_models/gai-call-settings.control-model.spec.ts +28 -28
  227. package/src/_modules/ai/_modules/google-ai/_models/gai-call-settings.control-model.ts +21 -21
  228. package/src/_modules/ai/_modules/google-ai/_models/gai-client-options.interface.ts +8 -8
  229. package/src/_modules/ai/_modules/google-ai/_models/gai-settings.control-model.spec.ts +22 -22
  230. package/src/_modules/ai/_modules/google-ai/_models/gai-settings.control-model.ts +25 -25
  231. package/src/_modules/ai/_modules/google-ai/_models/gai-user-provider-config.control-model.ts +20 -20
  232. package/src/_modules/ai/_modules/google-ai/index.ts +14 -14
  233. package/src/_modules/ai/_modules/local-ai/_collections/lai-models.const.ts +59 -59
  234. package/src/_modules/ai/_modules/local-ai/_enums/lai-model.enum.ts +6 -6
  235. package/src/_modules/ai/_modules/local-ai/_models/lai-call-settings.control-model.spec.ts +28 -28
  236. package/src/_modules/ai/_modules/local-ai/_models/lai-call-settings.control-model.ts +18 -18
  237. package/src/_modules/ai/_modules/local-ai/_models/lai-client-options.interface.ts +9 -9
  238. package/src/_modules/ai/_modules/local-ai/_models/lai-settings.control-model.spec.ts +22 -22
  239. package/src/_modules/ai/_modules/local-ai/_models/lai-settings.control-model.ts +23 -23
  240. package/src/_modules/ai/_modules/local-ai/index.ts +14 -14
  241. package/src/_modules/ai/_modules/open-ai/_collections/oai-embedding-model-dimensions.const.ts +7 -7
  242. package/src/_modules/ai/_modules/open-ai/_collections/oai-models.const.ts +404 -404
  243. package/src/_modules/ai/_modules/open-ai/_enums/oai-model.enum.ts +179 -179
  244. package/src/_modules/ai/_modules/open-ai/_models/oai-call-settings.control-model.spec.ts +28 -28
  245. package/src/_modules/ai/_modules/open-ai/_models/oai-call-settings.control-model.ts +21 -21
  246. package/src/_modules/ai/_modules/open-ai/_models/oai-client-options.interface.ts +81 -81
  247. package/src/_modules/ai/_modules/open-ai/_models/oai-embedding-info.interface.ts +13 -13
  248. package/src/_modules/ai/_modules/open-ai/_models/oai-settings.control-model.spec.ts +22 -22
  249. package/src/_modules/ai/_modules/open-ai/_models/oai-settings.control-model.ts +25 -25
  250. package/src/_modules/ai/_modules/open-ai/_models/oai-user-provider-config.control-model.ts +21 -21
  251. package/src/_modules/ai/_modules/open-ai/index.ts +28 -28
  252. package/src/_modules/ai/index.ts +27 -27
  253. package/src/_modules/ci-tools/_enums/cit-ci-result-code.enum.ts +11 -11
  254. package/src/_modules/ci-tools/_enums/cit-ci-step-result-code.enum.ts +9 -9
  255. package/src/_modules/ci-tools/_models/cit-ci-result-info.data-models.spec.ts +58 -58
  256. package/src/_modules/ci-tools/_models/cit-ci-result-info.data-models.ts +77 -77
  257. package/src/_modules/ci-tools/_models/cit-ci-step-result.interface.ts +12 -12
  258. package/src/_modules/ci-tools/index.ts +8 -8
  259. package/src/_modules/crypto/_collections/crypto-v1.util.ts +225 -225
  260. package/src/_modules/crypto/_collections/crypto-v2.util.ts +331 -331
  261. package/src/_modules/crypto/_collections/crypto-v4.util.ts +702 -702
  262. package/src/_modules/crypto/_collections/crypto.util.db-envelope.spec.ts +51 -51
  263. package/src/_modules/crypto/_collections/crypto.util.edge.spec.ts +605 -605
  264. package/src/_modules/crypto/_collections/crypto.util.extra.spec.ts +642 -642
  265. package/src/_modules/crypto/_collections/crypto.util.simple.spec.ts +512 -512
  266. package/src/_modules/crypto/_collections/crypto.util.ts +748 -748
  267. package/src/_modules/crypto/_models/artifact-signature.interface.ts +39 -0
  268. package/src/_modules/crypto/_services/artifact-signature.control-service.spec.ts +223 -0
  269. package/src/_modules/crypto/_services/artifact-signature.control-service.ts +395 -0
  270. package/src/_modules/crypto/index.ts +16 -14
  271. package/src/_modules/custom-data/_collections/cud-module-settings.const.ts +21 -21
  272. package/src/_modules/custom-data/_models/cud.data-model.spec.ts +38 -38
  273. package/src/_modules/custom-data/_models/cud.data-model.ts +39 -39
  274. package/src/_modules/custom-data/index.ts +10 -10
  275. package/src/_modules/data-handler/_models/data-handler-settings.control-model.spec.ts +110 -110
  276. package/src/_modules/data-handler/_models/data-handler-settings.control-model.ts +110 -110
  277. package/src/_modules/data-handler/_models/data-handler.control-model.spec.ts +473 -473
  278. package/src/_modules/data-handler/_models/data-handler.control-model.ts +486 -486
  279. package/src/_modules/data-handler/_models/data-list-handler.control-model.spec.ts +263 -263
  280. package/src/_modules/data-handler/_models/data-list-handler.control-model.ts +252 -252
  281. package/src/_modules/data-handler/_models/data-search-handler.control-model.spec.ts +417 -417
  282. package/src/_modules/data-handler/_models/data-search-handler.control-model.ts +390 -390
  283. package/src/_modules/data-handler/_models/list-collector-data-handler.control-model.spec.ts +374 -374
  284. package/src/_modules/data-handler/_models/list-collector-data-handler.control-model.ts +274 -274
  285. package/src/_modules/data-handler/index.ts +6 -6
  286. package/src/_modules/game/_collections/audio-mixer.util.spec.ts +198 -198
  287. package/src/_modules/game/_collections/audio-mixer.util.ts +143 -143
  288. package/src/_modules/game/_collections/audio-scale.util.spec.ts +140 -140
  289. package/src/_modules/game/_collections/audio-scale.util.ts +129 -129
  290. package/src/_modules/game/_enums/audio-channel.enum.ts +22 -22
  291. package/src/_modules/game/_models/audio-mixer-config.interface.ts +53 -53
  292. package/src/_modules/game/_models/audio-mixer-settings.interface.ts +38 -38
  293. package/src/_modules/game/_models/audio-mixer-state.interface.ts +36 -36
  294. package/src/_modules/game/_models/audio-mixer.control-model.spec.ts +376 -376
  295. package/src/_modules/game/_models/audio-mixer.control-model.ts +419 -419
  296. package/src/_modules/game/_models/audio-scale.interface.ts +21 -21
  297. package/src/_modules/game/_models/seeded-random-state.interface.ts +21 -21
  298. package/src/_modules/game/_models/seeded-random.control-model.spec.ts +290 -290
  299. package/src/_modules/game/_models/seeded-random.control-model.ts +311 -311
  300. package/src/_modules/game/index.ts +15 -15
  301. package/src/_modules/location/_collections/assets/country-codes-ISO-3166.json +3239 -3239
  302. package/src/_modules/location/_collections/assets/country-divisions-ISO-3166-all-list.json +19035 -19035
  303. package/src/_modules/location/_collections/assets/country-divisions-ISO-3166.json +4993 -4993
  304. package/src/_modules/location/_collections/assets/country-phone-codes.json +1203 -1203
  305. package/src/_modules/location/_collections/assets/country-subdivisions/afghanistan.json +137 -137
  306. package/src/_modules/location/_collections/assets/country-subdivisions/albania.json +49 -49
  307. package/src/_modules/location/_collections/assets/country-subdivisions/algeria.json +193 -193
  308. package/src/_modules/location/_collections/assets/country-subdivisions/andorra.json +29 -29
  309. package/src/_modules/location/_collections/assets/country-subdivisions/angola.json +73 -73
  310. package/src/_modules/location/_collections/assets/country-subdivisions/antigua-and-barbuda.json +33 -33
  311. package/src/_modules/location/_collections/assets/country-subdivisions/argentina.json +97 -97
  312. package/src/_modules/location/_collections/assets/country-subdivisions/armenia.json +45 -45
  313. package/src/_modules/location/_collections/assets/country-subdivisions/australia.json +33 -33
  314. package/src/_modules/location/_collections/assets/country-subdivisions/austria.json +37 -37
  315. package/src/_modules/location/_collections/assets/country-subdivisions/azerbaijan.json +5 -5
  316. package/src/_modules/location/_collections/assets/country-subdivisions/bahamas.json +125 -125
  317. package/src/_modules/location/_collections/assets/country-subdivisions/bahrain.json +21 -21
  318. package/src/_modules/location/_collections/assets/country-subdivisions/bangladesh.json +29 -29
  319. package/src/_modules/location/_collections/assets/country-subdivisions/barbados.json +45 -45
  320. package/src/_modules/location/_collections/assets/country-subdivisions/belarus.json +29 -29
  321. package/src/_modules/location/_collections/assets/country-subdivisions/belgium.json +13 -13
  322. package/src/_modules/location/_collections/assets/country-subdivisions/belize.json +25 -25
  323. package/src/_modules/location/_collections/assets/country-subdivisions/benin.json +49 -49
  324. package/src/_modules/location/_collections/assets/country-subdivisions/bhutan.json +81 -81
  325. package/src/_modules/location/_collections/assets/country-subdivisions/bolivia.json +37 -37
  326. package/src/_modules/location/_collections/assets/country-subdivisions/bosnia-and-herzegovina.json +13 -13
  327. package/src/_modules/location/_collections/assets/country-subdivisions/botswana.json +65 -65
  328. package/src/_modules/location/_collections/assets/country-subdivisions/brazil.json +109 -109
  329. package/src/_modules/location/_collections/assets/country-subdivisions/brunei-darussalam.json +17 -17
  330. package/src/_modules/location/_collections/assets/country-subdivisions/bulgaria.json +113 -113
  331. package/src/_modules/location/_collections/assets/country-subdivisions/burkina-faso.json +53 -53
  332. package/src/_modules/location/_collections/assets/country-subdivisions/burundi.json +69 -69
  333. package/src/_modules/location/_collections/assets/country-subdivisions/cambodia.json +97 -97
  334. package/src/_modules/location/_collections/assets/country-subdivisions/cameroon.json +41 -41
  335. package/src/_modules/location/_collections/assets/country-subdivisions/canada.json +79 -79
  336. package/src/_modules/location/_collections/assets/country-subdivisions/cape-verde.json +9 -9
  337. package/src/_modules/location/_collections/assets/country-subdivisions/central-african-republic.json +69 -69
  338. package/src/_modules/location/_collections/assets/country-subdivisions/chad.json +89 -89
  339. package/src/_modules/location/_collections/assets/country-subdivisions/chile.json +61 -61
  340. package/src/_modules/location/_collections/assets/country-subdivisions/china.json +205 -205
  341. package/src/_modules/location/_collections/assets/country-subdivisions/colombia.json +133 -133
  342. package/src/_modules/location/_collections/assets/country-subdivisions/comoros.json +13 -13
  343. package/src/_modules/location/_collections/assets/country-subdivisions/congo-the-democratic-republic-of-the.json +45 -45
  344. package/src/_modules/location/_collections/assets/country-subdivisions/congo.json +49 -49
  345. package/src/_modules/location/_collections/assets/country-subdivisions/costa-rica.json +29 -29
  346. package/src/_modules/location/_collections/assets/country-subdivisions/cote-d-ivoire-republic-of.json +77 -77
  347. package/src/_modules/location/_collections/assets/country-subdivisions/croatia.json +85 -85
  348. package/src/_modules/location/_collections/assets/country-subdivisions/cuba.json +65 -65
  349. package/src/_modules/location/_collections/assets/country-subdivisions/cyprus.json +25 -25
  350. package/src/_modules/location/_collections/assets/country-subdivisions/czech-republic.json +57 -57
  351. package/src/_modules/location/_collections/assets/country-subdivisions/denmark.json +21 -21
  352. package/src/_modules/location/_collections/assets/country-subdivisions/djibouti.json +25 -25
  353. package/src/_modules/location/_collections/assets/country-subdivisions/dominica.json +41 -41
  354. package/src/_modules/location/_collections/assets/country-subdivisions/dominican-republic.json +41 -41
  355. package/src/_modules/location/_collections/assets/country-subdivisions/ecuador.json +97 -97
  356. package/src/_modules/location/_collections/assets/country-subdivisions/egypt.json +117 -117
  357. package/src/_modules/location/_collections/assets/country-subdivisions/el-salvador.json +57 -57
  358. package/src/_modules/location/_collections/assets/country-subdivisions/equatorial-guinea.json +9 -9
  359. package/src/_modules/location/_collections/assets/country-subdivisions/eritrea.json +25 -25
  360. package/src/_modules/location/_collections/assets/country-subdivisions/estonia.json +61 -61
  361. package/src/_modules/location/_collections/assets/country-subdivisions/ethiopia.json +45 -45
  362. package/src/_modules/location/_collections/assets/country-subdivisions/fiji.json +21 -21
  363. package/src/_modules/location/_collections/assets/country-subdivisions/finland.json +77 -77
  364. package/src/_modules/location/_collections/assets/country-subdivisions/france.json +133 -133
  365. package/src/_modules/location/_collections/assets/country-subdivisions/gabon.json +37 -37
  366. package/src/_modules/location/_collections/assets/country-subdivisions/gambia.json +25 -25
  367. package/src/_modules/location/_collections/assets/country-subdivisions/georgia.json +49 -49
  368. package/src/_modules/location/_collections/assets/country-subdivisions/germany.json +65 -65
  369. package/src/_modules/location/_collections/assets/country-subdivisions/ghana.json +41 -41
  370. package/src/_modules/location/_collections/assets/country-subdivisions/greece.json +53 -53
  371. package/src/_modules/location/_collections/assets/country-subdivisions/greenland.json +17 -17
  372. package/src/_modules/location/_collections/assets/country-subdivisions/grenada.json +29 -29
  373. package/src/_modules/location/_collections/assets/country-subdivisions/guatemala.json +89 -89
  374. package/src/_modules/location/_collections/assets/country-subdivisions/guinea-bissau.json +13 -13
  375. package/src/_modules/location/_collections/assets/country-subdivisions/guinea.json +33 -33
  376. package/src/_modules/location/_collections/assets/country-subdivisions/guyana.json +41 -41
  377. package/src/_modules/location/_collections/assets/country-subdivisions/haiti.json +41 -41
  378. package/src/_modules/location/_collections/assets/country-subdivisions/honduras.json +73 -73
  379. package/src/_modules/location/_collections/assets/country-subdivisions/hong-kong.json +4 -4
  380. package/src/_modules/location/_collections/assets/country-subdivisions/hungary.json +173 -173
  381. package/src/_modules/location/_collections/assets/country-subdivisions/iceland.json +37 -37
  382. package/src/_modules/location/_collections/assets/country-subdivisions/india.json +181 -181
  383. package/src/_modules/location/_collections/assets/country-subdivisions/indonesia.json +29 -29
  384. package/src/_modules/location/_collections/assets/country-subdivisions/iran-islamic-republic-of.json +125 -125
  385. package/src/_modules/location/_collections/assets/country-subdivisions/iraq.json +73 -73
  386. package/src/_modules/location/_collections/assets/country-subdivisions/ireland.json +17 -17
  387. package/src/_modules/location/_collections/assets/country-subdivisions/israel.json +25 -25
  388. package/src/_modules/location/_collections/assets/country-subdivisions/italy.json +81 -81
  389. package/src/_modules/location/_collections/assets/country-subdivisions/jamaica.json +57 -57
  390. package/src/_modules/location/_collections/assets/country-subdivisions/japan.json +189 -189
  391. package/src/_modules/location/_collections/assets/country-subdivisions/jordan.json +49 -49
  392. package/src/_modules/location/_collections/assets/country-subdivisions/kazakhstan.json +65 -65
  393. package/src/_modules/location/_collections/assets/country-subdivisions/kenya.json +33 -33
  394. package/src/_modules/location/_collections/assets/country-subdivisions/kiribati.json +13 -13
  395. package/src/_modules/location/_collections/assets/country-subdivisions/korea-democratic-people-s-republic-of.json +45 -45
  396. package/src/_modules/location/_collections/assets/country-subdivisions/korea-republic-of.json +69 -69
  397. package/src/_modules/location/_collections/assets/country-subdivisions/kuwait.json +25 -25
  398. package/src/_modules/location/_collections/assets/country-subdivisions/kyrgyzstan.json +33 -33
  399. package/src/_modules/location/_collections/assets/country-subdivisions/lao-people-s-democratic-republic.json +73 -73
  400. package/src/_modules/location/_collections/assets/country-subdivisions/latvia.json +477 -477
  401. package/src/_modules/location/_collections/assets/country-subdivisions/lebanon.json +33 -33
  402. package/src/_modules/location/_collections/assets/country-subdivisions/lesotho.json +41 -41
  403. package/src/_modules/location/_collections/assets/country-subdivisions/liberia.json +61 -61
  404. package/src/_modules/location/_collections/assets/country-subdivisions/libyan-arab-jamahiriya.json +89 -89
  405. package/src/_modules/location/_collections/assets/country-subdivisions/liechtenstein.json +45 -45
  406. package/src/_modules/location/_collections/assets/country-subdivisions/lithuania.json +41 -41
  407. package/src/_modules/location/_collections/assets/country-subdivisions/luxembourg.json +13 -13
  408. package/src/_modules/location/_collections/assets/country-subdivisions/macedonia-the-former-yugoslav-republic-of.json +337 -337
  409. package/src/_modules/location/_collections/assets/country-subdivisions/madagascar.json +25 -25
  410. package/src/_modules/location/_collections/assets/country-subdivisions/malawi.json +13 -13
  411. package/src/_modules/location/_collections/assets/country-subdivisions/malaysia.json +81 -81
  412. package/src/_modules/location/_collections/assets/country-subdivisions/maldives.json +33 -33
  413. package/src/_modules/location/_collections/assets/country-subdivisions/mali.json +37 -37
  414. package/src/_modules/location/_collections/assets/country-subdivisions/malta.json +273 -273
  415. package/src/_modules/location/_collections/assets/country-subdivisions/marshall-islands.json +9 -9
  416. package/src/_modules/location/_collections/assets/country-subdivisions/mauritania.json +53 -53
  417. package/src/_modules/location/_collections/assets/country-subdivisions/mauritius.json +69 -69
  418. package/src/_modules/location/_collections/assets/country-subdivisions/mexico.json +161 -161
  419. package/src/_modules/location/_collections/assets/country-subdivisions/micronesia-federated-states-of.json +17 -17
  420. package/src/_modules/location/_collections/assets/country-subdivisions/moldova-republic-of.json +149 -149
  421. package/src/_modules/location/_collections/assets/country-subdivisions/monaco.json +69 -69
  422. package/src/_modules/location/_collections/assets/country-subdivisions/mongolia.json +89 -89
  423. package/src/_modules/location/_collections/assets/country-subdivisions/montenegro.json +93 -93
  424. package/src/_modules/location/_collections/assets/country-subdivisions/morocco.json +65 -65
  425. package/src/_modules/location/_collections/assets/country-subdivisions/mozambique.json +45 -45
  426. package/src/_modules/location/_collections/assets/country-subdivisions/myanmar.json +57 -57
  427. package/src/_modules/location/_collections/assets/country-subdivisions/namibia.json +57 -57
  428. package/src/_modules/location/_collections/assets/country-subdivisions/nauru.json +57 -57
  429. package/src/_modules/location/_collections/assets/country-subdivisions/nepal.json +21 -21
  430. package/src/_modules/location/_collections/assets/country-subdivisions/netherlands.json +91 -91
  431. package/src/_modules/location/_collections/assets/country-subdivisions/new-zealand.json +111 -111
  432. package/src/_modules/location/_collections/assets/country-subdivisions/nicaragua.json +69 -69
  433. package/src/_modules/location/_collections/assets/country-subdivisions/niger.json +33 -33
  434. package/src/_modules/location/_collections/assets/country-subdivisions/nigeria.json +149 -149
  435. package/src/_modules/location/_collections/assets/country-subdivisions/norway.json +85 -85
  436. package/src/_modules/location/_collections/assets/country-subdivisions/oman.json +37 -37
  437. package/src/_modules/location/_collections/assets/country-subdivisions/pakistan.json +33 -33
  438. package/src/_modules/location/_collections/assets/country-subdivisions/palau.json +65 -65
  439. package/src/_modules/location/_collections/assets/country-subdivisions/palestinian-territory-occupied.json +65 -65
  440. package/src/_modules/location/_collections/assets/country-subdivisions/panama.json +53 -53
  441. package/src/_modules/location/_collections/assets/country-subdivisions/papua-new-guinea.json +81 -81
  442. package/src/_modules/location/_collections/assets/country-subdivisions/paraguay.json +73 -73
  443. package/src/_modules/location/_collections/assets/country-subdivisions/peru.json +105 -105
  444. package/src/_modules/location/_collections/assets/country-subdivisions/philippines.json +69 -69
  445. package/src/_modules/location/_collections/assets/country-subdivisions/poland.json +65 -65
  446. package/src/_modules/location/_collections/assets/country-subdivisions/portugal.json +81 -81
  447. package/src/_modules/location/_collections/assets/country-subdivisions/qatar.json +29 -29
  448. package/src/_modules/location/_collections/assets/country-subdivisions/romania.json +169 -169
  449. package/src/_modules/location/_collections/assets/country-subdivisions/russian-federation.json +499 -499
  450. package/src/_modules/location/_collections/assets/country-subdivisions/rwanda.json +21 -21
  451. package/src/_modules/location/_collections/assets/country-subdivisions/saint-helena-ascension-and-tristan-da-cunha.json +13 -13
  452. package/src/_modules/location/_collections/assets/country-subdivisions/saint-kitts-and-nevis.json +9 -9
  453. package/src/_modules/location/_collections/assets/country-subdivisions/saint-lucia.json +45 -45
  454. package/src/_modules/location/_collections/assets/country-subdivisions/saint-vincent-and-the-grenadines.json +25 -25
  455. package/src/_modules/location/_collections/assets/country-subdivisions/samoa.json +45 -45
  456. package/src/_modules/location/_collections/assets/country-subdivisions/san-marino.json +37 -37
  457. package/src/_modules/location/_collections/assets/country-subdivisions/sao-tome-and-principe.json +9 -9
  458. package/src/_modules/location/_collections/assets/country-subdivisions/saudi-arabia.json +53 -53
  459. package/src/_modules/location/_collections/assets/country-subdivisions/senegal.json +57 -57
  460. package/src/_modules/location/_collections/assets/country-subdivisions/serbia.json +9 -9
  461. package/src/_modules/location/_collections/assets/country-subdivisions/seychelles.json +101 -101
  462. package/src/_modules/location/_collections/assets/country-subdivisions/sierra-leone.json +17 -17
  463. package/src/_modules/location/_collections/assets/country-subdivisions/singapore.json +21 -21
  464. package/src/_modules/location/_collections/assets/country-subdivisions/slovakia.json +33 -33
  465. package/src/_modules/location/_collections/assets/country-subdivisions/slovenia.json +841 -841
  466. package/src/_modules/location/_collections/assets/country-subdivisions/solomon-islands.json +41 -41
  467. package/src/_modules/location/_collections/assets/country-subdivisions/somalia.json +73 -73
  468. package/src/_modules/location/_collections/assets/country-subdivisions/south-africa.json +37 -37
  469. package/src/_modules/location/_collections/assets/country-subdivisions/south-sudan.json +41 -41
  470. package/src/_modules/location/_collections/assets/country-subdivisions/spain.json +346 -346
  471. package/src/_modules/location/_collections/assets/country-subdivisions/sri-lanka.json +37 -37
  472. package/src/_modules/location/_collections/assets/country-subdivisions/sudan.json +69 -69
  473. package/src/_modules/location/_collections/assets/country-subdivisions/suriname.json +41 -41
  474. package/src/_modules/location/_collections/assets/country-subdivisions/swaziland.json +17 -17
  475. package/src/_modules/location/_collections/assets/country-subdivisions/sweden.json +85 -85
  476. package/src/_modules/location/_collections/assets/country-subdivisions/switzerland.json +105 -105
  477. package/src/_modules/location/_collections/assets/country-subdivisions/syrian-arab-republic.json +57 -57
  478. package/src/_modules/location/_collections/assets/country-subdivisions/taiwan-province-of-china.json +93 -93
  479. package/src/_modules/location/_collections/assets/country-subdivisions/tajikistan.json +17 -17
  480. package/src/_modules/location/_collections/assets/country-subdivisions/tanzania-united-republic-of.json +105 -105
  481. package/src/_modules/location/_collections/assets/country-subdivisions/thailand.json +313 -313
  482. package/src/_modules/location/_collections/assets/country-subdivisions/timor-leste.json +53 -53
  483. package/src/_modules/location/_collections/assets/country-subdivisions/togo.json +21 -21
  484. package/src/_modules/location/_collections/assets/country-subdivisions/tonga.json +21 -21
  485. package/src/_modules/location/_collections/assets/country-subdivisions/trinidad-and-tobago.json +65 -65
  486. package/src/_modules/location/_collections/assets/country-subdivisions/tunisia.json +97 -97
  487. package/src/_modules/location/_collections/assets/country-subdivisions/turkey.json +325 -325
  488. package/src/_modules/location/_collections/assets/country-subdivisions/turkmenistan.json +25 -25
  489. package/src/_modules/location/_collections/assets/country-subdivisions/tuvalu.json +33 -33
  490. package/src/_modules/location/_collections/assets/country-subdivisions/uganda.json +17 -17
  491. package/src/_modules/location/_collections/assets/country-subdivisions/ukraine.json +109 -109
  492. package/src/_modules/location/_collections/assets/country-subdivisions/united-arab-emirates.json +29 -29
  493. package/src/_modules/location/_collections/assets/country-subdivisions/united-kingdom.json +1196 -1196
  494. package/src/_modules/location/_collections/assets/country-subdivisions/united-states-minor-outlying-islands.json +37 -37
  495. package/src/_modules/location/_collections/assets/country-subdivisions/united-states.json +286 -286
  496. package/src/_modules/location/_collections/assets/country-subdivisions/uruguay.json +77 -77
  497. package/src/_modules/location/_collections/assets/country-subdivisions/uzbekistan.json +57 -57
  498. package/src/_modules/location/_collections/assets/country-subdivisions/vanuatu.json +25 -25
  499. package/src/_modules/location/_collections/assets/country-subdivisions/venezuela.json +101 -101
  500. package/src/_modules/location/_collections/assets/country-subdivisions/viet-nam.json +257 -257
  501. package/src/_modules/location/_collections/assets/country-subdivisions/yemen.json +85 -85
  502. package/src/_modules/location/_collections/assets/country-subdivisions/zambia.json +37 -37
  503. package/src/_modules/location/_collections/assets/country-subdivisions/zimbabwe.json +41 -41
  504. package/src/_modules/location/_collections/loc-country-divisions.const.ts +10 -10
  505. package/src/_modules/location/_collections/loc-country-isos.const.ts +8 -8
  506. package/src/_modules/location/_collections/loc-regions.util.spec.ts +61 -61
  507. package/src/_modules/location/_collections/loc-regions.util.ts +137 -137
  508. package/src/_modules/location/_collections/loc.util.spec.ts +52 -52
  509. package/src/_modules/location/_collections/loc.util.ts +74 -74
  510. package/src/_modules/location/_enums/loc-region.enum.ts +14 -14
  511. package/src/_modules/location/_enums/loc-sub-region.enum.ts +31 -31
  512. package/src/_modules/location/_enums/loc-subdivision-region-type.enum.ts +47 -47
  513. package/src/_modules/location/_models/loc-coordinates.interface.ts +7 -7
  514. package/src/_modules/location/_models/loc-country-division.interface.ts +8 -8
  515. package/src/_modules/location/_models/loc-country-iso.interface.ts +23 -23
  516. package/src/_modules/location/_models/loc-country-phone-code.interface.ts +9 -9
  517. package/src/_modules/location/_models/loc-division-collection.interface.ts +12 -12
  518. package/src/_modules/location/_models/loc-division-region-data.interface.ts +9 -9
  519. package/src/_modules/location/_models/loc-geo-ip-location.interface.ts +27 -27
  520. package/src/_modules/location/index.ts +22 -22
  521. package/src/_modules/messaging/README.md +279 -279
  522. package/src/_modules/messaging/_collections/msg-module-settings.const.ts +46 -46
  523. package/src/_modules/messaging/_enums/msg-attachment-type.enum.ts +26 -26
  524. package/src/_modules/messaging/_enums/msg-conversation-type.enum.ts +20 -20
  525. package/src/_modules/messaging/_enums/msg-delivery-status.enum.ts +17 -17
  526. package/src/_modules/messaging/_enums/msg-event-key.enum.ts +31 -31
  527. package/src/_modules/messaging/_enums/msg-participant-role.enum.ts +20 -20
  528. package/src/_modules/messaging/_enums/msg-provider-type.enum.ts +7 -7
  529. package/src/_modules/messaging/_enums/msg-status.enum.ts +26 -26
  530. package/src/_modules/messaging/_enums/msg-type.enum.ts +23 -23
  531. package/src/_modules/messaging/_models/msg-attachment.interface.ts +46 -46
  532. package/src/_modules/messaging/_models/msg-conversation.data-model.spec.ts +69 -69
  533. package/src/_modules/messaging/_models/msg-conversation.data-model.ts +96 -96
  534. package/src/_modules/messaging/_models/msg-mention.interface.ts +29 -29
  535. package/src/_modules/messaging/_models/msg-message.data-model.spec.ts +79 -79
  536. package/src/_modules/messaging/_models/msg-message.data-model.ts +127 -127
  537. package/src/_modules/messaging/_models/msg-participant.interface.ts +46 -46
  538. package/src/_modules/messaging/_models/msg-reaction.interface.ts +20 -20
  539. package/src/_modules/messaging/_models/msg-thread-info.interface.ts +35 -35
  540. package/src/_modules/messaging/_modules/agent/_enums/agt-process-step-type.enum.ts +35 -35
  541. package/src/_modules/messaging/_modules/agent/_enums/agt-tool-status.enum.ts +23 -23
  542. package/src/_modules/messaging/_modules/agent/_models/agt-process-step.interface.ts +55 -55
  543. package/src/_modules/messaging/_modules/agent/_models/agt-reasoning-info.interface.ts +26 -26
  544. package/src/_modules/messaging/_modules/agent/_models/agt-tool-usage.interface.ts +37 -37
  545. package/src/_modules/messaging/_modules/agent/index.ts +8 -8
  546. package/src/_modules/messaging/index.ts +28 -28
  547. package/src/_modules/ngx-models/_collections/angular-placeholders.ts +34 -34
  548. package/src/_modules/ngx-models/_collections/button-new-style-settings-presets.const.ts +181 -181
  549. package/src/_modules/ngx-models/_collections/dynamo-global.settings.spec.ts +74 -74
  550. package/src/_modules/ngx-models/_collections/dynamo-global.settings.ts +103 -103
  551. package/src/_modules/ngx-models/_enums/button-new/button-new-border-radius.enum.ts +16 -16
  552. package/src/_modules/ngx-models/_enums/button-new/button-new-icon-style-type.enum.ts +14 -14
  553. package/src/_modules/ngx-models/_enums/button-new/button-new-size.enum.ts +16 -16
  554. package/src/_modules/ngx-models/_enums/button-new/button-new-style-type.enum.ts +18 -18
  555. package/src/_modules/ngx-models/_enums/button-type.enum.ts +36 -36
  556. package/src/_modules/ngx-models/_enums/checkbox-label-position.enum.ts +8 -8
  557. package/src/_modules/ngx-models/_enums/container-type.enum.ts +11 -11
  558. package/src/_modules/ngx-models/_enums/errors/error-level.enum.ts +13 -13
  559. package/src/_modules/ngx-models/_enums/errors/error-notification-color.enum.ts +13 -13
  560. package/src/_modules/ngx-models/_enums/errors/error-ui-mode.enum.ts +13 -13
  561. package/src/_modules/ngx-models/_enums/field-appearance.enum.ts +14 -14
  562. package/src/_modules/ngx-models/_enums/field-type.enum.ts +52 -52
  563. package/src/_modules/ngx-models/_enums/file-type.enum.ts +12 -12
  564. package/src/_modules/ngx-models/_enums/flex/flex-layout-align.enum.ts +21 -21
  565. package/src/_modules/ngx-models/_enums/flex/flex-layout.enum.ts +5 -5
  566. package/src/_modules/ngx-models/_enums/form-access.enum.ts +9 -9
  567. package/src/_modules/ngx-models/_enums/form-reset-data.enum.ts +12 -12
  568. package/src/_modules/ngx-models/_enums/form-store-on.enum.ts +9 -9
  569. package/src/_modules/ngx-models/_enums/forms-new/field-new-validator-type.enum.ts +20 -20
  570. package/src/_modules/ngx-models/_enums/frames/header-item-type.enum.ts +15 -15
  571. package/src/_modules/ngx-models/_enums/frames/header-position.enum.ts +10 -10
  572. package/src/_modules/ngx-models/_enums/frames/side-nav-badge-color.enum.ts +13 -13
  573. package/src/_modules/ngx-models/_enums/html/input-types.enum.ts +27 -27
  574. package/src/_modules/ngx-models/_enums/material-color.enum.ts +16 -16
  575. package/src/_modules/ngx-models/_enums/material-tooltip-position.enum.ts +16 -16
  576. package/src/_modules/ngx-models/_enums/preconfigured-field-type.enum.ts +8 -8
  577. package/src/_modules/ngx-models/_enums/storage-type.enum.ts +15 -15
  578. package/src/_modules/ngx-models/_enums/validator-type.enum.ts +9 -9
  579. package/src/_modules/ngx-models/_enums/voice/audio-mime-type.enum.ts +14 -14
  580. package/src/_modules/ngx-models/_enums/voice/voice-provider-type.enum.ts +26 -26
  581. package/src/_modules/ngx-models/_enums/voice/voice-recorder-state.enum.ts +20 -20
  582. package/src/_modules/ngx-models/_models/control-models/button-new-style-settings.control-model.ts +37 -37
  583. package/src/_modules/ngx-models/_models/control-models/button-params.control-model.spec.ts +70 -70
  584. package/src/_modules/ngx-models/_models/control-models/button-params.control-model.ts +332 -332
  585. package/src/_modules/ngx-models/_models/control-models/chat/chat-column-layout-settings.control-model.ts +28 -28
  586. package/src/_modules/ngx-models/_models/control-models/data-handler-settings.control-model.spec.ts +90 -90
  587. package/src/_modules/ngx-models/_models/control-models/data-handler-settings.control-model.ts +134 -134
  588. package/src/_modules/ngx-models/_models/control-models/data-handler.control-model.ts +529 -529
  589. package/src/_modules/ngx-models/_models/control-models/errors/error-notification.control-model.ts +52 -52
  590. package/src/_modules/ngx-models/_models/control-models/errors/error-settings.control-model.ts +34 -34
  591. package/src/_modules/ngx-models/_models/control-models/errors/errors-pojo.spec.ts +86 -86
  592. package/src/_modules/ngx-models/_models/control-models/errors/errors-settings.control-model.ts +52 -52
  593. package/src/_modules/ngx-models/_models/control-models/field-settings.control-model.spec.ts +109 -109
  594. package/src/_modules/ngx-models/_models/control-models/field-settings.control-model.ts +133 -133
  595. package/src/_modules/ngx-models/_models/control-models/form-settings.control-model.spec.ts +109 -109
  596. package/src/_modules/ngx-models/_models/control-models/form-settings.control-model.ts +160 -160
  597. package/src/_modules/ngx-models/_models/control-models/forms-new/checkbox-field-new-settings.control-model.ts +25 -25
  598. package/src/_modules/ngx-models/_models/control-models/forms-new/date-field-new-settings.control-model.ts +26 -26
  599. package/src/_modules/ngx-models/_models/control-models/forms-new/date-range-field-new-settings.control-model.ts +42 -42
  600. package/src/_modules/ngx-models/_models/control-models/forms-new/datetime-field-new-settings.control-model.ts +35 -35
  601. package/src/_modules/ngx-models/_models/control-models/forms-new/datetime-range-field-new-settings.control-model.ts +35 -35
  602. package/src/_modules/ngx-models/_models/control-models/forms-new/field-new-settings.control-model.ts +85 -85
  603. package/src/_modules/ngx-models/_models/control-models/forms-new/field-new-types.ts +42 -42
  604. package/src/_modules/ngx-models/_models/control-models/forms-new/form-new-settings.control-model.ts +40 -40
  605. package/src/_modules/ngx-models/_models/control-models/forms-new/forms-new.spec.ts +179 -179
  606. package/src/_modules/ngx-models/_models/control-models/forms-new/index.ts +20 -20
  607. package/src/_modules/ngx-models/_models/control-models/forms-new/multiselect-field-new-settings.control-model.ts +27 -27
  608. package/src/_modules/ngx-models/_models/control-models/forms-new/number-field-new-settings.control-model.ts +24 -24
  609. package/src/_modules/ngx-models/_models/control-models/forms-new/password-field-new-settings.control-model.ts +35 -35
  610. package/src/_modules/ngx-models/_models/control-models/forms-new/radio-select-field-new-settings.control-model.ts +35 -35
  611. package/src/_modules/ngx-models/_models/control-models/forms-new/select-field-new-settings.control-model.ts +46 -46
  612. package/src/_modules/ngx-models/_models/control-models/forms-new/slider-field-new-settings.control-model.ts +37 -37
  613. package/src/_modules/ngx-models/_models/control-models/forms-new/slider-range-field-new-settings.control-model.ts +45 -45
  614. package/src/_modules/ngx-models/_models/control-models/forms-new/text-field-new-settings.control-model.ts +30 -30
  615. package/src/_modules/ngx-models/_models/control-models/forms-new/textarea-field-new-settings.control-model.ts +22 -22
  616. package/src/_modules/ngx-models/_models/control-models/frames/frames-pojo.spec.ts +108 -108
  617. package/src/_modules/ngx-models/_models/control-models/frames/header-item-settings.control-model.ts +52 -52
  618. package/src/_modules/ngx-models/_models/control-models/frames/main-frame-settings.control-model.ts +40 -40
  619. package/src/_modules/ngx-models/_models/control-models/frames/side-nav-item-settings.control-model.ts +55 -55
  620. package/src/_modules/ngx-models/_models/control-models/frames/side-nav-settings.control-model.ts +30 -30
  621. package/src/_modules/ngx-models/_models/control-models/select.control-model.spec.ts +45 -45
  622. package/src/_modules/ngx-models/_models/control-models/select.control-model.ts +28 -28
  623. package/src/_modules/ngx-models/_models/control-models/voice/voice-pojo.spec.ts +111 -111
  624. package/src/_modules/ngx-models/_models/control-models/voice/voice-providers-settings.control-model.ts +74 -74
  625. package/src/_modules/ngx-models/_models/control-models/voice/voice-record-button-settings.control-model.ts +42 -42
  626. package/src/_modules/ngx-models/_models/control-models/voice/voice-recorder-settings.control-model.ts +37 -37
  627. package/src/_modules/ngx-models/_models/interfaces/button-new-settings.interface.ts +74 -74
  628. package/src/_modules/ngx-models/_models/interfaces/confirm-settings.interface.ts +20 -20
  629. package/src/_modules/ngx-models/_models/interfaces/global-settings.interface.ts +67 -67
  630. package/src/_modules/ngx-models/_models/interfaces/select-options-custom-constructor.interface.ts +40 -40
  631. package/src/_modules/ngx-models/_models/interfaces/storage.service.spec.ts +18 -18
  632. package/src/_modules/ngx-models/_models/interfaces/storage.service.ts +40 -40
  633. package/src/_modules/ngx-models/_models/interfaces/voice/speak-options.interface.ts +19 -19
  634. package/src/_modules/ngx-models/_models/interfaces/voice/speak-result.interface.ts +14 -14
  635. package/src/_modules/ngx-models/_models/interfaces/voice/transcribe-options.interface.ts +14 -14
  636. package/src/_modules/ngx-models/_models/interfaces/voice/transcribe-result.interface.ts +15 -15
  637. package/src/_modules/ngx-models/_models/interfaces/voice/voice-profile.interface.ts +18 -18
  638. package/src/_modules/ngx-models/_models/interfaces/voice/voice-provider-init.interface.ts +21 -21
  639. package/src/_modules/ngx-models/_utils/shared.service.spec.ts +7 -7
  640. package/src/_modules/ngx-models/_utils/shared.service.ts +541 -541
  641. package/src/_modules/ngx-models/index.ts +110 -110
  642. package/src/_modules/pipe/_collections/pip-transforms.const.ts +42 -42
  643. package/src/_modules/pipe/_collections/utils/pip-country-pipe.util.spec.ts +47 -47
  644. package/src/_modules/pipe/_collections/utils/pip-country-pipe.util.ts +41 -41
  645. package/src/_modules/pipe/_collections/utils/pip-custom-pipe.util.spec.ts +39 -39
  646. package/src/_modules/pipe/_collections/utils/pip-custom-pipe.util.ts +30 -30
  647. package/src/_modules/pipe/_collections/utils/pip-division-pipe.util.spec.ts +41 -41
  648. package/src/_modules/pipe/_collections/utils/pip-division-pipe.util.ts +36 -36
  649. package/src/_modules/pipe/_collections/utils/pip-json-pipe.util.spec.ts +62 -62
  650. package/src/_modules/pipe/_collections/utils/pip-json-pipe.util.ts +17 -17
  651. package/src/_modules/pipe/_collections/utils/pip-list-pipe.util.spec.ts +34 -34
  652. package/src/_modules/pipe/_collections/utils/pip-list-pipe.util.ts +25 -25
  653. package/src/_modules/pipe/_collections/utils/pip-multi-pipe-pipe.util.spec.ts +67 -67
  654. package/src/_modules/pipe/_collections/utils/pip-multi-pipe-pipe.util.ts +226 -226
  655. package/src/_modules/pipe/_collections/utils/pip-obj-key-pipe.util.spec.ts +28 -28
  656. package/src/_modules/pipe/_collections/utils/pip-obj-key-pipe.util.ts +21 -21
  657. package/src/_modules/pipe/_collections/utils/pip-range-pipe.util.spec.ts +59 -59
  658. package/src/_modules/pipe/_collections/utils/pip-range-pipe.util.ts +106 -106
  659. package/src/_modules/pipe/_collections/utils/pip-region-pipe.util.spec.ts +31 -31
  660. package/src/_modules/pipe/_collections/utils/pip-region-pipe.util.ts +35 -35
  661. package/src/_modules/pipe/_collections/utils/pip-replace-pipe.util.spec.ts +44 -44
  662. package/src/_modules/pipe/_collections/utils/pip-replace-pipe.util.ts +23 -23
  663. package/src/_modules/pipe/_collections/utils/pip-slider-pipe.util.spec.ts +21 -21
  664. package/src/_modules/pipe/_collections/utils/pip-slider-pipe.util.ts +33 -33
  665. package/src/_modules/pipe/_collections/utils/pip-smart-replace-pipe.util.spec.ts +62 -62
  666. package/src/_modules/pipe/_collections/utils/pip-smart-replace-pipe.util.ts +80 -80
  667. package/src/_modules/pipe/_enums/pip-range-pipe-setting.enum.ts +15 -15
  668. package/src/_modules/pipe/_enums/pip.enum.ts +65 -65
  669. package/src/_modules/pipe/_models/pip-multi-pipe-settings.type.ts +8 -8
  670. package/src/_modules/pipe/_models/pip-transforms.interface.ts +30 -30
  671. package/src/_modules/pipe/index.ts +30 -30
  672. package/src/_modules/security/_collections/stored-secret.util.spec.ts +81 -81
  673. package/src/_modules/security/_collections/stored-secret.util.ts +96 -96
  674. package/src/_modules/security/_models/stored-secret.control-model.ts +55 -55
  675. package/src/_modules/security/index.ts +9 -9
  676. package/src/_modules/socket/_enums/sck-event-key.enum.ts +21 -21
  677. package/src/_modules/socket/_models/sck-client-params.control-model.spec.ts +67 -67
  678. package/src/_modules/socket/_models/sck-client-params.control-model.ts +50 -50
  679. package/src/_modules/socket/_models/sck-socket-event.control-model.spec.ts +89 -66
  680. package/src/_modules/socket/_models/sck-socket-event.control-model.ts +185 -172
  681. package/src/_modules/socket/_services/sck-client.service-base.spec.ts +99 -99
  682. package/src/_modules/socket/_services/sck-client.service-base.ts +353 -353
  683. package/src/_modules/socket/index.ts +14 -14
  684. package/src/_modules/state-machine/_models/state-machine-config.interface.ts +19 -19
  685. package/src/_modules/state-machine/_models/state-machine-event.interface.ts +25 -25
  686. package/src/_modules/state-machine/_models/state-machine.control-model.spec.ts +314 -314
  687. package/src/_modules/state-machine/_models/state-machine.control-model.ts +298 -298
  688. package/src/_modules/state-machine/_models/state-transition-result.interface.ts +53 -53
  689. package/src/_modules/state-machine/_models/state-transition.interface.ts +30 -30
  690. package/src/_modules/state-machine/index.ts +6 -6
  691. package/src/_modules/test/_collections/tst-module-settings.const.ts +67 -67
  692. package/src/_modules/test/index.ts +5 -5
  693. package/src/_modules/usage/_collections/usg-module-settings.const.ts +33 -33
  694. package/src/_modules/usage/_models/usg-action.control-model.spec.ts +27 -27
  695. package/src/_modules/usage/_models/usg-action.control-model.ts +28 -28
  696. package/src/_modules/usage/_models/usg-daily-usage-data.control-model.spec.ts +36 -36
  697. package/src/_modules/usage/_models/usg-daily-usage-data.control-model.ts +35 -35
  698. package/src/_modules/usage/_models/usg-data.control-model.spec.ts +41 -41
  699. package/src/_modules/usage/_models/usg-data.control-model.ts +35 -35
  700. package/src/_modules/usage/_models/usg-session.data-model.spec.ts +97 -97
  701. package/src/_modules/usage/_models/usg-session.data-model.ts +72 -72
  702. package/src/_modules/usage/index.ts +11 -11
  703. package/src/index.ts +104 -104
  704. package/tsconfig.app.json +12 -12
  705. package/tsconfig.json +31 -31
  706. package/tsconfig.test.json +16 -16
  707. package/tslint.json +153 -153
@@ -1,748 +1,748 @@
1
- import * as CryptoJS from 'crypto-js';
2
- import {
3
- DyFM_Error,
4
- DyFM_Error_Settings
5
- } from '../../../_models/control-models/error.control-model';
6
- import { DyFM_Object } from '../../../_collections/utils/object.util';
7
- import { DyFM_global_settings } from '../../../_collections/constants/global-settings.const';
8
- import { DyFM_Log } from '../../../_collections/utils/log.util';
9
-
10
-
11
- /**
12
- * Configuration options for encryption/decryption
13
- */
14
- export interface CryptoConfig {
15
- ivLength?: number;
16
- saltLength?: number;
17
- keyIterations?: number;
18
- keySize?: number;
19
- }
20
-
21
- // Compact: about 60–80 character tokens, not 200+
22
- // Non-standard: hard to reverse-engineer
23
- // Usable in cookies, headers, URLs
24
-
25
- /**
26
- * A utility class for stable encryption and decryption of data
27
- * Uses AES-256-CBC with deterministic IV and salt for consistent results across systems
28
- * Prioritizes reliability and cross-platform compatibility over security
29
- *
30
- * @important DETERMINISTIC ENCRYPTION: This implementation produces identical encrypted
31
- * output for identical input data and key across different systems and multiple calls.
32
- * The same input will ALWAYS generate the same encrypted string on any platform.
33
- *
34
- * @warning SECURITY NOTICE: This deterministic behavior is intentional for cross-platform
35
- * compatibility but reduces security. Identical inputs produce identical outputs, which
36
- * can be exploited for pattern analysis attacks. Use only when consistency across
37
- * systems is more important than cryptographic security.
38
- */
39
- export class DyFM_Crypto {
40
- private static readonly CRYPTO_VERSION: string = '1.0';
41
- private static readonly DEFAULT_CONFIG: Required<CryptoConfig> = {
42
- ivLength: 16, // 128 bits
43
- saltLength: 16, // 128 bits
44
- keyIterations: 1000, // Reduced for better performance and stability
45
- keySize: 8 // 256 bits (8 * 32)
46
- };
47
- private static readonly defaultErrorUserMsg: string =
48
- `We encountered an unhandled Authentication Error, ` +
49
- `\nplease contact the responsible development team.`;
50
-
51
- /**
52
- * Validates the input data and key with comprehensive error messages
53
- * @throws {DyFM_Error} if validation fails
54
- */
55
- private static validateInput(data: any, key: string, operation: 'encrypt' | 'decrypt'): void {
56
- // Validate key
57
- if (!key) {
58
- throw new DyFM_Error({
59
- ...this.getDefaultErrorSettings(operation),
60
- errorCode: 'DyFM-CRY-KEY-MISSING',
61
- message: `Encryption key is required for ${operation} operation. Please provide a valid key.`
62
- });
63
- }
64
-
65
- if (typeof key !== 'string') {
66
- throw new DyFM_Error({
67
- ...this.getDefaultErrorSettings(operation),
68
- errorCode: 'DyFM-CRY-KEY-TYPE',
69
- message: `Encryption key must be a string, but received ${typeof key}. Please provide a valid string key.`
70
- });
71
- }
72
-
73
- if (key.trim().length === 0) {
74
- throw new DyFM_Error({
75
- ...this.getDefaultErrorSettings(operation),
76
- errorCode: 'DyFM-CRY-KEY-EMPTY',
77
- message: 'Encryption key cannot be empty or contain only whitespace. Please provide a non-empty key.'
78
- });
79
- }
80
-
81
- // Only warn about weak keys but don't reject them for backward compatibility
82
- if (key.length < 8) {
83
- console.warn('Warning: Encryption key is too short (minimum 8 characters recommended). Consider using a stronger key for better security.');
84
- }
85
-
86
- // Validate data based on operation
87
- if (operation === 'encrypt') {
88
- this.validateEncryptData(data);
89
- } else if (operation === 'decrypt') {
90
- this.validateDecryptData(data);
91
- }
92
- }
93
-
94
- /**
95
- * Validates data for encryption
96
- */
97
- private static validateEncryptData(data: any): void {
98
- if (data === undefined) {
99
- throw new DyFM_Error({
100
- ...this.getDefaultErrorSettings('encrypt'),
101
- errorCode: 'DyFM-CRY-DATA-UNDEFINED',
102
- message: 'Cannot encrypt undefined data. Please provide valid data to encrypt.'
103
- });
104
- }
105
-
106
- if (data === null) {
107
- throw new DyFM_Error({
108
- ...this.getDefaultErrorSettings('encrypt'),
109
- errorCode: 'DyFM-CRY-DATA-NULL',
110
- message: 'Cannot encrypt null data. Please provide valid data to encrypt.'
111
- });
112
- }
113
-
114
- // Check for empty strings
115
- if (typeof data === 'string' && data.trim().length === 0) {
116
- throw new DyFM_Error({
117
- ...this.getDefaultErrorSettings('encrypt'),
118
- errorCode: 'DyFM-CRY-DATA-EMPTY-STRING',
119
- message: 'Cannot encrypt empty string. Please provide non-empty data to encrypt.'
120
- });
121
- }
122
-
123
- // Allow empty objects and arrays for backward compatibility
124
- // Only reject truly empty data like empty strings
125
- }
126
-
127
- /**
128
- * Validates data for decryption
129
- */
130
- private static validateDecryptData(data: any): void {
131
- if (data === undefined) {
132
- throw new DyFM_Error({
133
- ...this.getDefaultErrorSettings('decrypt'),
134
- errorCode: 'DyFM-CRY-ENCRYPTED-UNDEFINED',
135
- message: 'Cannot decrypt undefined data. Please provide valid encrypted data to decrypt.'
136
- });
137
- }
138
-
139
- if (data === null) {
140
- throw new DyFM_Error({
141
- ...this.getDefaultErrorSettings('decrypt'),
142
- errorCode: 'DyFM-CRY-ENCRYPTED-NULL',
143
- message: 'Cannot decrypt null data. Please provide valid encrypted data to decrypt.'
144
- });
145
- }
146
-
147
- if (typeof data !== 'string') {
148
- throw new DyFM_Error({
149
- ...this.getDefaultErrorSettings('decrypt'),
150
- errorCode: 'DyFM-CRY-ENCRYPTED-TYPE',
151
- message: `Encrypted data must be a string, but received ${typeof data}. Please provide valid encrypted string data.`
152
- });
153
- }
154
-
155
- if (data.trim().length === 0) {
156
- throw new DyFM_Error({
157
- ...this.getDefaultErrorSettings('decrypt'),
158
- errorCode: 'DyFM-CRY-ENCRYPTED-EMPTY',
159
- message: 'Cannot decrypt empty string. Please provide valid encrypted data to decrypt.'
160
- });
161
- }
162
-
163
- if (data.length < 10) {
164
- throw new DyFM_Error({
165
- ...this.getDefaultErrorSettings('decrypt'),
166
- errorCode: 'DyFM-CRY-ENCRYPTED-TOO-SHORT',
167
- message: 'Encrypted data appears to be too short to be valid. Please check the encrypted data.'
168
- });
169
- }
170
-
171
- // Check if it looks like valid encrypted data format
172
- if (!/^[A-Za-z0-9\-_]+$/.test(data)) {
173
- throw new DyFM_Error({
174
- ...this.getDefaultErrorSettings('decrypt'),
175
- errorCode: 'DyFM-CRY-ENCRYPTED-INVALID-FORMAT',
176
- message: 'Encrypted data does not appear to be in valid format. Expected URL-safe base64 format.'
177
- });
178
- }
179
- }
180
-
181
- /**
182
- * Generates a deterministic IV based on the input data and key
183
- * Uses SHA-256 with proper truncation for maximum stability
184
- *
185
- * @important DETERMINISTIC: Same data + key will ALWAYS produce the same IV
186
- * across all systems and CryptoJS versions for consistent encryption results
187
- */
188
- private static generateIV(data: string, key: string, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
189
- // Create a deterministic seed from data and key
190
- const seed: string = this.createDeterministicSeed(data, key, 'IV');
191
-
192
- // Use SHA-256 for better stability and consistency
193
- const hash: CryptoJS.lib.WordArray = CryptoJS.SHA256(seed);
194
-
195
- // Extract exactly 16 bytes (128 bits) for IV
196
- // Use the first 4 words (4 * 4 = 16 bytes) from the hash
197
- const ivWords: number[] = hash.words.slice(0, 4);
198
- return CryptoJS.lib.WordArray.create(ivWords);
199
- }
200
-
201
- /**
202
- * Generates a deterministic salt based on the input data and key
203
- * Uses SHA-256 with proper truncation for maximum stability
204
- *
205
- * @important DETERMINISTIC: Same data + key will ALWAYS produce the same salt
206
- * across all systems and CryptoJS versions for consistent encryption results
207
- */
208
- private static generateSalt(data: string, key: string, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
209
- // Create a deterministic seed from data and key (different from IV)
210
- const seed: string = this.createDeterministicSeed(data, key, 'SALT');
211
-
212
- // Use SHA-256 for better stability and consistency
213
- const hash: CryptoJS.lib.WordArray = CryptoJS.SHA256(seed);
214
-
215
- // Extract exactly 16 bytes (128 bits) for salt
216
- // Use the first 4 words (4 * 4 = 16 bytes) from the hash
217
- const saltWords: number[] = hash.words.slice(0, 4);
218
- return CryptoJS.lib.WordArray.create(saltWords);
219
- }
220
-
221
- /**
222
- * Creates a deterministic seed for IV/salt generation
223
- * Ensures consistent output across all environments and versions
224
- */
225
- private static createDeterministicSeed(data: string, key: string, purpose: string): string {
226
- // Create a consistent seed that includes all relevant factors
227
- // Order matters: data + key + purpose for consistency
228
- const seed: string = `${data}|${key}|${purpose}`;
229
- return seed;
230
- }
231
-
232
- /**
233
- * Derives a key using PBKDF2 with reduced iterations for stability
234
- */
235
- private static deriveKey(key: string, salt: CryptoJS.lib.WordArray, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
236
- return CryptoJS.PBKDF2(key, salt, {
237
- keySize: config.keySize,
238
- iterations: config.keyIterations
239
- });
240
- }
241
-
242
- /**
243
- * Safely serializes data to JSON with deterministic ordering
244
- * Uses regular JSON.stringify but ensures consistency through other means
245
- */
246
- private static safeSerialize<T>(data: T): string {
247
- try {
248
- // Use regular JSON.stringify for backward compatibility
249
- // The deterministic behavior comes from the IV/salt generation, not serialization
250
- return JSON.stringify(data);
251
- } catch (error) {
252
- throw new DyFM_Error({
253
- ...this.getDefaultErrorSettings('safeSerialize', error),
254
- errorCode: 'DyFM-CRY-SER',
255
- message: 'Failed to serialize data'
256
- });
257
- }
258
- }
259
-
260
- /**
261
- * Deterministic JSON stringify that produces identical output across environments
262
- * Uses a hybrid approach: sorts keys for consistency but preserves order for arrays
263
- */
264
- private static deterministicStringify(obj: any): string {
265
- if (obj === null) return 'null';
266
- if (obj === undefined) return 'undefined';
267
- if (typeof obj === 'string') return JSON.stringify(obj);
268
- if (typeof obj === 'number') return JSON.stringify(obj);
269
- if (typeof obj === 'boolean') return JSON.stringify(obj);
270
-
271
- if (Array.isArray(obj)) {
272
- const items = obj.map(item => this.deterministicStringify(item));
273
- return '[' + items.join(',') + ']';
274
- }
275
-
276
- if (typeof obj === 'object') {
277
- // For objects, we need to be more careful about key ordering
278
- // Use a stable sort that preserves original order when possible
279
- const keys: string[] = Object.keys(obj);
280
-
281
- // Only sort if there are potential ordering issues
282
- const needsSorting: boolean = keys.some((key, index) => {
283
- if (index === 0) return false;
284
- return key < keys[index - 1];
285
- });
286
-
287
- const sortedKeys: string[] = needsSorting ? [...keys].sort() : keys;
288
- const pairs: string[] = sortedKeys.map(key => {
289
- const value: string = this.deterministicStringify(obj[key]);
290
- return JSON.stringify(key) + ':' + value;
291
- });
292
- return '{' + pairs.join(',') + '}';
293
- }
294
-
295
- // Handle Date objects and other special types
296
- if (obj instanceof Date) {
297
- return JSON.stringify(obj.toISOString());
298
- }
299
-
300
- // Fallback to regular JSON.stringify for other types
301
- return JSON.stringify(obj);
302
- }
303
-
304
- /**
305
- * Safely deserializes JSON data with enhanced error handling
306
- */
307
- private static safeDeserialize<T>(data: string): T {
308
- try {
309
- if (!data || data.trim().length === 0) {
310
- throw new DyFM_Error({
311
- ...this.getDefaultErrorSettings('safeDeserialize'),
312
- errorCode: 'DyFM-CRY-DES-EMPTY',
313
- message: 'Cannot deserialize empty data. The decrypted data appears to be empty or invalid.'
314
- });
315
- }
316
-
317
- //let parsed = JSON.parse(data);
318
- let parsed: T = DyFM_Object.failableSafeParseJSON(data);
319
-
320
- // Handle double-stringified JSON (or more levels of stringification)
321
- let maxAttempts: number = 3; // Prevent infinite loops
322
- while (typeof parsed === 'string' && maxAttempts > 0) {
323
- try {
324
- //const nextParsed = JSON.parse(parsed);
325
- const nextParsed: T = DyFM_Object.failableSafeParseJSON(parsed);
326
- // Only continue if parsing actually changed the result
327
- if (nextParsed !== parsed) {
328
- parsed = nextParsed;
329
- maxAttempts--;
330
- } else {
331
- break;
332
- }
333
- } catch {
334
- // If parse fails, return current state
335
- break;
336
- }
337
- }
338
-
339
- // Handle primitive values
340
- /* if (typeof parsed === 'string' || typeof parsed === 'number' || typeof parsed === 'boolean') {
341
- return parsed as T;
342
- } */
343
-
344
- return parsed as T;
345
- } catch (error) {
346
- if (error instanceof DyFM_Error) {
347
- throw error;
348
- }
349
-
350
- throw new DyFM_Error({
351
- ...this.getDefaultErrorSettings('safeDeserialize', error),
352
- errorCode: 'DyFM-CRY-DES',
353
- message: 'Failed to deserialize data. The decrypted data may be corrupted or in an unexpected format.'
354
- });
355
- }
356
- }
357
-
358
- /**
359
- * Encrypts data using AES-256-CBC with deterministic IV and salt
360
- *
361
- * @important DETERMINISTIC BEHAVIOR: This method will produce identical encrypted
362
- * output for identical input parameters across different systems, Node.js versions,
363
- * and multiple function calls. The same data + key combination will ALWAYS generate
364
- * the same encrypted string.
365
- *
366
- * @param data The data to encrypt
367
- * @param key The encryption key
368
- * @param config Optional configuration
369
- * @returns URL-safe encrypted string that is identical across systems for same input
370
- * @throws {DyFM_Error} if encryption fails
371
- *
372
- * @example
373
- * // These will produce identical results on any system:
374
- * const result1 = DyFM_Crypto.encrypt({id: 1}, "mykey");
375
- * const result2 = DyFM_Crypto.encrypt({id: 1}, "mykey");
376
- * console.log(result1 === result2); // Always true
377
- */
378
- static encrypt<T>(data: T, key: string, config?: CryptoConfig): string {
379
- try {
380
- this.validateInput(data, key, 'encrypt');
381
- const finalConfig: Required<CryptoConfig> = { ...this.DEFAULT_CONFIG, ...config };
382
-
383
- // Convert data to string
384
- const dataStr: string = this.safeSerialize(data);
385
-
386
- // Generate deterministic IV and salt based on data and key
387
- const iv: CryptoJS.lib.WordArray = this.generateIV(dataStr, key, finalConfig);
388
- const salt: CryptoJS.lib.WordArray = this.generateSalt(dataStr, key, finalConfig);
389
-
390
- // Derive key using PBKDF2
391
- const derivedKey: CryptoJS.lib.WordArray = this.deriveKey(key, salt, finalConfig);
392
-
393
- // Encrypt the data
394
- const encrypted: CryptoJS.lib.WordArray = CryptoJS.AES.encrypt(dataStr, derivedKey, {
395
- iv: iv,
396
- mode: CryptoJS.mode.CBC,
397
- padding: CryptoJS.pad.Pkcs7
398
- });
399
-
400
- // Combine IV + Salt + Ciphertext (skip version for backward compatibility)
401
- const combined: CryptoJS.lib.WordArray = iv.concat(salt).concat(encrypted.ciphertext);
402
-
403
- // Convert to URL-safe base64
404
- return CryptoJS.enc.Base64.stringify(combined)
405
- .replace(/\+/g, '-')
406
- .replace(/\//g, '_')
407
- .replace(/=+$/, '');
408
- } catch (error) {
409
- throw new DyFM_Error({
410
- ...this.getDefaultErrorSettings('encrypt', error),
411
- errorCode: 'DyFM-CRY-ENC',
412
- });
413
- }
414
- }
415
-
416
- /**
417
- * Decrypts data that was encrypted using encrypt()
418
- * @param encryptedData The encrypted data
419
- * @param key The decryption key
420
- * @param config Optional configuration
421
- * @returns The decrypted data
422
- * @throws {DyFM_Error} if decryption fails
423
- */
424
- static decrypt<T>(encryptedData: string, key: string, config?: CryptoConfig): T {
425
- try {
426
- this.validateInput(encryptedData, key, 'decrypt');
427
- const finalConfig: Required<CryptoConfig> = { ...this.DEFAULT_CONFIG, ...config };
428
-
429
- // Convert from URL-safe base64
430
- const base64: string = encryptedData
431
- .replace(/-/g, '+')
432
- .replace(/_/g, '/');
433
-
434
- // Add padding if needed (base64 must be multiple of 4 characters)
435
- // This ensures CryptoJS.parse works correctly even when padding was stripped during URL transmission
436
- const paddingNeeded: number = (4 - (base64.length % 4)) % 4;
437
- const paddedBase64: string = base64 + '='.repeat(paddingNeeded);
438
-
439
- // Validate base64 format before parsing
440
- // Check if the string length makes sense for expected minimum byte count
441
- // Minimum expected: 48 bytes = ~64 base64 characters (48 * 4/3 = 64)
442
- const minExpectedBase64Length: number = Math.ceil((finalConfig.ivLength + finalConfig.saltLength + 16) * 4 / 3);
443
- if (paddedBase64.length < minExpectedBase64Length) {
444
- if (DyFM_global_settings.log_settings.server_debug || true) {
445
- DyFM_Log.H_error(
446
- `Encrypted data is too short. Expected at least ${minExpectedBase64Length} base64 characters ` +
447
- `(for ${(finalConfig.ivLength + finalConfig.saltLength + 16)} bytes), ` +
448
- `but received ${paddedBase64.length} characters (${encryptedData.length} original). ` +
449
- `This may indicate the data was truncated during transmission or storage.` +
450
- '\n\nEncrypted data: ' + encryptedData,
451
- '\n\nkey: ' + key
452
- );
453
- }
454
-
455
- throw new DyFM_Error({
456
- ...this.getDefaultErrorSettings('decrypt'),
457
- errorCode: 'DyFM-CRY-DATA-CORRUPTED',
458
- message: `Encrypted data is too short. Expected at least ${minExpectedBase64Length} base64 characters ` +
459
- `(for ${(finalConfig.ivLength + finalConfig.saltLength + 16)} bytes), ` +
460
- `but received ${paddedBase64.length} characters (${encryptedData.length} original). ` +
461
- `This may indicate the data was truncated during transmission or storage.`
462
- });
463
- }
464
-
465
- // Parse the combined data
466
- const combined: CryptoJS.lib.WordArray = CryptoJS.enc.Base64.parse(paddedBase64);
467
-
468
- // For now, skip version checking to maintain backward compatibility
469
- // TODO: Implement proper version checking in future versions
470
-
471
- // Validate minimum length (IV + Salt + minimum ciphertext)
472
- const minLength: number = (finalConfig.ivLength + finalConfig.saltLength + 16) / 4; // 16 bytes minimum for ciphertext
473
- if (combined.words.length < minLength) {
474
- throw new DyFM_Error({
475
- ...this.getDefaultErrorSettings('decrypt'),
476
- errorCode: 'DyFM-CRY-DATA-CORRUPTED',
477
- message: `Encrypted data is corrupted or incomplete. Expected at least ${minLength * 4} bytes, but received ${combined.sigBytes} bytes. ` +
478
- `Original string length: ${encryptedData.length} characters. ` +
479
- `Base64 length: ${base64.length} characters (${paddedBase64.length} with padding). ` +
480
- `This may indicate the data was truncated during transmission or storage.`
481
- });
482
- }
483
-
484
- // Extract IV, salt, and ciphertext (skip version for now)
485
- const ivStart: number = 0;
486
- const saltStart: number = ivStart + finalConfig.ivLength / 4;
487
- const cipherStart: number = saltStart + finalConfig.saltLength / 4;
488
-
489
- const iv: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.create(combined.words.slice(ivStart, saltStart));
490
- const salt: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.create(combined.words.slice(saltStart, cipherStart));
491
- const ciphertext: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.create(combined.words.slice(cipherStart));
492
-
493
- // Derive key using PBKDF2
494
- const derivedKey: CryptoJS.lib.WordArray = this.deriveKey(key, salt, finalConfig);
495
-
496
- // Decrypt the data
497
- const decrypted: CryptoJS.lib.WordArray = CryptoJS.AES.decrypt(
498
- { ciphertext: ciphertext },
499
- derivedKey,
500
- {
501
- iv: iv,
502
- mode: CryptoJS.mode.CBC,
503
- padding: CryptoJS.pad.Pkcs7
504
- }
505
- );
506
-
507
- // Parse JSON
508
- const decryptedStr: string = decrypted.toString(CryptoJS.enc.Utf8);
509
-
510
- // Check if decryption produced empty result
511
- if (!decryptedStr || decryptedStr.trim().length === 0) {
512
- throw new DyFM_Error({
513
- ...this.getDefaultErrorSettings('decrypt'),
514
- errorCode: 'DyFM-CRY-DECRYPT-EMPTY',
515
- message: 'Decryption failed - the result is empty. This usually means the encryption key is incorrect or the data is corrupted.'
516
- });
517
- }
518
-
519
- return this.safeDeserialize<T>(decryptedStr);
520
- } catch (error) {
521
- // Check if it's already a DyFM_Error
522
- if (error instanceof DyFM_Error) {
523
- throw error;
524
- }
525
-
526
- // Handle specific decryption errors
527
- if (error instanceof Error) {
528
- if (error.message.includes('Malformed UTF-8')) {
529
- throw new DyFM_Error({
530
- ...this.getDefaultErrorSettings('decrypt', error),
531
- errorCode: 'DyFM-CRY-DECRYPT-UTF8',
532
- message: 'Decryption failed - invalid UTF-8 data. This usually means the encryption key is incorrect or the data is corrupted.'
533
- });
534
- }
535
-
536
- if (error.message.includes('Invalid padding')) {
537
- throw new DyFM_Error({
538
- ...this.getDefaultErrorSettings('decrypt', error),
539
- errorCode: 'DyFM-CRY-DECRYPT-PADDING',
540
- message: 'Decryption failed - invalid padding. This usually means the encryption key is incorrect or the data is corrupted.'
541
- });
542
- }
543
- }
544
-
545
- throw new DyFM_Error({
546
- ...this.getDefaultErrorSettings('decrypt', error),
547
- errorCode: 'DyFM-CRY-DRY',
548
- message: 'Decryption failed. Please verify the encryption key and ensure the encrypted data is valid.'
549
- });
550
- }
551
- }
552
-
553
- /**
554
- * Generates a secure random key with enhanced complexity
555
- * @param length Length of the key in characters (default: 32)
556
- * @param customChars Optional custom character set to use
557
- * @returns A secure random key with mixed case letters, numbers, and special characters
558
- */
559
- static generateKey(length: number = 32, customChars?: string): string {
560
- // Use custom character set if provided, otherwise use simple safe characters
561
- const chars: string = customChars || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
562
- let complexKey: string = '';
563
-
564
- // Generate random characters directly for the desired length
565
- for (let i = 0; i < length; i++) {
566
- // Generate random bytes for each character
567
- const randomBytes: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.random(1);
568
- const randomValue: number = randomBytes.words[0];
569
- const charIndex: number = Math.abs(randomValue) % chars.length;
570
- complexKey += chars[charIndex];
571
- }
572
-
573
- return complexKey;
574
- }
575
-
576
- /**
577
- * Validates if a string is a valid encrypted data
578
- * @param encryptedData The data to validate
579
- * @returns true if the data appears to be valid encrypted data
580
- */
581
- static isValidEncryptedData(encryptedData: string): boolean {
582
- if (!encryptedData || typeof encryptedData !== 'string') {
583
- return false;
584
- }
585
- return /^[A-Za-z0-9\-_]+$/.test(encryptedData);
586
- }
587
-
588
- // ────────────────────────────────────────────────────────────────────────────
589
- // AT-REST DB field-encryption — verzió-envelope (opt-in `encrypt:true` mezőkhöz)
590
- // ────────────────────────────────────────────────────────────────────────────
591
- //
592
- // Formátum: `DYENC1:<keyVersion>:<ciphertext>`. A `keyVersion` a jövőbeli kulcs-rotációhoz
593
- // (a decrypt a verzióból tudja, melyik kulcs kell). Az envelope-prefix teszi MEGBÍZHATÓVÁ a
594
- // "titkosított VAGY legacy plaintext" megkülönböztetést (az `isValidEncryptedData` regex
595
- // önmagában plaintextre is illeszkedhet → nem elég a detektáláshoz).
596
-
597
- /** Az AT-REST DB-field-encryption envelope-prefixe. */
598
- static readonly DB_ENC_PREFIX: string = 'DYENC1:';
599
-
600
- /**
601
- * DB-mező titkosítása verzió-envelope-pel. Visszaad: `DYENC1:<keyVersion>:<ciphertext>`.
602
- * A `DyFM_Crypto.encrypt` determinisztikus (Q3: elfogadott promptra) — üres/undefined/null-ra
603
- * dob, ezért a HÍVÓ (nts-transzform) skip-eli az ilyen értékeket.
604
- *
605
- * @param data A titkosítandó érték
606
- * @param key A titkosító kulcs (FDP_CORE_DBCONTENT_CRYPT_KEY)
607
- * @param keyVersion A kulcs-verzió (rotációhoz; default 1)
608
- */
609
- static encryptDbValue<T>(data: T, key: string, keyVersion: number = 1, config?: CryptoConfig): string {
610
- const ciphertext: string = this.encrypt(data, key, config);
611
- return `${this.DB_ENC_PREFIX}${keyVersion}:${ciphertext}`;
612
- }
613
-
614
- /** True, ha az érték a DB-enc envelope-pel kezdődik (tehát titkosított, nem legacy plaintext). */
615
- static isEncryptedDbValue(value: unknown): value is string {
616
- return typeof value === 'string' && value.startsWith(this.DB_ENC_PREFIX);
617
- }
618
-
619
- /** A DB-enc envelope `keyVersion`-je, vagy `null` ha az érték nem envelope. */
620
- static getDbValueKeyVersion(value: unknown): number | null {
621
- if (!this.isEncryptedDbValue(value)) {
622
- return null;
623
- }
624
- const rest: string = value.slice(this.DB_ENC_PREFIX.length);
625
- const sep: number = rest.indexOf(':');
626
- if (sep < 0) {
627
- return null;
628
- }
629
- const parsed: number = Number(rest.slice(0, sep));
630
- return Number.isFinite(parsed) ? parsed : null;
631
- }
632
-
633
- /**
634
- * DB-enc envelope visszafejtése. A hívó a `getDbValueKeyVersion` alapján oldja fel a megfelelő
635
- * kulcsot, és azt adja át. Ha az érték NEM envelope (legacy plaintext), a hívó NE ezt hívja
636
- * (a read-transzform envelope-detektálással szűr → a plaintext változatlanul jön vissza).
637
- */
638
- static decryptDbValue<T>(value: string, key: string, config?: CryptoConfig): T {
639
- if (!this.isEncryptedDbValue(value)) {
640
- throw new DyFM_Error({
641
- ...this.getDefaultErrorSettings('decrypt'),
642
- errorCode: 'DyFM-CRY-ENV-MISSING',
643
- message: 'Value is not a DB-encryption envelope (missing DYENC1: prefix).',
644
- });
645
- }
646
- const rest: string = value.slice(this.DB_ENC_PREFIX.length);
647
- const sep: number = rest.indexOf(':');
648
- const ciphertext: string = rest.slice(sep + 1);
649
- return this.decrypt<T>(ciphertext, key, config);
650
- }
651
-
652
- /**
653
- * Analyzes encrypted data to help with debugging
654
- * @param encryptedData The encrypted data to analyze
655
- * @returns Analysis information about the encrypted data
656
- */
657
- static analyzeEncryptedData(encryptedData: string): {
658
- isValid: boolean;
659
- version?: string;
660
- dataLength: number;
661
- hasValidFormat: boolean;
662
- error?: string;
663
- } {
664
- try {
665
- if (!this.isValidEncryptedData(encryptedData)) {
666
- return {
667
- isValid: false,
668
- dataLength: encryptedData?.length || 0,
669
- hasValidFormat: false,
670
- error: 'Invalid format - not URL-safe base64'
671
- };
672
- }
673
-
674
- // Convert from URL-safe base64
675
- const base64: string = encryptedData
676
- .replace(/-/g, '+')
677
- .replace(/_/g, '/');
678
-
679
- // Parse the combined data
680
- const combined: CryptoJS.lib.WordArray = CryptoJS.enc.Base64.parse(base64);
681
-
682
- // For now, just check if the data has minimum required length
683
- const minLength: number = (16 + 16 + 16) / 4; // IV + Salt + minimum ciphertext
684
-
685
- if (combined.words.length < minLength) {
686
- return {
687
- isValid: false,
688
- dataLength: combined.sigBytes,
689
- hasValidFormat: false,
690
- error: 'Data too short to contain valid encrypted data'
691
- };
692
- }
693
-
694
- return {
695
- isValid: true,
696
- version: 'legacy',
697
- dataLength: combined.sigBytes,
698
- hasValidFormat: true,
699
- error: undefined
700
- };
701
- } catch (error) {
702
- return {
703
- isValid: false,
704
- dataLength: encryptedData?.length || 0,
705
- hasValidFormat: false,
706
- error: `Analysis failed: ${error}`
707
- };
708
- }
709
- }
710
-
711
- /**
712
- * Gets default error settings with enhanced debugging information
713
- */
714
- private static getDefaultErrorSettings(operation: string, error?: any): DyFM_Error_Settings {
715
- const baseSettings: DyFM_Error_Settings = {
716
- status: (error as DyFM_Error)?.___status ?? (error as any)?.status ?? 401,
717
- message: `Crypto operation "${operation}" failed.`,
718
- error: error,
719
- errorCode: 'DyFM-CRY-ERR'
720
- };
721
-
722
- // Add debugging information for common failure scenarios
723
- if (operation === 'decrypt') {
724
- baseSettings.message += '\nThis usually indicates: ' +
725
- '\n 1) Wrong encryption key, ' +
726
- '\n 2) Corrupted encrypted data, ' +
727
- '\n 3) Version incompatibility, ' +
728
- '\n 4) Data was encrypted with different parameters, ' +
729
- '\n 5) Data truncation during transmission (check HTTP header size limits, URL length limits), ' +
730
- '\n 6) Missing base64 padding (should be automatically handled, but may indicate transmission issues).';
731
-
732
- // Add specific guidance for truncation issues (like the "18 bytes" error)
733
- baseSettings.message += '\n\nFor truncation issues (e.g., "received 18 bytes"): ' +
734
- '\n - Check if data is being truncated in HTTP headers (Nginx default: 4-8KB, configurable), ' +
735
- '\n - Verify URL parameter length limits if passed via query string, ' +
736
- '\n - Check database field size limits if stored, ' +
737
- '\n - Ensure proxy/load balancer header size limits are sufficient, ' +
738
- '\n - Verify the encrypted data string length matches expected size before decryption.';
739
- } else if (operation === 'encrypt') {
740
- baseSettings.message += '\nThis usually indicates: ' +
741
- '\n 1) Invalid input data, ' +
742
- '\n 2) Invalid encryption key, ' +
743
- '\n 3) Serialization failure.';
744
- }
745
-
746
- return baseSettings;
747
- }
748
- }
1
+ import * as CryptoJS from 'crypto-js';
2
+ import {
3
+ DyFM_Error,
4
+ DyFM_Error_Settings
5
+ } from '../../../_models/control-models/error.control-model';
6
+ import { DyFM_Object } from '../../../_collections/utils/object.util';
7
+ import { DyFM_global_settings } from '../../../_collections/constants/global-settings.const';
8
+ import { DyFM_Log } from '../../../_collections/utils/log.util';
9
+
10
+
11
+ /**
12
+ * Configuration options for encryption/decryption
13
+ */
14
+ export interface CryptoConfig {
15
+ ivLength?: number;
16
+ saltLength?: number;
17
+ keyIterations?: number;
18
+ keySize?: number;
19
+ }
20
+
21
+ // Compact: about 60–80 character tokens, not 200+
22
+ // Non-standard: hard to reverse-engineer
23
+ // Usable in cookies, headers, URLs
24
+
25
+ /**
26
+ * A utility class for stable encryption and decryption of data
27
+ * Uses AES-256-CBC with deterministic IV and salt for consistent results across systems
28
+ * Prioritizes reliability and cross-platform compatibility over security
29
+ *
30
+ * @important DETERMINISTIC ENCRYPTION: This implementation produces identical encrypted
31
+ * output for identical input data and key across different systems and multiple calls.
32
+ * The same input will ALWAYS generate the same encrypted string on any platform.
33
+ *
34
+ * @warning SECURITY NOTICE: This deterministic behavior is intentional for cross-platform
35
+ * compatibility but reduces security. Identical inputs produce identical outputs, which
36
+ * can be exploited for pattern analysis attacks. Use only when consistency across
37
+ * systems is more important than cryptographic security.
38
+ */
39
+ export class DyFM_Crypto {
40
+ private static readonly CRYPTO_VERSION: string = '1.0';
41
+ private static readonly DEFAULT_CONFIG: Required<CryptoConfig> = {
42
+ ivLength: 16, // 128 bits
43
+ saltLength: 16, // 128 bits
44
+ keyIterations: 1000, // Reduced for better performance and stability
45
+ keySize: 8 // 256 bits (8 * 32)
46
+ };
47
+ private static readonly defaultErrorUserMsg: string =
48
+ `We encountered an unhandled Authentication Error, ` +
49
+ `\nplease contact the responsible development team.`;
50
+
51
+ /**
52
+ * Validates the input data and key with comprehensive error messages
53
+ * @throws {DyFM_Error} if validation fails
54
+ */
55
+ private static validateInput(data: any, key: string, operation: 'encrypt' | 'decrypt'): void {
56
+ // Validate key
57
+ if (!key) {
58
+ throw new DyFM_Error({
59
+ ...this.getDefaultErrorSettings(operation),
60
+ errorCode: 'DyFM-CRY-KEY-MISSING',
61
+ message: `Encryption key is required for ${operation} operation. Please provide a valid key.`
62
+ });
63
+ }
64
+
65
+ if (typeof key !== 'string') {
66
+ throw new DyFM_Error({
67
+ ...this.getDefaultErrorSettings(operation),
68
+ errorCode: 'DyFM-CRY-KEY-TYPE',
69
+ message: `Encryption key must be a string, but received ${typeof key}. Please provide a valid string key.`
70
+ });
71
+ }
72
+
73
+ if (key.trim().length === 0) {
74
+ throw new DyFM_Error({
75
+ ...this.getDefaultErrorSettings(operation),
76
+ errorCode: 'DyFM-CRY-KEY-EMPTY',
77
+ message: 'Encryption key cannot be empty or contain only whitespace. Please provide a non-empty key.'
78
+ });
79
+ }
80
+
81
+ // Only warn about weak keys but don't reject them for backward compatibility
82
+ if (key.length < 8) {
83
+ console.warn('Warning: Encryption key is too short (minimum 8 characters recommended). Consider using a stronger key for better security.');
84
+ }
85
+
86
+ // Validate data based on operation
87
+ if (operation === 'encrypt') {
88
+ this.validateEncryptData(data);
89
+ } else if (operation === 'decrypt') {
90
+ this.validateDecryptData(data);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Validates data for encryption
96
+ */
97
+ private static validateEncryptData(data: any): void {
98
+ if (data === undefined) {
99
+ throw new DyFM_Error({
100
+ ...this.getDefaultErrorSettings('encrypt'),
101
+ errorCode: 'DyFM-CRY-DATA-UNDEFINED',
102
+ message: 'Cannot encrypt undefined data. Please provide valid data to encrypt.'
103
+ });
104
+ }
105
+
106
+ if (data === null) {
107
+ throw new DyFM_Error({
108
+ ...this.getDefaultErrorSettings('encrypt'),
109
+ errorCode: 'DyFM-CRY-DATA-NULL',
110
+ message: 'Cannot encrypt null data. Please provide valid data to encrypt.'
111
+ });
112
+ }
113
+
114
+ // Check for empty strings
115
+ if (typeof data === 'string' && data.trim().length === 0) {
116
+ throw new DyFM_Error({
117
+ ...this.getDefaultErrorSettings('encrypt'),
118
+ errorCode: 'DyFM-CRY-DATA-EMPTY-STRING',
119
+ message: 'Cannot encrypt empty string. Please provide non-empty data to encrypt.'
120
+ });
121
+ }
122
+
123
+ // Allow empty objects and arrays for backward compatibility
124
+ // Only reject truly empty data like empty strings
125
+ }
126
+
127
+ /**
128
+ * Validates data for decryption
129
+ */
130
+ private static validateDecryptData(data: any): void {
131
+ if (data === undefined) {
132
+ throw new DyFM_Error({
133
+ ...this.getDefaultErrorSettings('decrypt'),
134
+ errorCode: 'DyFM-CRY-ENCRYPTED-UNDEFINED',
135
+ message: 'Cannot decrypt undefined data. Please provide valid encrypted data to decrypt.'
136
+ });
137
+ }
138
+
139
+ if (data === null) {
140
+ throw new DyFM_Error({
141
+ ...this.getDefaultErrorSettings('decrypt'),
142
+ errorCode: 'DyFM-CRY-ENCRYPTED-NULL',
143
+ message: 'Cannot decrypt null data. Please provide valid encrypted data to decrypt.'
144
+ });
145
+ }
146
+
147
+ if (typeof data !== 'string') {
148
+ throw new DyFM_Error({
149
+ ...this.getDefaultErrorSettings('decrypt'),
150
+ errorCode: 'DyFM-CRY-ENCRYPTED-TYPE',
151
+ message: `Encrypted data must be a string, but received ${typeof data}. Please provide valid encrypted string data.`
152
+ });
153
+ }
154
+
155
+ if (data.trim().length === 0) {
156
+ throw new DyFM_Error({
157
+ ...this.getDefaultErrorSettings('decrypt'),
158
+ errorCode: 'DyFM-CRY-ENCRYPTED-EMPTY',
159
+ message: 'Cannot decrypt empty string. Please provide valid encrypted data to decrypt.'
160
+ });
161
+ }
162
+
163
+ if (data.length < 10) {
164
+ throw new DyFM_Error({
165
+ ...this.getDefaultErrorSettings('decrypt'),
166
+ errorCode: 'DyFM-CRY-ENCRYPTED-TOO-SHORT',
167
+ message: 'Encrypted data appears to be too short to be valid. Please check the encrypted data.'
168
+ });
169
+ }
170
+
171
+ // Check if it looks like valid encrypted data format
172
+ if (!/^[A-Za-z0-9\-_]+$/.test(data)) {
173
+ throw new DyFM_Error({
174
+ ...this.getDefaultErrorSettings('decrypt'),
175
+ errorCode: 'DyFM-CRY-ENCRYPTED-INVALID-FORMAT',
176
+ message: 'Encrypted data does not appear to be in valid format. Expected URL-safe base64 format.'
177
+ });
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Generates a deterministic IV based on the input data and key
183
+ * Uses SHA-256 with proper truncation for maximum stability
184
+ *
185
+ * @important DETERMINISTIC: Same data + key will ALWAYS produce the same IV
186
+ * across all systems and CryptoJS versions for consistent encryption results
187
+ */
188
+ private static generateIV(data: string, key: string, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
189
+ // Create a deterministic seed from data and key
190
+ const seed: string = this.createDeterministicSeed(data, key, 'IV');
191
+
192
+ // Use SHA-256 for better stability and consistency
193
+ const hash: CryptoJS.lib.WordArray = CryptoJS.SHA256(seed);
194
+
195
+ // Extract exactly 16 bytes (128 bits) for IV
196
+ // Use the first 4 words (4 * 4 = 16 bytes) from the hash
197
+ const ivWords: number[] = hash.words.slice(0, 4);
198
+ return CryptoJS.lib.WordArray.create(ivWords);
199
+ }
200
+
201
+ /**
202
+ * Generates a deterministic salt based on the input data and key
203
+ * Uses SHA-256 with proper truncation for maximum stability
204
+ *
205
+ * @important DETERMINISTIC: Same data + key will ALWAYS produce the same salt
206
+ * across all systems and CryptoJS versions for consistent encryption results
207
+ */
208
+ private static generateSalt(data: string, key: string, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
209
+ // Create a deterministic seed from data and key (different from IV)
210
+ const seed: string = this.createDeterministicSeed(data, key, 'SALT');
211
+
212
+ // Use SHA-256 for better stability and consistency
213
+ const hash: CryptoJS.lib.WordArray = CryptoJS.SHA256(seed);
214
+
215
+ // Extract exactly 16 bytes (128 bits) for salt
216
+ // Use the first 4 words (4 * 4 = 16 bytes) from the hash
217
+ const saltWords: number[] = hash.words.slice(0, 4);
218
+ return CryptoJS.lib.WordArray.create(saltWords);
219
+ }
220
+
221
+ /**
222
+ * Creates a deterministic seed for IV/salt generation
223
+ * Ensures consistent output across all environments and versions
224
+ */
225
+ private static createDeterministicSeed(data: string, key: string, purpose: string): string {
226
+ // Create a consistent seed that includes all relevant factors
227
+ // Order matters: data + key + purpose for consistency
228
+ const seed: string = `${data}|${key}|${purpose}`;
229
+ return seed;
230
+ }
231
+
232
+ /**
233
+ * Derives a key using PBKDF2 with reduced iterations for stability
234
+ */
235
+ private static deriveKey(key: string, salt: CryptoJS.lib.WordArray, config: Required<CryptoConfig>): CryptoJS.lib.WordArray {
236
+ return CryptoJS.PBKDF2(key, salt, {
237
+ keySize: config.keySize,
238
+ iterations: config.keyIterations
239
+ });
240
+ }
241
+
242
+ /**
243
+ * Safely serializes data to JSON with deterministic ordering
244
+ * Uses regular JSON.stringify but ensures consistency through other means
245
+ */
246
+ private static safeSerialize<T>(data: T): string {
247
+ try {
248
+ // Use regular JSON.stringify for backward compatibility
249
+ // The deterministic behavior comes from the IV/salt generation, not serialization
250
+ return JSON.stringify(data);
251
+ } catch (error) {
252
+ throw new DyFM_Error({
253
+ ...this.getDefaultErrorSettings('safeSerialize', error),
254
+ errorCode: 'DyFM-CRY-SER',
255
+ message: 'Failed to serialize data'
256
+ });
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Deterministic JSON stringify that produces identical output across environments
262
+ * Uses a hybrid approach: sorts keys for consistency but preserves order for arrays
263
+ */
264
+ private static deterministicStringify(obj: any): string {
265
+ if (obj === null) return 'null';
266
+ if (obj === undefined) return 'undefined';
267
+ if (typeof obj === 'string') return JSON.stringify(obj);
268
+ if (typeof obj === 'number') return JSON.stringify(obj);
269
+ if (typeof obj === 'boolean') return JSON.stringify(obj);
270
+
271
+ if (Array.isArray(obj)) {
272
+ const items = obj.map(item => this.deterministicStringify(item));
273
+ return '[' + items.join(',') + ']';
274
+ }
275
+
276
+ if (typeof obj === 'object') {
277
+ // For objects, we need to be more careful about key ordering
278
+ // Use a stable sort that preserves original order when possible
279
+ const keys: string[] = Object.keys(obj);
280
+
281
+ // Only sort if there are potential ordering issues
282
+ const needsSorting: boolean = keys.some((key, index) => {
283
+ if (index === 0) return false;
284
+ return key < keys[index - 1];
285
+ });
286
+
287
+ const sortedKeys: string[] = needsSorting ? [...keys].sort() : keys;
288
+ const pairs: string[] = sortedKeys.map(key => {
289
+ const value: string = this.deterministicStringify(obj[key]);
290
+ return JSON.stringify(key) + ':' + value;
291
+ });
292
+ return '{' + pairs.join(',') + '}';
293
+ }
294
+
295
+ // Handle Date objects and other special types
296
+ if (obj instanceof Date) {
297
+ return JSON.stringify(obj.toISOString());
298
+ }
299
+
300
+ // Fallback to regular JSON.stringify for other types
301
+ return JSON.stringify(obj);
302
+ }
303
+
304
+ /**
305
+ * Safely deserializes JSON data with enhanced error handling
306
+ */
307
+ private static safeDeserialize<T>(data: string): T {
308
+ try {
309
+ if (!data || data.trim().length === 0) {
310
+ throw new DyFM_Error({
311
+ ...this.getDefaultErrorSettings('safeDeserialize'),
312
+ errorCode: 'DyFM-CRY-DES-EMPTY',
313
+ message: 'Cannot deserialize empty data. The decrypted data appears to be empty or invalid.'
314
+ });
315
+ }
316
+
317
+ //let parsed = JSON.parse(data);
318
+ let parsed: T = DyFM_Object.failableSafeParseJSON(data);
319
+
320
+ // Handle double-stringified JSON (or more levels of stringification)
321
+ let maxAttempts: number = 3; // Prevent infinite loops
322
+ while (typeof parsed === 'string' && maxAttempts > 0) {
323
+ try {
324
+ //const nextParsed = JSON.parse(parsed);
325
+ const nextParsed: T = DyFM_Object.failableSafeParseJSON(parsed);
326
+ // Only continue if parsing actually changed the result
327
+ if (nextParsed !== parsed) {
328
+ parsed = nextParsed;
329
+ maxAttempts--;
330
+ } else {
331
+ break;
332
+ }
333
+ } catch {
334
+ // If parse fails, return current state
335
+ break;
336
+ }
337
+ }
338
+
339
+ // Handle primitive values
340
+ /* if (typeof parsed === 'string' || typeof parsed === 'number' || typeof parsed === 'boolean') {
341
+ return parsed as T;
342
+ } */
343
+
344
+ return parsed as T;
345
+ } catch (error) {
346
+ if (error instanceof DyFM_Error) {
347
+ throw error;
348
+ }
349
+
350
+ throw new DyFM_Error({
351
+ ...this.getDefaultErrorSettings('safeDeserialize', error),
352
+ errorCode: 'DyFM-CRY-DES',
353
+ message: 'Failed to deserialize data. The decrypted data may be corrupted or in an unexpected format.'
354
+ });
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Encrypts data using AES-256-CBC with deterministic IV and salt
360
+ *
361
+ * @important DETERMINISTIC BEHAVIOR: This method will produce identical encrypted
362
+ * output for identical input parameters across different systems, Node.js versions,
363
+ * and multiple function calls. The same data + key combination will ALWAYS generate
364
+ * the same encrypted string.
365
+ *
366
+ * @param data The data to encrypt
367
+ * @param key The encryption key
368
+ * @param config Optional configuration
369
+ * @returns URL-safe encrypted string that is identical across systems for same input
370
+ * @throws {DyFM_Error} if encryption fails
371
+ *
372
+ * @example
373
+ * // These will produce identical results on any system:
374
+ * const result1 = DyFM_Crypto.encrypt({id: 1}, "mykey");
375
+ * const result2 = DyFM_Crypto.encrypt({id: 1}, "mykey");
376
+ * console.log(result1 === result2); // Always true
377
+ */
378
+ static encrypt<T>(data: T, key: string, config?: CryptoConfig): string {
379
+ try {
380
+ this.validateInput(data, key, 'encrypt');
381
+ const finalConfig: Required<CryptoConfig> = { ...this.DEFAULT_CONFIG, ...config };
382
+
383
+ // Convert data to string
384
+ const dataStr: string = this.safeSerialize(data);
385
+
386
+ // Generate deterministic IV and salt based on data and key
387
+ const iv: CryptoJS.lib.WordArray = this.generateIV(dataStr, key, finalConfig);
388
+ const salt: CryptoJS.lib.WordArray = this.generateSalt(dataStr, key, finalConfig);
389
+
390
+ // Derive key using PBKDF2
391
+ const derivedKey: CryptoJS.lib.WordArray = this.deriveKey(key, salt, finalConfig);
392
+
393
+ // Encrypt the data
394
+ const encrypted: CryptoJS.lib.WordArray = CryptoJS.AES.encrypt(dataStr, derivedKey, {
395
+ iv: iv,
396
+ mode: CryptoJS.mode.CBC,
397
+ padding: CryptoJS.pad.Pkcs7
398
+ });
399
+
400
+ // Combine IV + Salt + Ciphertext (skip version for backward compatibility)
401
+ const combined: CryptoJS.lib.WordArray = iv.concat(salt).concat(encrypted.ciphertext);
402
+
403
+ // Convert to URL-safe base64
404
+ return CryptoJS.enc.Base64.stringify(combined)
405
+ .replace(/\+/g, '-')
406
+ .replace(/\//g, '_')
407
+ .replace(/=+$/, '');
408
+ } catch (error) {
409
+ throw new DyFM_Error({
410
+ ...this.getDefaultErrorSettings('encrypt', error),
411
+ errorCode: 'DyFM-CRY-ENC',
412
+ });
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Decrypts data that was encrypted using encrypt()
418
+ * @param encryptedData The encrypted data
419
+ * @param key The decryption key
420
+ * @param config Optional configuration
421
+ * @returns The decrypted data
422
+ * @throws {DyFM_Error} if decryption fails
423
+ */
424
+ static decrypt<T>(encryptedData: string, key: string, config?: CryptoConfig): T {
425
+ try {
426
+ this.validateInput(encryptedData, key, 'decrypt');
427
+ const finalConfig: Required<CryptoConfig> = { ...this.DEFAULT_CONFIG, ...config };
428
+
429
+ // Convert from URL-safe base64
430
+ const base64: string = encryptedData
431
+ .replace(/-/g, '+')
432
+ .replace(/_/g, '/');
433
+
434
+ // Add padding if needed (base64 must be multiple of 4 characters)
435
+ // This ensures CryptoJS.parse works correctly even when padding was stripped during URL transmission
436
+ const paddingNeeded: number = (4 - (base64.length % 4)) % 4;
437
+ const paddedBase64: string = base64 + '='.repeat(paddingNeeded);
438
+
439
+ // Validate base64 format before parsing
440
+ // Check if the string length makes sense for expected minimum byte count
441
+ // Minimum expected: 48 bytes = ~64 base64 characters (48 * 4/3 = 64)
442
+ const minExpectedBase64Length: number = Math.ceil((finalConfig.ivLength + finalConfig.saltLength + 16) * 4 / 3);
443
+ if (paddedBase64.length < minExpectedBase64Length) {
444
+ if (DyFM_global_settings.log_settings.server_debug || true) {
445
+ DyFM_Log.H_error(
446
+ `Encrypted data is too short. Expected at least ${minExpectedBase64Length} base64 characters ` +
447
+ `(for ${(finalConfig.ivLength + finalConfig.saltLength + 16)} bytes), ` +
448
+ `but received ${paddedBase64.length} characters (${encryptedData.length} original). ` +
449
+ `This may indicate the data was truncated during transmission or storage.` +
450
+ '\n\nEncrypted data: ' + encryptedData,
451
+ '\n\nkey: ' + key
452
+ );
453
+ }
454
+
455
+ throw new DyFM_Error({
456
+ ...this.getDefaultErrorSettings('decrypt'),
457
+ errorCode: 'DyFM-CRY-DATA-CORRUPTED',
458
+ message: `Encrypted data is too short. Expected at least ${minExpectedBase64Length} base64 characters ` +
459
+ `(for ${(finalConfig.ivLength + finalConfig.saltLength + 16)} bytes), ` +
460
+ `but received ${paddedBase64.length} characters (${encryptedData.length} original). ` +
461
+ `This may indicate the data was truncated during transmission or storage.`
462
+ });
463
+ }
464
+
465
+ // Parse the combined data
466
+ const combined: CryptoJS.lib.WordArray = CryptoJS.enc.Base64.parse(paddedBase64);
467
+
468
+ // For now, skip version checking to maintain backward compatibility
469
+ // TODO: Implement proper version checking in future versions
470
+
471
+ // Validate minimum length (IV + Salt + minimum ciphertext)
472
+ const minLength: number = (finalConfig.ivLength + finalConfig.saltLength + 16) / 4; // 16 bytes minimum for ciphertext
473
+ if (combined.words.length < minLength) {
474
+ throw new DyFM_Error({
475
+ ...this.getDefaultErrorSettings('decrypt'),
476
+ errorCode: 'DyFM-CRY-DATA-CORRUPTED',
477
+ message: `Encrypted data is corrupted or incomplete. Expected at least ${minLength * 4} bytes, but received ${combined.sigBytes} bytes. ` +
478
+ `Original string length: ${encryptedData.length} characters. ` +
479
+ `Base64 length: ${base64.length} characters (${paddedBase64.length} with padding). ` +
480
+ `This may indicate the data was truncated during transmission or storage.`
481
+ });
482
+ }
483
+
484
+ // Extract IV, salt, and ciphertext (skip version for now)
485
+ const ivStart: number = 0;
486
+ const saltStart: number = ivStart + finalConfig.ivLength / 4;
487
+ const cipherStart: number = saltStart + finalConfig.saltLength / 4;
488
+
489
+ const iv: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.create(combined.words.slice(ivStart, saltStart));
490
+ const salt: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.create(combined.words.slice(saltStart, cipherStart));
491
+ const ciphertext: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.create(combined.words.slice(cipherStart));
492
+
493
+ // Derive key using PBKDF2
494
+ const derivedKey: CryptoJS.lib.WordArray = this.deriveKey(key, salt, finalConfig);
495
+
496
+ // Decrypt the data
497
+ const decrypted: CryptoJS.lib.WordArray = CryptoJS.AES.decrypt(
498
+ { ciphertext: ciphertext },
499
+ derivedKey,
500
+ {
501
+ iv: iv,
502
+ mode: CryptoJS.mode.CBC,
503
+ padding: CryptoJS.pad.Pkcs7
504
+ }
505
+ );
506
+
507
+ // Parse JSON
508
+ const decryptedStr: string = decrypted.toString(CryptoJS.enc.Utf8);
509
+
510
+ // Check if decryption produced empty result
511
+ if (!decryptedStr || decryptedStr.trim().length === 0) {
512
+ throw new DyFM_Error({
513
+ ...this.getDefaultErrorSettings('decrypt'),
514
+ errorCode: 'DyFM-CRY-DECRYPT-EMPTY',
515
+ message: 'Decryption failed - the result is empty. This usually means the encryption key is incorrect or the data is corrupted.'
516
+ });
517
+ }
518
+
519
+ return this.safeDeserialize<T>(decryptedStr);
520
+ } catch (error) {
521
+ // Check if it's already a DyFM_Error
522
+ if (error instanceof DyFM_Error) {
523
+ throw error;
524
+ }
525
+
526
+ // Handle specific decryption errors
527
+ if (error instanceof Error) {
528
+ if (error.message.includes('Malformed UTF-8')) {
529
+ throw new DyFM_Error({
530
+ ...this.getDefaultErrorSettings('decrypt', error),
531
+ errorCode: 'DyFM-CRY-DECRYPT-UTF8',
532
+ message: 'Decryption failed - invalid UTF-8 data. This usually means the encryption key is incorrect or the data is corrupted.'
533
+ });
534
+ }
535
+
536
+ if (error.message.includes('Invalid padding')) {
537
+ throw new DyFM_Error({
538
+ ...this.getDefaultErrorSettings('decrypt', error),
539
+ errorCode: 'DyFM-CRY-DECRYPT-PADDING',
540
+ message: 'Decryption failed - invalid padding. This usually means the encryption key is incorrect or the data is corrupted.'
541
+ });
542
+ }
543
+ }
544
+
545
+ throw new DyFM_Error({
546
+ ...this.getDefaultErrorSettings('decrypt', error),
547
+ errorCode: 'DyFM-CRY-DRY',
548
+ message: 'Decryption failed. Please verify the encryption key and ensure the encrypted data is valid.'
549
+ });
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Generates a secure random key with enhanced complexity
555
+ * @param length Length of the key in characters (default: 32)
556
+ * @param customChars Optional custom character set to use
557
+ * @returns A secure random key with mixed case letters, numbers, and special characters
558
+ */
559
+ static generateKey(length: number = 32, customChars?: string): string {
560
+ // Use custom character set if provided, otherwise use simple safe characters
561
+ const chars: string = customChars || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
562
+ let complexKey: string = '';
563
+
564
+ // Generate random characters directly for the desired length
565
+ for (let i = 0; i < length; i++) {
566
+ // Generate random bytes for each character
567
+ const randomBytes: CryptoJS.lib.WordArray = CryptoJS.lib.WordArray.random(1);
568
+ const randomValue: number = randomBytes.words[0];
569
+ const charIndex: number = Math.abs(randomValue) % chars.length;
570
+ complexKey += chars[charIndex];
571
+ }
572
+
573
+ return complexKey;
574
+ }
575
+
576
+ /**
577
+ * Validates if a string is a valid encrypted data
578
+ * @param encryptedData The data to validate
579
+ * @returns true if the data appears to be valid encrypted data
580
+ */
581
+ static isValidEncryptedData(encryptedData: string): boolean {
582
+ if (!encryptedData || typeof encryptedData !== 'string') {
583
+ return false;
584
+ }
585
+ return /^[A-Za-z0-9\-_]+$/.test(encryptedData);
586
+ }
587
+
588
+ // ────────────────────────────────────────────────────────────────────────────
589
+ // AT-REST DB field-encryption — verzió-envelope (opt-in `encrypt:true` mezőkhöz)
590
+ // ────────────────────────────────────────────────────────────────────────────
591
+ //
592
+ // Formátum: `DYENC1:<keyVersion>:<ciphertext>`. A `keyVersion` a jövőbeli kulcs-rotációhoz
593
+ // (a decrypt a verzióból tudja, melyik kulcs kell). Az envelope-prefix teszi MEGBÍZHATÓVÁ a
594
+ // "titkosított VAGY legacy plaintext" megkülönböztetést (az `isValidEncryptedData` regex
595
+ // önmagában plaintextre is illeszkedhet → nem elég a detektáláshoz).
596
+
597
+ /** Az AT-REST DB-field-encryption envelope-prefixe. */
598
+ static readonly DB_ENC_PREFIX: string = 'DYENC1:';
599
+
600
+ /**
601
+ * DB-mező titkosítása verzió-envelope-pel. Visszaad: `DYENC1:<keyVersion>:<ciphertext>`.
602
+ * A `DyFM_Crypto.encrypt` determinisztikus (Q3: elfogadott promptra) — üres/undefined/null-ra
603
+ * dob, ezért a HÍVÓ (nts-transzform) skip-eli az ilyen értékeket.
604
+ *
605
+ * @param data A titkosítandó érték
606
+ * @param key A titkosító kulcs (FDP_CORE_DBCONTENT_CRYPT_KEY)
607
+ * @param keyVersion A kulcs-verzió (rotációhoz; default 1)
608
+ */
609
+ static encryptDbValue<T>(data: T, key: string, keyVersion: number = 1, config?: CryptoConfig): string {
610
+ const ciphertext: string = this.encrypt(data, key, config);
611
+ return `${this.DB_ENC_PREFIX}${keyVersion}:${ciphertext}`;
612
+ }
613
+
614
+ /** True, ha az érték a DB-enc envelope-pel kezdődik (tehát titkosított, nem legacy plaintext). */
615
+ static isEncryptedDbValue(value: unknown): value is string {
616
+ return typeof value === 'string' && value.startsWith(this.DB_ENC_PREFIX);
617
+ }
618
+
619
+ /** A DB-enc envelope `keyVersion`-je, vagy `null` ha az érték nem envelope. */
620
+ static getDbValueKeyVersion(value: unknown): number | null {
621
+ if (!this.isEncryptedDbValue(value)) {
622
+ return null;
623
+ }
624
+ const rest: string = value.slice(this.DB_ENC_PREFIX.length);
625
+ const sep: number = rest.indexOf(':');
626
+ if (sep < 0) {
627
+ return null;
628
+ }
629
+ const parsed: number = Number(rest.slice(0, sep));
630
+ return Number.isFinite(parsed) ? parsed : null;
631
+ }
632
+
633
+ /**
634
+ * DB-enc envelope visszafejtése. A hívó a `getDbValueKeyVersion` alapján oldja fel a megfelelő
635
+ * kulcsot, és azt adja át. Ha az érték NEM envelope (legacy plaintext), a hívó NE ezt hívja
636
+ * (a read-transzform envelope-detektálással szűr → a plaintext változatlanul jön vissza).
637
+ */
638
+ static decryptDbValue<T>(value: string, key: string, config?: CryptoConfig): T {
639
+ if (!this.isEncryptedDbValue(value)) {
640
+ throw new DyFM_Error({
641
+ ...this.getDefaultErrorSettings('decrypt'),
642
+ errorCode: 'DyFM-CRY-ENV-MISSING',
643
+ message: 'Value is not a DB-encryption envelope (missing DYENC1: prefix).',
644
+ });
645
+ }
646
+ const rest: string = value.slice(this.DB_ENC_PREFIX.length);
647
+ const sep: number = rest.indexOf(':');
648
+ const ciphertext: string = rest.slice(sep + 1);
649
+ return this.decrypt<T>(ciphertext, key, config);
650
+ }
651
+
652
+ /**
653
+ * Analyzes encrypted data to help with debugging
654
+ * @param encryptedData The encrypted data to analyze
655
+ * @returns Analysis information about the encrypted data
656
+ */
657
+ static analyzeEncryptedData(encryptedData: string): {
658
+ isValid: boolean;
659
+ version?: string;
660
+ dataLength: number;
661
+ hasValidFormat: boolean;
662
+ error?: string;
663
+ } {
664
+ try {
665
+ if (!this.isValidEncryptedData(encryptedData)) {
666
+ return {
667
+ isValid: false,
668
+ dataLength: encryptedData?.length || 0,
669
+ hasValidFormat: false,
670
+ error: 'Invalid format - not URL-safe base64'
671
+ };
672
+ }
673
+
674
+ // Convert from URL-safe base64
675
+ const base64: string = encryptedData
676
+ .replace(/-/g, '+')
677
+ .replace(/_/g, '/');
678
+
679
+ // Parse the combined data
680
+ const combined: CryptoJS.lib.WordArray = CryptoJS.enc.Base64.parse(base64);
681
+
682
+ // For now, just check if the data has minimum required length
683
+ const minLength: number = (16 + 16 + 16) / 4; // IV + Salt + minimum ciphertext
684
+
685
+ if (combined.words.length < minLength) {
686
+ return {
687
+ isValid: false,
688
+ dataLength: combined.sigBytes,
689
+ hasValidFormat: false,
690
+ error: 'Data too short to contain valid encrypted data'
691
+ };
692
+ }
693
+
694
+ return {
695
+ isValid: true,
696
+ version: 'legacy',
697
+ dataLength: combined.sigBytes,
698
+ hasValidFormat: true,
699
+ error: undefined
700
+ };
701
+ } catch (error) {
702
+ return {
703
+ isValid: false,
704
+ dataLength: encryptedData?.length || 0,
705
+ hasValidFormat: false,
706
+ error: `Analysis failed: ${error}`
707
+ };
708
+ }
709
+ }
710
+
711
+ /**
712
+ * Gets default error settings with enhanced debugging information
713
+ */
714
+ private static getDefaultErrorSettings(operation: string, error?: any): DyFM_Error_Settings {
715
+ const baseSettings: DyFM_Error_Settings = {
716
+ status: (error as DyFM_Error)?.___status ?? (error as any)?.status ?? 401,
717
+ message: `Crypto operation "${operation}" failed.`,
718
+ error: error,
719
+ errorCode: 'DyFM-CRY-ERR'
720
+ };
721
+
722
+ // Add debugging information for common failure scenarios
723
+ if (operation === 'decrypt') {
724
+ baseSettings.message += '\nThis usually indicates: ' +
725
+ '\n 1) Wrong encryption key, ' +
726
+ '\n 2) Corrupted encrypted data, ' +
727
+ '\n 3) Version incompatibility, ' +
728
+ '\n 4) Data was encrypted with different parameters, ' +
729
+ '\n 5) Data truncation during transmission (check HTTP header size limits, URL length limits), ' +
730
+ '\n 6) Missing base64 padding (should be automatically handled, but may indicate transmission issues).';
731
+
732
+ // Add specific guidance for truncation issues (like the "18 bytes" error)
733
+ baseSettings.message += '\n\nFor truncation issues (e.g., "received 18 bytes"): ' +
734
+ '\n - Check if data is being truncated in HTTP headers (Nginx default: 4-8KB, configurable), ' +
735
+ '\n - Verify URL parameter length limits if passed via query string, ' +
736
+ '\n - Check database field size limits if stored, ' +
737
+ '\n - Ensure proxy/load balancer header size limits are sufficient, ' +
738
+ '\n - Verify the encrypted data string length matches expected size before decryption.';
739
+ } else if (operation === 'encrypt') {
740
+ baseSettings.message += '\nThis usually indicates: ' +
741
+ '\n 1) Invalid input data, ' +
742
+ '\n 2) Invalid encryption key, ' +
743
+ '\n 3) Serialization failure.';
744
+ }
745
+
746
+ return baseSettings;
747
+ }
748
+ }