moai-adk 0.4.5__py3-none-any.whl → 0.20.1__py3-none-any.whl

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.

Potentially problematic release.


This version of moai-adk might be problematic. Click here for more details.

Files changed (433) hide show
  1. moai_adk/__init__.py +1 -1
  2. moai_adk/__main__.py +74 -1
  3. moai_adk/cli/commands/__init__.py +1 -1
  4. moai_adk/cli/commands/analyze.py +119 -0
  5. moai_adk/cli/commands/backup.py +25 -1
  6. moai_adk/cli/commands/doctor.py +31 -5
  7. moai_adk/cli/commands/improve_user_experience.py +307 -0
  8. moai_adk/cli/commands/init.py +111 -10
  9. moai_adk/cli/commands/status.py +33 -3
  10. moai_adk/cli/commands/update.py +921 -130
  11. moai_adk/cli/commands/validate_links.py +120 -0
  12. moai_adk/cli/prompts/init_prompts.py +22 -87
  13. moai_adk/core/analysis/__init__.py +9 -0
  14. moai_adk/core/analysis/session_analyzer.py +388 -0
  15. moai_adk/core/analysis/tag_chain_analyzer.py +344 -0
  16. moai_adk/core/analysis/tag_chain_repair.py +879 -0
  17. moai_adk/core/config/__init__.py +19 -0
  18. moai_adk/core/config/migration.py +235 -0
  19. moai_adk/core/git/__init__.py +1 -1
  20. moai_adk/core/git/branch.py +1 -1
  21. moai_adk/core/git/commit.py +1 -1
  22. moai_adk/core/git/manager.py +1 -1
  23. moai_adk/core/issue_creator.py +313 -0
  24. moai_adk/core/mcp/setup.py +56 -0
  25. moai_adk/core/mcp/setup_old.py +296 -0
  26. moai_adk/core/project/backup_utils.py +1 -1
  27. moai_adk/core/project/checker.py +2 -2
  28. moai_adk/core/project/detector.py +211 -12
  29. moai_adk/core/project/initializer.py +85 -15
  30. moai_adk/core/project/phase_executor.py +76 -13
  31. moai_adk/core/project/validator.py +13 -13
  32. moai_adk/core/quality/__init__.py +1 -1
  33. moai_adk/core/quality/trust_checker.py +1 -1
  34. moai_adk/core/quality/validators/__init__.py +1 -1
  35. moai_adk/core/quality/validators/base_validator.py +1 -1
  36. moai_adk/core/tags/__init__.py +86 -0
  37. moai_adk/core/tags/auto_corrector.py +693 -0
  38. moai_adk/core/tags/ci_validator.py +463 -0
  39. moai_adk/core/tags/cli.py +283 -0
  40. moai_adk/core/tags/generator.py +109 -0
  41. moai_adk/core/tags/inserter.py +99 -0
  42. moai_adk/core/tags/mapper.py +126 -0
  43. moai_adk/core/tags/parser.py +76 -0
  44. moai_adk/core/tags/policy_validator.py +580 -0
  45. moai_adk/core/tags/pre_commit_validator.py +421 -0
  46. moai_adk/core/tags/reporter.py +956 -0
  47. moai_adk/core/tags/rollback_manager.py +525 -0
  48. moai_adk/core/tags/tags.py +149 -0
  49. moai_adk/core/tags/validator.py +897 -0
  50. moai_adk/core/template/__init__.py +1 -1
  51. moai_adk/core/template/backup.py +1 -1
  52. moai_adk/core/template/merger.py +50 -1
  53. moai_adk/core/template/processor.py +119 -13
  54. moai_adk/core/template_engine.py +268 -0
  55. moai_adk/templates/.claude/agents/alfred/backend-expert.md +348 -0
  56. moai_adk/templates/.claude/agents/alfred/cc-manager.md +209 -944
  57. moai_adk/templates/.claude/agents/alfred/database-expert.md +352 -0
  58. moai_adk/templates/.claude/agents/alfred/debug-helper.md +34 -5
  59. moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
  60. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +38 -8
  61. moai_adk/templates/.claude/agents/alfred/format-expert.md +469 -0
  62. moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
  63. moai_adk/templates/.claude/agents/alfred/git-manager.md +128 -9
  64. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +104 -6
  65. moai_adk/templates/.claude/agents/alfred/project-manager.md +88 -16
  66. moai_adk/templates/.claude/agents/alfred/quality-gate.md +36 -9
  67. moai_adk/templates/.claude/agents/alfred/security-expert.md +270 -0
  68. moai_adk/templates/.claude/agents/alfred/skill-factory.md +865 -0
  69. moai_adk/templates/.claude/agents/alfred/spec-builder.md +214 -43
  70. moai_adk/templates/.claude/agents/alfred/tag-agent.md +111 -9
  71. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +309 -160
  72. moai_adk/templates/.claude/agents/alfred/trust-checker.md +36 -7
  73. moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +605 -0
  74. moai_adk/templates/.claude/commands/alfred/0-project.md +393 -966
  75. moai_adk/templates/.claude/commands/alfred/1-plan.md +651 -367
  76. moai_adk/templates/.claude/commands/alfred/2-run.md +388 -241
  77. moai_adk/templates/.claude/commands/alfred/3-sync.md +1921 -410
  78. moai_adk/templates/.claude/commands/alfred/9-feedback.md +153 -0
  79. moai_adk/templates/.claude/commands/alfred/release-new.md +3604 -0
  80. moai_adk/templates/.claude/hooks/alfred/core/project.py +484 -20
  81. moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
  82. moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
  83. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
  84. moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
  85. moai_adk/templates/.claude/hooks/alfred/post_tool__enable_streaming_ui.py +50 -0
  86. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +93 -0
  87. moai_adk/templates/.claude/hooks/alfred/post_tool__tag_auto_corrector.py +407 -0
  88. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +99 -0
  89. moai_adk/templates/.claude/hooks/alfred/pre_tool__realtime_tag_monitor.py +335 -0
  90. moai_adk/templates/.claude/hooks/alfred/pre_tool__tag_policy_validator.py +325 -0
  91. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +93 -0
  92. moai_adk/templates/.claude/hooks/alfred/session_start__auto_cleanup.py +580 -0
  93. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +298 -0
  94. moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +170 -0
  95. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
  96. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
  97. moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
  98. moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +230 -0
  99. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
  100. moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
  101. moai_adk/templates/.claude/hooks/alfred/shared/handlers/daily_analysis.py +351 -0
  102. moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
  103. moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +174 -0
  104. moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +87 -0
  105. moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +61 -0
  106. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +111 -0
  107. moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
  108. moai_adk/templates/.claude/hooks/alfred/utils/hook_config.py +94 -0
  109. moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
  110. moai_adk/templates/.claude/output-styles/alfred/alfred-moai-adk-beginner.md +267 -0
  111. moai_adk/templates/.claude/output-styles/alfred/keating-personal-tutor.md +440 -0
  112. moai_adk/templates/.claude/output-styles/alfred/r2d2-agentic-coding.md +583 -0
  113. moai_adk/templates/.claude/settings.json +96 -14
  114. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
  115. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
  116. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/reference.md +242 -0
  117. moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/SKILL.md +237 -0
  118. moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/examples.md +871 -0
  119. moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/reference.md +653 -0
  120. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/README.md +162 -0
  121. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/SKILL.md +227 -0
  122. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/examples.md +354 -0
  123. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/reference.md +158 -0
  124. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +179 -79
  125. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/examples.md +117 -0
  126. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/scripts/pre-review-check.sh +62 -0
  127. moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +132 -0
  128. moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
  129. moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
  130. moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
  131. moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
  132. moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
  133. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
  134. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
  135. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
  136. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
  137. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
  138. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
  139. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +229 -0
  140. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
  141. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
  142. moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +87 -73
  143. moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +29 -0
  144. moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +28 -0
  145. moai_adk/templates/.claude/skills/moai-alfred-personas/README.md +42 -0
  146. moai_adk/templates/.claude/skills/moai-alfred-personas/SKILL.md +429 -0
  147. moai_adk/templates/.claude/skills/moai-alfred-personas/examples.md +520 -0
  148. moai_adk/templates/.claude/skills/moai-alfred-personas/reference.md +405 -0
  149. moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
  150. moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
  151. moai_adk/templates/.claude/skills/moai-alfred-practices/reference.md +369 -0
  152. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
  153. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
  154. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
  155. moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
  156. moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
  157. moai_adk/templates/.claude/skills/moai-alfred-rules/reference.md +539 -0
  158. moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +320 -0
  159. moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
  160. moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
  161. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
  162. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
  163. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples/validate-spec.sh +161 -0
  164. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
  165. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
  166. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
  167. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
  168. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
  169. moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
  170. moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +269 -0
  171. moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +32 -0
  172. moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +298 -0
  173. moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +26 -0
  174. moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +307 -0
  175. moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +21 -0
  176. moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +252 -0
  177. moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +19 -0
  178. moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +19 -0
  179. moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +24 -0
  180. moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +199 -0
  181. moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +39 -0
  182. moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +316 -0
  183. moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +18 -0
  184. moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +263 -0
  185. moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +30 -0
  186. moai_adk/templates/.claude/skills/moai-cc-skill-factory/CHECKLIST.md +482 -0
  187. moai_adk/templates/.claude/skills/moai-cc-skill-factory/EXAMPLES.md +303 -0
  188. moai_adk/templates/.claude/skills/moai-cc-skill-factory/INTERACTIVE-DISCOVERY.md +524 -0
  189. moai_adk/templates/.claude/skills/moai-cc-skill-factory/METADATA.md +477 -0
  190. moai_adk/templates/.claude/skills/moai-cc-skill-factory/PARALLEL-ANALYSIS-REPORT.md +429 -0
  191. moai_adk/templates/.claude/skills/moai-cc-skill-factory/PYTHON-VERSION-MATRIX.md +391 -0
  192. moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-FACTORY-WORKFLOW.md +431 -0
  193. moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-UPDATE-ADVISOR.md +577 -0
  194. moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL.md +273 -0
  195. moai_adk/templates/.claude/skills/moai-cc-skill-factory/STEP-BY-STEP-GUIDE.md +466 -0
  196. moai_adk/templates/.claude/skills/moai-cc-skill-factory/STRUCTURE.md +583 -0
  197. moai_adk/templates/.claude/skills/moai-cc-skill-factory/WEB-RESEARCH.md +526 -0
  198. moai_adk/templates/.claude/skills/moai-cc-skill-factory/reference.md +608 -0
  199. moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/generate-structure.sh +328 -0
  200. moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/validate-skill.sh +312 -0
  201. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/SKILL_TEMPLATE.md +245 -0
  202. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/examples-template.md +285 -0
  203. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/reference-template.md +278 -0
  204. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/scripts-template.sh +303 -0
  205. moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +291 -0
  206. moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +15 -0
  207. moai_adk/templates/.claude/skills/moai-change-logger/SKILL.md +563 -0
  208. moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
  209. moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
  210. moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
  211. moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +234 -43
  212. moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +1633 -0
  213. moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +660 -0
  214. moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +97 -69
  215. moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +29 -0
  216. moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +30 -0
  217. moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +97 -72
  218. moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +29 -0
  219. moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +30 -0
  220. moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +97 -74
  221. moai_adk/templates/.claude/skills/moai-domain-database/examples.md +29 -0
  222. moai_adk/templates/.claude/skills/moai-domain-database/reference.md +30 -0
  223. moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +98 -74
  224. moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +29 -0
  225. moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +31 -0
  226. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +102 -73
  227. moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +29 -0
  228. moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +31 -0
  229. moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +97 -73
  230. moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +29 -0
  231. moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +30 -0
  232. moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +97 -67
  233. moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +29 -0
  234. moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +30 -0
  235. moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +97 -79
  236. moai_adk/templates/.claude/skills/moai-domain-security/examples.md +29 -0
  237. moai_adk/templates/.claude/skills/moai-domain-security/reference.md +30 -0
  238. moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +97 -71
  239. moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +29 -0
  240. moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +30 -0
  241. moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +265 -64
  242. moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +1064 -0
  243. moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +1047 -0
  244. moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +87 -78
  245. moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +29 -0
  246. moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +28 -0
  247. moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +87 -70
  248. moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +29 -0
  249. moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +28 -0
  250. moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +87 -86
  251. moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +29 -0
  252. moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +28 -0
  253. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +80 -62
  254. moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +29 -0
  255. moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +28 -0
  256. moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +207 -50
  257. moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +29 -0
  258. moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +29 -0
  259. moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +90 -71
  260. moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +29 -0
  261. moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +28 -0
  262. moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +78 -58
  263. moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +29 -0
  264. moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +28 -0
  265. moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +78 -51
  266. moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +29 -0
  267. moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +28 -0
  268. moai_adk/templates/.claude/skills/moai-foundation-trust/.!11330!examples.md +0 -0
  269. moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +253 -32
  270. moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
  271. moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +1099 -0
  272. moai_adk/templates/.claude/skills/moai-jit-docs-enhanced/SKILL.md +460 -0
  273. moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +98 -74
  274. moai_adk/templates/.claude/skills/moai-lang-c/examples.md +29 -0
  275. moai_adk/templates/.claude/skills/moai-lang-c/reference.md +31 -0
  276. moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +98 -76
  277. moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +29 -0
  278. moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +31 -0
  279. moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +2358 -70
  280. moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +29 -0
  281. moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +30 -0
  282. moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +2962 -68
  283. moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +29 -0
  284. moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +30 -0
  285. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +1898 -70
  286. moai_adk/templates/.claude/skills/moai-lang-go/examples.md +29 -0
  287. moai_adk/templates/.claude/skills/moai-lang-go/reference.md +31 -0
  288. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +1465 -68
  289. moai_adk/templates/.claude/skills/moai-lang-java/examples.md +29 -0
  290. moai_adk/templates/.claude/skills/moai-lang-java/reference.md +31 -0
  291. moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +2364 -66
  292. moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +29 -0
  293. moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +32 -0
  294. moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +1630 -69
  295. moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +29 -0
  296. moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +31 -0
  297. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +89 -61
  298. moai_adk/templates/.claude/skills/moai-lang-php/examples.md +29 -0
  299. moai_adk/templates/.claude/skills/moai-lang-php/reference.md +30 -0
  300. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +735 -66
  301. moai_adk/templates/.claude/skills/moai-lang-python/examples.md +624 -0
  302. moai_adk/templates/.claude/skills/moai-lang-python/reference.md +316 -0
  303. moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +97 -73
  304. moai_adk/templates/.claude/skills/moai-lang-r/examples.md +29 -0
  305. moai_adk/templates/.claude/skills/moai-lang-r/reference.md +30 -0
  306. moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +98 -73
  307. moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +29 -0
  308. moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +31 -0
  309. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +1834 -70
  310. moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +29 -0
  311. moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +31 -0
  312. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +99 -74
  313. moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +29 -0
  314. moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +30 -0
  315. moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +97 -74
  316. moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +29 -0
  317. moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +30 -0
  318. moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +98 -74
  319. moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +29 -0
  320. moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +31 -0
  321. moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +1959 -69
  322. moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +29 -0
  323. moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +30 -0
  324. moai_adk/templates/.claude/skills/moai-lang-template/SKILL.md +348 -0
  325. moai_adk/templates/.claude/skills/moai-lang-template/VARIABLES.md +98 -0
  326. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +1230 -66
  327. moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +29 -0
  328. moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +34 -0
  329. moai_adk/templates/.claude/skills/moai-learning-optimizer/SKILL.md +575 -0
  330. moai_adk/templates/.claude/skills/moai-project-batch-questions/README.md +50 -0
  331. moai_adk/templates/.claude/skills/moai-project-batch-questions/SKILL.md +304 -0
  332. moai_adk/templates/.claude/skills/moai-project-batch-questions/examples.md +417 -0
  333. moai_adk/templates/.claude/skills/moai-project-batch-questions/reference.md +704 -0
  334. moai_adk/templates/.claude/skills/moai-project-config-manager/README.md +87 -0
  335. moai_adk/templates/.claude/skills/moai-project-config-manager/SKILL.md +552 -0
  336. moai_adk/templates/.claude/skills/moai-project-config-manager/examples.md +1109 -0
  337. moai_adk/templates/.claude/skills/moai-project-config-manager/reference.md +514 -0
  338. moai_adk/templates/.claude/skills/moai-project-config-manager/validate.py +106 -0
  339. moai_adk/templates/.claude/skills/moai-project-documentation/README.md +11 -0
  340. moai_adk/templates/.claude/skills/moai-project-documentation/SKILL.md +622 -0
  341. moai_adk/templates/.claude/skills/moai-project-documentation/examples.md +20 -0
  342. moai_adk/templates/.claude/skills/moai-project-documentation/reference.md +12 -0
  343. moai_adk/templates/.claude/skills/moai-project-language-initializer/README.md +152 -0
  344. moai_adk/templates/.claude/skills/moai-project-language-initializer/SKILL.md +285 -0
  345. moai_adk/templates/.claude/skills/moai-project-language-initializer/examples.md +333 -0
  346. moai_adk/templates/.claude/skills/moai-project-language-initializer/reference.md +386 -0
  347. moai_adk/templates/.claude/skills/moai-project-template-optimizer/README.md +49 -0
  348. moai_adk/templates/.claude/skills/moai-project-template-optimizer/SKILL.md +319 -0
  349. moai_adk/templates/.claude/skills/moai-project-template-optimizer/examples.md +58 -0
  350. moai_adk/templates/.claude/skills/moai-project-template-optimizer/reference.md +123 -0
  351. moai_adk/templates/.claude/skills/moai-session-info/SKILL.md +314 -0
  352. moai_adk/templates/.claude/skills/moai-streaming-ui/SKILL.md +552 -0
  353. moai_adk/templates/.claude/skills/moai-tag-policy-validator/SKILL.md +570 -0
  354. moai_adk/templates/.git-hooks/pre-commit +66 -0
  355. moai_adk/templates/.git-hooks/pre-push +255 -0
  356. moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
  357. moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
  358. moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
  359. moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
  360. moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
  361. moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
  362. moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
  363. moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
  364. moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
  365. moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
  366. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
  367. moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
  368. moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
  369. moai_adk/templates/.github/workflows/release.yml +118 -0
  370. moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
  371. moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
  372. moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
  373. moai_adk/templates/.github/workflows/spec-issue-sync.yml +338 -0
  374. moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
  375. moai_adk/templates/.github/workflows/tag-report.yml +269 -0
  376. moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
  377. moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
  378. moai_adk/templates/.mcp.json +31 -0
  379. moai_adk/templates/.moai/config.json +80 -7
  380. moai_adk/templates/CLAUDE.md +562 -546
  381. moai_adk/utils/banner.py +5 -5
  382. moai_adk/utils/common.py +294 -0
  383. moai_adk/utils/link_validator.py +235 -0
  384. moai_adk/utils/logger.py +8 -8
  385. moai_adk/utils/user_experience.py +451 -0
  386. moai_adk-0.20.1.dist-info/METADATA +233 -0
  387. moai_adk-0.20.1.dist-info/RECORD +404 -0
  388. moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
  389. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -156
  390. moai_adk/templates/.claude/hooks/alfred/core/__init__.py +0 -85
  391. moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
  392. moai_adk/templates/.claude/hooks/alfred/handlers/session.py +0 -92
  393. moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +0 -70
  394. moai_adk/templates/.claude/hooks/alfred/handlers/user.py +0 -41
  395. moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -636
  396. moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -692
  397. moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -470
  398. moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +0 -103
  399. moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -103
  400. moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -95
  401. moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +0 -105
  402. moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +0 -97
  403. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -97
  404. moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -90
  405. moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -99
  406. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +0 -87
  407. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +0 -62
  408. moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +0 -94
  409. moai_adk/templates/.claude/skills/moai-claude-code/examples.md +0 -513
  410. moai_adk/templates/.claude/skills/moai-claude-code/reference.md +0 -433
  411. moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +0 -332
  412. moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +0 -384
  413. moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +0 -363
  414. moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +0 -595
  415. moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +0 -496
  416. moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +0 -100
  417. moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +0 -99
  418. moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +0 -100
  419. moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +0 -98
  420. moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +0 -98
  421. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
  422. moai_adk/templates/.moai/memory/development-guide.md +0 -344
  423. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
  424. moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
  425. moai_adk/templates/.moai/project/product.md +0 -161
  426. moai_adk/templates/.moai/project/structure.md +0 -156
  427. moai_adk/templates/.moai/project/tech.md +0 -227
  428. moai_adk/templates/__init__.py +0 -2
  429. moai_adk-0.4.5.dist-info/METADATA +0 -369
  430. moai_adk-0.4.5.dist-info/RECORD +0 -152
  431. {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/WHEEL +0 -0
  432. {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/entry_points.txt +0 -0
  433. {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,96 +1,2394 @@
1
1
  ---
2
-
3
2
  name: moai-lang-javascript
4
- description: JavaScript best practices with Jest, ESLint, Prettier, and npm package management. Use when writing or reviewing JavaScript code in project workflows.
3
+ version: 2.0.0
4
+ created: 2025-11-06
5
+ updated: 2025-11-06
6
+ status: active
7
+ description: "JavaScript best practices with Node.js, modern frameworks, and ecosystem patterns for 2025"
8
+ keywords: [javascript, nodejs, frontend, backend, legacy, maintenance, modernization, browser]
5
9
  allowed-tools:
6
10
  - Read
11
+ - Write
12
+ - Edit
7
13
  - Bash
14
+ - WebFetch
15
+ - WebSearch
8
16
  ---
9
17
 
10
- # JavaScript Expert
18
+ # JavaScript Development Mastery
19
+
20
+ **Modern JavaScript Development with 2025 Best Practices**
21
+
22
+ > Comprehensive JavaScript development guidance covering Node.js backend development, legacy browser support, modern framework integration, and progressive enhancement patterns using the latest tools and methodologies.
23
+
24
+ ## What It Does
25
+
26
+ - **Node.js Backend Services**: Express, Koa, and modern server-side JavaScript
27
+ - **Legacy Browser Support**: Progressive enhancement and polyfill strategies
28
+ - **API Development**: REST, GraphQL, and real-time communication
29
+ - **CLI Tools & Automation**: Node.js-based developer utilities
30
+ - **Modern Build Systems**: Webpack, Vite, and bundling optimization
31
+ - **Testing & Quality**: Unit testing, integration testing, and code quality
32
+ - **Migration Strategies**: Legacy code modernization and TypeScript migration
33
+
34
+ ## When to Use
11
35
 
12
- ## Skill Metadata
13
- | Field | Value |
14
- | ----- | ----- |
15
- | Allowed tools | Read (read_file), Bash (terminal) |
16
- | Auto-load | On demand when language keywords are detected |
17
- | Trigger cues | JavaScript code discussions, framework guidance, or file extensions such as .js. |
18
- | Tier | 3 |
36
+ ### Perfect Scenarios
37
+ - **Legacy JavaScript application maintenance and modernization**
38
+ - **Node.js backend services and APIs**
39
+ - **CLI tools and developer utilities**
40
+ - **Progressive web applications requiring broad browser support**
41
+ - **Projects needing gradual TypeScript migration**
42
+ - **Serverless functions and edge computing**
43
+ - **Real-time applications with WebSockets**
19
44
 
20
- ## What it does
45
+ ### Common Triggers
46
+ - "Create Node.js API"
47
+ - "Modernize JavaScript legacy code"
48
+ - "Set up Express server"
49
+ - "JavaScript best practices"
50
+ - "Migrate to TypeScript"
51
+ - "Browser compatibility issues"
21
52
 
22
- Provides JavaScript-specific expertise for TDD development, including Jest testing, ESLint linting, Prettier formatting, and npm package management.
53
+ ## Tool Version Matrix (2025-11-06)
23
54
 
24
- ## When to use
55
+ ### Core JavaScript
56
+ - **Node.js**: 22.x (LTS) / 20.x (Active LTS)
57
+ - **npm**: 10.x - Node package manager
58
+ - **Yarn**: 4.x - Alternative package manager
59
+ - **pnpm**: 9.x - Fast, disk space efficient package manager
25
60
 
26
- - Engages when the conversation references JavaScript work, frameworks, or files like .js.
27
- - "Writing JavaScript tests", "How to use Jest", "ES6+ grammar"
28
- - Automatically invoked when working with JavaScript projects
29
- - JavaScript SPEC implementation (`/alfred:2-run`)
61
+ ### Backend Frameworks
62
+ - **Express**: 4.21.x / 5.1.x (new default) - Web framework
63
+ - **Koa**: 2.15.x - Next generation web framework
64
+ - **Fastify**: 5.x - Fast and low overhead web framework
65
+ - **NestJS**: 10.x - Progressive Node.js framework
66
+ - **Hapi**: 21.x - Rich framework for building applications
30
67
 
31
- ## How it works
68
+ ### Frontend Integration
69
+ - **React**: 19.x - UI library (for integration patterns)
70
+ - **Vue**: 3.5.x - Progressive framework
71
+ - **Webpack**: 5.x - Module bundler
72
+ - **Vite**: 6.x - Fast build tool
73
+ - **Rollup**: 4.x - Module bundler for libraries
32
74
 
33
- **TDD Framework**:
34
- - **Jest**: Unit testing with mocking, snapshots
35
- - **@testing-library**: DOM/React testing
36
- - Test coverage ≥85% enforcement
75
+ ### Database & Storage
76
+ - **Prisma**: 5.22.x - Next-generation ORM
77
+ - **Sequelize**: 6.37.x - SQL ORM
78
+ - **Mongoose**: 8.8.x - MongoDB ODM
79
+ - **Redis**: 4.7.x - Redis client
80
+ - **LowDB**: 7.x - Small JSON database
37
81
 
38
- **Code Quality**:
39
- - **ESLint**: JavaScript linting with recommended rules
40
- - **Prettier**: Code formatting (opinionated)
41
- - **JSDoc**: Type hints via comments (for type safety)
82
+ ### Testing Tools
83
+ - **Jest**: 30.x - JavaScript testing framework
84
+ - **Mocha**: 10.x - Feature-rich test framework
85
+ - **Chai**: 5.x - BDD/TDD assertion library
86
+ - **Supertest**: 7.x - HTTP assertion testing
87
+ - **Playwright**: 1.48.x - End-to-end testing
42
88
 
43
- **Package Management**:
44
- - **npm**: Standard package manager
45
- - **package.json** for dependencies and scripts
46
- - Semantic versioning
89
+ ### Development Tools
90
+ - **ESLint**: 9.x - Pluggable linter
91
+ - **Prettier**: 3.3.x - Code formatter
92
+ - **Babel**: 7.26.x - JavaScript compiler
93
+ - **Nodemon**: 3.1.x - Monitor for changes and restart
94
+ - **PM2**: 5.4.x - Production process manager
47
95
 
48
- **Modern JavaScript**:
49
- - ES6+ features (arrow functions, destructuring, spread/rest)
50
- - Async/await over callbacks
51
- - Module imports (ESM) over CommonJS
96
+ ## Ecosystem Overview
52
97
 
53
- **Best Practices**:
54
- - File ≤300 LOC, function ≤50 LOC
55
- - Prefer `const` over `let`, avoid `var`
56
- - Guard clauses for early returns
57
- - Meaningful names, avoid abbreviations
98
+ ### Project Setup (2025 Best Practice)
58
99
 
59
- ## Examples
60
100
  ```bash
61
- npm run test && npm run lint
101
+ # Modern Node.js project with package.json
102
+ npm init -y
103
+ npm install express helmet cors compression morgan dotenv
104
+ npm install -D nodemon jest supertest eslint prettier concurrently
105
+
106
+ # Project structure
107
+ mkdir -p {src/{routes,middleware,services,utils,models},test/{unit,integration},config,scripts}
108
+
109
+ # TypeScript support for gradual migration
110
+ npm install -D typescript @types/node @types/express ts-node
111
+
112
+ # Modern build setup
113
+ npm install -D vite @vitejs/plugin-node
62
114
  ```
63
115
 
64
- ## Inputs
65
- - Language-specific source directories (e.g. `src/`, `app/`).
66
- - Language-specific build/test configuration files (e.g. `package.json`, `pyproject.toml`, `go.mod`).
67
- - Relevant test suites and sample data.
116
+ ### Modern Project Structure
117
+
118
+ ```
119
+ my-javascript-project/
120
+ ├── package.json # Package configuration
121
+ ├── package-lock.json # Lock file
122
+ ├── .eslintrc.js # ESLint configuration
123
+ ├── .prettierrc # Prettier configuration
124
+ ├── .gitignore
125
+ ├── README.md
126
+ ├── jest.config.js # Jest testing configuration
127
+ ├── nodemon.json # Nodemon configuration
128
+ ├── src/
129
+ │ ├── app.js # Application entry point
130
+ │ ├── routes/ # API routes
131
+ │ │ ├── index.js
132
+ │ │ ├── users.js
133
+ │ │ └── auth.js
134
+ │ ├── middleware/ # Express middleware
135
+ │ │ ├── auth.js
136
+ │ │ ├── validation.js
137
+ │ │ └── errorHandler.js
138
+ │ ├── services/ # Business logic
139
+ │ │ ├── userService.js
140
+ │ │ ├── emailService.js
141
+ │ │ └── cacheService.js
142
+ │ ├── models/ # Data models
143
+ │ │ ├── User.js
144
+ │ │ └── index.js
145
+ │ ├── utils/ # Utility functions
146
+ │ │ ├── logger.js
147
+ │ │ ├── validator.js
148
+ │ │ └── helpers.js
149
+ │ └── config/ # Configuration
150
+ │ ├── database.js
151
+ │ └── index.js
152
+ ├── test/
153
+ │ ├── unit/ # Unit tests
154
+ │ ├── integration/ # Integration tests
155
+ │ └── fixtures/ # Test data
156
+ ├── scripts/ # Build and utility scripts
157
+ ├── docs/ # Documentation
158
+ └── dist/ # Build output
159
+ ```
160
+
161
+ ## Modern JavaScript Patterns
162
+
163
+ ### Async Programming with Modern Patterns
164
+
165
+ ```javascript
166
+ // src/services/userService.js
167
+ const { promisify } = require('util');
168
+ const crypto = require('crypto');
169
+ const EventEmitter = require('events');
170
+
171
+ class UserService extends EventEmitter {
172
+ constructor(database) {
173
+ super();
174
+ this.db = database;
175
+ this.cache = new Map();
176
+ }
177
+
178
+ // Async/await pattern for database operations
179
+ async createUser(userData) {
180
+ try {
181
+ // Validate input
182
+ this.validateUserData(userData);
183
+
184
+ // Hash password
185
+ const hashedPassword = await this.hashPassword(userData.password);
186
+
187
+ // Create user object
188
+ const user = {
189
+ id: crypto.randomUUID(),
190
+ email: userData.email,
191
+ name: userData.name,
192
+ password: hashedPassword,
193
+ createdAt: new Date(),
194
+ updatedAt: new Date()
195
+ };
196
+
197
+ // Save to database
198
+ const savedUser = await this.db.collection('users').insertOne(user);
199
+
200
+ // Emit event
201
+ this.emit('userCreated', savedUser);
202
+
203
+ // Remove password before returning
204
+ const { password, ...userResponse } = savedUser;
205
+
206
+ return userResponse;
207
+ } catch (error) {
208
+ this.emit('error', error);
209
+ throw new Error(`Failed to create user: ${error.message}`);
210
+ }
211
+ }
212
+
213
+ async getUserById(userId) {
214
+ // Check cache first
215
+ if (this.cache.has(userId)) {
216
+ return this.cache.get(userId);
217
+ }
218
+
219
+ try {
220
+ const user = await this.db.collection('users').findOne({ id: userId });
221
+
222
+ if (!user) {
223
+ throw new Error('User not found');
224
+ }
225
+
226
+ // Cache result for 5 minutes
227
+ this.cache.set(userId, user);
228
+ setTimeout(() => this.cache.delete(userId), 5 * 60 * 1000);
229
+
230
+ const { password, ...userResponse } = user;
231
+ return userResponse;
232
+ } catch (error) {
233
+ this.emit('error', error);
234
+ throw error;
235
+ }
236
+ }
237
+
238
+ // Batch processing with Promise.allSettled
239
+ async processUsersBatch(userIds) {
240
+ const userPromises = userIds.map(id =>
241
+ this.getUserById(id).catch(error => ({ id, error: error.message }))
242
+ );
243
+
244
+ const results = await Promise.allSettled(userPromises);
245
+
246
+ return results.map((result, index) => ({
247
+ userId: userIds[index],
248
+ status: result.status,
249
+ value: result.status === 'fulfilled' ? result.value : null,
250
+ reason: result.status === 'rejected' ? result.reason : null
251
+ }));
252
+ }
253
+
254
+ // Async iterator pattern for streaming large datasets
255
+ async *getAllUsersStream() {
256
+ let cursor;
257
+ try {
258
+ cursor = await this.db.collection('users').find();
259
+
260
+ while (await cursor.hasNext()) {
261
+ const user = await cursor.next();
262
+ const { password, ...userResponse } = user;
263
+ yield userResponse;
264
+ }
265
+ } finally {
266
+ if (cursor) {
267
+ await cursor.close();
268
+ }
269
+ }
270
+ }
271
+
272
+ // Promise-based helper methods
273
+ async hashPassword(password) {
274
+ const scrypt = promisify(crypto.scrypt);
275
+ const salt = crypto.randomBytes(16).toString('hex');
276
+ const derivedKey = await scrypt(password, salt, 64);
277
+ return `${salt}:${derivedKey.toString('hex')}`;
278
+ }
279
+
280
+ validateUserData(userData) {
281
+ const errors = [];
282
+
283
+ if (!userData.email || !this.isValidEmail(userData.email)) {
284
+ errors.push('Valid email is required');
285
+ }
286
+
287
+ if (!userData.name || userData.name.length < 2) {
288
+ errors.push('Name must be at least 2 characters');
289
+ }
290
+
291
+ if (!userData.password || userData.password.length < 8) {
292
+ errors.push('Password must be at least 8 characters');
293
+ }
294
+
295
+ if (errors.length > 0) {
296
+ throw new Error(errors.join(', '));
297
+ }
298
+ }
299
+
300
+ isValidEmail(email) {
301
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
302
+ return emailRegex.test(email);
303
+ }
304
+ }
305
+
306
+ module.exports = UserService;
307
+ ```
308
+
309
+ ### Express.js Application with Modern Middleware
310
+
311
+ ```javascript
312
+ // src/app.js
313
+ const express = require('express');
314
+ const helmet = require('helmet');
315
+ const cors = require('cors');
316
+ const compression = require('compression');
317
+ const morgan = require('morgan');
318
+ const rateLimit = require('express-rate-limit');
319
+ const { createProxyMiddleware } = require('http-proxy-middleware');
320
+
321
+ const userRoutes = require('./routes/users');
322
+ const authRoutes = require('./routes/auth');
323
+ const errorHandler = require('./middleware/errorHandler');
324
+ const logger = require('./utils/logger');
325
+ const config = require('./config');
326
+
327
+ const app = express();
328
+
329
+ // Security middleware
330
+ app.use(helmet({
331
+ contentSecurityPolicy: {
332
+ directives: {
333
+ defaultSrc: ["'self'"],
334
+ styleSrc: ["'self'", "'unsafe-inline'"],
335
+ scriptSrc: ["'self'"],
336
+ imgSrc: ["'self'", "data:", "https:"],
337
+ },
338
+ },
339
+ }));
340
+
341
+ // CORS configuration
342
+ app.use(cors({
343
+ origin: config.cors.origins,
344
+ credentials: true,
345
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
346
+ allowedHeaders: ['Content-Type', 'Authorization']
347
+ }));
348
+
349
+ // Rate limiting
350
+ const limiter = rateLimit({
351
+ windowMs: 15 * 60 * 1000, // 15 minutes
352
+ max: config.rateLimit.max, // limit each IP to 100 requests per windowMs
353
+ message: {
354
+ error: 'Too many requests from this IP, please try again later.'
355
+ },
356
+ standardHeaders: true,
357
+ legacyHeaders: false,
358
+ });
359
+
360
+ app.use('/api/', limiter);
361
+
362
+ // Body parsing middleware
363
+ app.use(express.json({
364
+ limit: '10mb',
365
+ verify: (req, res, buf) => {
366
+ req.rawBody = buf;
367
+ }
368
+ }));
369
+
370
+ app.use(express.urlencoded({ extended: true, limit: '10mb' }));
371
+
372
+ // Compression middleware
373
+ app.use(compression());
374
+
375
+ // Logging middleware
376
+ app.use(morgan('combined', {
377
+ stream: {
378
+ write: (message) => logger.info(message.trim())
379
+ }
380
+ }));
381
+
382
+ // Health check endpoint
383
+ app.get('/health', (req, res) => {
384
+ res.status(200).json({
385
+ status: 'healthy',
386
+ timestamp: new Date().toISOString(),
387
+ uptime: process.uptime(),
388
+ version: process.env.npm_package_version
389
+ });
390
+ });
391
+
392
+ // API routes
393
+ app.use('/api/users', userRoutes);
394
+ app.use('/api/auth', authRoutes);
395
+
396
+ // API documentation proxy (if using Swagger)
397
+ if (config.env === 'development') {
398
+ app.use('/api-docs', createProxyMiddleware({
399
+ target: 'http://localhost:3001',
400
+ changeOrigin: true,
401
+ pathRewrite: {
402
+ '^/api-docs': ''
403
+ }
404
+ }));
405
+ }
406
+
407
+ // 404 handler
408
+ app.use('*', (req, res) => {
409
+ res.status(404).json({
410
+ error: 'Route not found',
411
+ path: req.originalUrl,
412
+ method: req.method
413
+ });
414
+ });
415
+
416
+ // Error handling middleware (must be last)
417
+ app.use(errorHandler);
418
+
419
+ module.exports = app;
420
+ ```
421
+
422
+ ### Modern Route Handlers
423
+
424
+ ```javascript
425
+ // src/routes/users.js
426
+ const express = require('express');
427
+ const { body, param, query, validationResult } = require('express-validator');
428
+ const UserService = require('../services/userService');
429
+ const auth = require('../middleware/auth');
430
+ const logger = require('../utils/logger');
431
+
432
+ const router = express.Router();
433
+ const userService = new UserService(database); // Assuming database is available
434
+
435
+ // Validation middleware
436
+ const handleValidationErrors = (req, res, next) => {
437
+ const errors = validationResult(req);
438
+ if (!errors.isEmpty()) {
439
+ return res.status(400).json({
440
+ error: 'Validation failed',
441
+ details: errors.array()
442
+ });
443
+ }
444
+ next();
445
+ };
446
+
447
+ // POST /api/users - Create user
448
+ router.post('/',
449
+ [
450
+ body('email').isEmail().normalizeEmail(),
451
+ body('name').isLength({ min: 2, max: 100 }).trim().escape(),
452
+ body('password').isLength({ min: 8 }).matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/),
453
+ ],
454
+ handleValidationErrors,
455
+ async (req, res, next) => {
456
+ try {
457
+ const user = await userService.createUser(req.body);
458
+
459
+ res.status(201).json({
460
+ success: true,
461
+ data: user,
462
+ message: 'User created successfully'
463
+ });
464
+
465
+ logger.info(`User created: ${user.id}`);
466
+ } catch (error) {
467
+ next(error);
468
+ }
469
+ }
470
+ );
471
+
472
+ // GET /api/users - Get users with pagination and filtering
473
+ router.get('/',
474
+ [
475
+ query('page').optional().isInt({ min: 1 }).toInt(),
476
+ query('limit').optional().isInt({ min: 1, max: 100 }).toInt(),
477
+ query('search').optional().isLength({ min: 1, max: 100 }).trim().escape(),
478
+ ],
479
+ handleValidationErrors,
480
+ async (req, res, next) => {
481
+ try {
482
+ const { page = 1, limit = 10, search } = req.query;
483
+ const skip = (page - 1) * limit;
484
+
485
+ // Build filter
486
+ const filter = {};
487
+ if (search) {
488
+ filter.$or = [
489
+ { name: { $regex: search, $options: 'i' } },
490
+ { email: { $regex: search, $options: 'i' } }
491
+ ];
492
+ }
493
+
494
+ // Get users
495
+ const users = await userService.getUsers(filter, { skip, limit });
496
+ const total = await userService.countUsers(filter);
497
+
498
+ res.json({
499
+ success: true,
500
+ data: {
501
+ users,
502
+ pagination: {
503
+ page,
504
+ limit,
505
+ total,
506
+ pages: Math.ceil(total / limit)
507
+ }
508
+ }
509
+ });
510
+ } catch (error) {
511
+ next(error);
512
+ }
513
+ }
514
+ );
515
+
516
+ // GET /api/users/:id - Get user by ID
517
+ router.get('/:id',
518
+ [
519
+ param('id').isUUID().withMessage('Invalid user ID format')
520
+ ],
521
+ handleValidationErrors,
522
+ async (req, res, next) => {
523
+ try {
524
+ const user = await userService.getUserById(req.params.id);
525
+
526
+ if (!user) {
527
+ return res.status(404).json({
528
+ error: 'User not found',
529
+ userId: req.params.id
530
+ });
531
+ }
532
+
533
+ res.json({
534
+ success: true,
535
+ data: user
536
+ });
537
+ } catch (error) {
538
+ next(error);
539
+ }
540
+ }
541
+ );
542
+
543
+ // PUT /api/users/:id - Update user
544
+ router.put('/:id',
545
+ auth.authenticate,
546
+ [
547
+ param('id').isUUID().withMessage('Invalid user ID format'),
548
+ body('name').optional().isLength({ min: 2, max: 100 }).trim().escape(),
549
+ body('email').optional().isEmail().normalizeEmail(),
550
+ ],
551
+ handleValidationErrors,
552
+ async (req, res, next) => {
553
+ try {
554
+ // Check if user is updating their own profile or is admin
555
+ if (req.user.id !== req.params.id && req.user.role !== 'admin') {
556
+ return res.status(403).json({
557
+ error: 'Forbidden: You can only update your own profile'
558
+ });
559
+ }
560
+
561
+ const updateData = {};
562
+ if (req.body.name) updateData.name = req.body.name;
563
+ if (req.body.email) updateData.email = req.body.email;
564
+
565
+ const user = await userService.updateUser(req.params.id, updateData);
566
+
567
+ res.json({
568
+ success: true,
569
+ data: user,
570
+ message: 'User updated successfully'
571
+ });
572
+ } catch (error) {
573
+ next(error);
574
+ }
575
+ }
576
+ );
577
+
578
+ // DELETE /api/users/:id - Delete user
579
+ router.delete('/:id',
580
+ auth.authenticate,
581
+ auth.requireRole('admin'),
582
+ [
583
+ param('id').isUUID().withMessage('Invalid user ID format')
584
+ ],
585
+ handleValidationErrors,
586
+ async (req, res, next) => {
587
+ try {
588
+ await userService.deleteUser(req.params.id);
589
+
590
+ res.json({
591
+ success: true,
592
+ message: 'User deleted successfully'
593
+ });
594
+ } catch (error) {
595
+ next(error);
596
+ }
597
+ }
598
+ );
599
+
600
+ module.exports = router;
601
+ ```
602
+
603
+ ## Performance Optimization
604
+
605
+ ### Caching and Memory Management
606
+
607
+ ```javascript
608
+ // src/services/cacheService.js
609
+ const NodeCache = require('node-cache');
610
+ const redis = require('redis');
611
+ const crypto = require('crypto');
612
+
613
+ class CacheService {
614
+ constructor(options = {}) {
615
+ // In-memory cache for frequently accessed data
616
+ this.memoryCache = new NodeCache({
617
+ stdTTL: options.memoryTTL || 300, // 5 minutes
618
+ checkperiod: options.checkPeriod || 60, // 1 minute
619
+ useClones: false // Improve performance for objects
620
+ });
621
+
622
+ // Redis cache for distributed caching
623
+ if (options.redis) {
624
+ this.redisClient = redis.createClient(options.redis);
625
+ this.redisClient.on('error', (err) => {
626
+ console.error('Redis client error:', err);
627
+ });
628
+ }
629
+ }
630
+
631
+ // Multi-level caching
632
+ async get(key) {
633
+ // Check memory cache first
634
+ let value = this.memoryCache.get(key);
635
+ if (value !== undefined) {
636
+ return value;
637
+ }
638
+
639
+ // Check Redis cache
640
+ if (this.redisClient) {
641
+ try {
642
+ value = await this.redisClient.get(key);
643
+ if (value) {
644
+ const parsed = JSON.parse(value);
645
+ // Store in memory cache for faster access
646
+ this.memoryCache.set(key, parsed);
647
+ return parsed;
648
+ }
649
+ } catch (error) {
650
+ console.error('Redis get error:', error);
651
+ }
652
+ }
653
+
654
+ return null;
655
+ }
656
+
657
+ async set(key, value, options = {}) {
658
+ const serialized = JSON.stringify(value);
659
+
660
+ // Set in memory cache
661
+ this.memoryCache.set(key, value, options.ttl);
662
+
663
+ // Set in Redis cache
664
+ if (this.redisClient) {
665
+ try {
666
+ await this.redisClient.setEx(key, options.ttl || 300, serialized);
667
+ } catch (error) {
668
+ console.error('Redis set error:', error);
669
+ }
670
+ }
671
+ }
672
+
673
+ // Cache warming
674
+ async warmup(dataLoader, keys) {
675
+ const promises = keys.map(async (key) => {
676
+ const value = await dataLoader(key);
677
+ await this.set(key, value);
678
+ return { key, value };
679
+ });
680
+
681
+ return Promise.all(promises);
682
+ }
683
+
684
+ // Cache invalidation with tags
685
+ async invalidateByPattern(pattern) {
686
+ // Invalidate memory cache
687
+ const keys = this.memoryCache.keys();
688
+ const regex = new RegExp(pattern);
689
+
690
+ keys.forEach(key => {
691
+ if (regex.test(key)) {
692
+ this.memoryCache.del(key);
693
+ }
694
+ });
695
+
696
+ // Invalidate Redis cache
697
+ if (this.redisClient) {
698
+ try {
699
+ const redisKeys = await this.redisClient.keys(pattern);
700
+ if (redisKeys.length > 0) {
701
+ await this.redisClient.del(redisKeys);
702
+ }
703
+ } catch (error) {
704
+ console.error('Redis pattern deletion error:', error);
705
+ }
706
+ }
707
+ }
708
+
709
+ generateCacheKey(prefix, params) {
710
+ const sortedParams = Object.keys(params)
711
+ .sort()
712
+ .reduce((result, key) => {
713
+ result[key] = params[key];
714
+ return result;
715
+ }, {});
716
+
717
+ const paramString = JSON.stringify(sortedParams);
718
+ const hash = crypto.createHash('md5').update(paramString).digest('hex');
719
+
720
+ return `${prefix}:${hash}`;
721
+ }
722
+
723
+ // Memory leak prevention
724
+ cleanup() {
725
+ this.memoryCache.flushAll();
726
+
727
+ if (this.redisClient) {
728
+ this.redisClient.quit();
729
+ }
730
+ }
731
+ }
732
+
733
+ module.exports = CacheService;
734
+ ```
735
+
736
+ ### Memory-Efficient Data Processing
737
+
738
+ ```javascript
739
+ // src/utils/streamProcessor.js
740
+ const { Transform, pipeline } = require('stream');
741
+ const fs = require('fs');
742
+ const readline = require('readline');
743
+ const { promisify } = require('util');
744
+
745
+ const pipelineAsync = promisify(pipeline);
746
+
747
+ class StreamProcessor {
748
+ // Process large files without loading into memory
749
+ static async processLargeFile(filePath, processor) {
750
+ const fileStream = fs.createReadStream(filePath);
751
+ const rl = readline.createInterface({
752
+ input: fileStream,
753
+ crlfDelay: Infinity
754
+ });
755
+
756
+ let lineNumber = 0;
757
+ const results = [];
758
+
759
+ for await (const line of rl) {
760
+ try {
761
+ const result = await processor(line, lineNumber);
762
+ if (result !== null) {
763
+ results.push(result);
764
+ }
765
+ } catch (error) {
766
+ console.error(`Error processing line ${lineNumber}:`, error);
767
+ }
768
+
769
+ lineNumber++;
770
+
771
+ // Yield control periodically to prevent blocking
772
+ if (lineNumber % 1000 === 0) {
773
+ await new Promise(resolve => setImmediate(resolve));
774
+ }
775
+ }
776
+
777
+ return results;
778
+ }
779
+
780
+ // Transform stream for data processing
781
+ static createTransformStream(processor) {
782
+ return new Transform({
783
+ objectMode: true,
784
+ transform(chunk, encoding, callback) {
785
+ try {
786
+ const result = processor(chunk);
787
+ callback(null, result);
788
+ } catch (error) {
789
+ callback(error);
790
+ }
791
+ }
792
+ });
793
+ }
794
+
795
+ // Batch processing with memory management
796
+ static async* batchProcessor(data, batchSize = 100) {
797
+ for (let i = 0; i < data.length; i += batchSize) {
798
+ const batch = data.slice(i, i + batchSize);
799
+ yield batch;
800
+
801
+ // Force garbage collection every 10 batches
802
+ if (i % (batchSize * 10) === 0) {
803
+ if (global.gc) {
804
+ global.gc();
805
+ }
806
+ }
807
+ }
808
+ }
809
+
810
+ // Memory-efficient CSV processing
811
+ static async processCSV(filePath, options = {}) {
812
+ const { transform, filter, batchSize = 1000 } = options;
813
+ const results = [];
814
+ let currentBatch = [];
815
+
816
+ return new Promise((resolve, reject) => {
817
+ const fileStream = fs.createReadStream(filePath);
818
+ const rl = readline.createInterface({
819
+ input: fileStream,
820
+ crlfDelay: Infinity
821
+ });
822
+
823
+ let lineNumber = 0;
824
+ let headers = [];
825
+
826
+ rl.on('line', async (line) => {
827
+ try {
828
+ const values = line.split(',');
829
+
830
+ if (lineNumber === 0) {
831
+ headers = values;
832
+ } else {
833
+ const record = {};
834
+ headers.forEach((header, index) => {
835
+ record[header.trim()] = values[index]?.trim() || '';
836
+ });
837
+
838
+ // Apply filter if provided
839
+ if (filter && !filter(record)) {
840
+ return;
841
+ }
842
+
843
+ // Apply transform if provided
844
+ const processedRecord = transform ? transform(record) : record;
845
+
846
+ currentBatch.push(processedRecord);
847
+
848
+ // Process batch when full
849
+ if (currentBatch.length >= batchSize) {
850
+ results.push(...currentBatch);
851
+ currentBatch = [];
852
+
853
+ // Yield control
854
+ await new Promise(resolve => setImmediate(resolve));
855
+ }
856
+ }
857
+ } catch (error) {
858
+ console.error(`Error processing line ${lineNumber}:`, error);
859
+ }
860
+
861
+ lineNumber++;
862
+ });
863
+
864
+ rl.on('close', () => {
865
+ // Process remaining records
866
+ results.push(...currentBatch);
867
+ resolve(results);
868
+ });
869
+
870
+ rl.on('error', reject);
871
+ });
872
+ }
873
+ }
874
+
875
+ module.exports = StreamProcessor;
876
+ ```
877
+
878
+ ## Testing Strategies
879
+
880
+ ### Comprehensive Unit Testing
881
+
882
+ ```javascript
883
+ // test/unit/userService.test.js
884
+ const UserService = require('../../src/services/userService');
885
+ const { EventEmitter } = require('events');
886
+
887
+ // Mock database
888
+ const mockDatabase = {
889
+ collection: jest.fn(() => ({
890
+ insertOne: jest.fn(),
891
+ findOne: jest.fn(),
892
+ find: jest.fn(),
893
+ updateOne: jest.fn(),
894
+ deleteOne: jest.fn(),
895
+ }))
896
+ };
897
+
898
+ describe('UserService', () => {
899
+ let userService;
900
+
901
+ beforeEach(() => {
902
+ userService = new UserService(mockDatabase);
903
+ jest.clearAllMocks();
904
+ });
905
+
906
+ describe('createUser', () => {
907
+ it('should create a user with valid data', async () => {
908
+ const userData = {
909
+ email: 'test@example.com',
910
+ name: 'Test User',
911
+ password: 'Password123'
912
+ };
913
+
914
+ const mockUser = {
915
+ id: 'user-123',
916
+ email: userData.email,
917
+ name: userData.name,
918
+ password: 'hashed-password',
919
+ createdAt: new Date(),
920
+ updatedAt: new Date()
921
+ };
922
+
923
+ mockDatabase.collection().insertOne.mockResolvedValue(mockUser);
924
+
925
+ const result = await userService.createUser(userData);
926
+
927
+ expect(result).toEqual({
928
+ id: mockUser.id,
929
+ email: mockUser.email,
930
+ name: mockUser.name,
931
+ createdAt: mockUser.createdAt,
932
+ updatedAt: mockUser.updatedAt
933
+ });
934
+
935
+ expect(mockDatabase.collection).toHaveBeenCalledWith('users');
936
+ expect(mockDatabase.collection().insertOne).toHaveBeenCalledWith(
937
+ expect.objectContaining({
938
+ email: userData.email,
939
+ name: userData.name,
940
+ password: expect.stringMatching(/^[a-f0-9]+:.+$/)
941
+ })
942
+ );
943
+ });
944
+
945
+ it('should reject invalid email', async () => {
946
+ const userData = {
947
+ email: 'invalid-email',
948
+ name: 'Test User',
949
+ password: 'Password123'
950
+ };
951
+
952
+ await expect(userService.createUser(userData))
953
+ .rejects.toThrow('Valid email is required');
954
+ });
955
+
956
+ it('should reject short password', async () => {
957
+ const userData = {
958
+ email: 'test@example.com',
959
+ name: 'Test User',
960
+ password: 'short'
961
+ };
962
+
963
+ await expect(userService.createUser(userData))
964
+ .rejects.toThrow('Password must be at least 8 characters');
965
+ });
966
+
967
+ it('should emit userCreated event', async () => {
968
+ const userData = {
969
+ email: 'test@example.com',
970
+ name: 'Test User',
971
+ password: 'Password123'
972
+ };
973
+
974
+ const mockUser = {
975
+ id: 'user-123',
976
+ email: userData.email,
977
+ name: userData.name,
978
+ password: 'hashed-password',
979
+ createdAt: new Date(),
980
+ updatedAt: new Date()
981
+ };
982
+
983
+ mockDatabase.collection().insertOne.mockResolvedValue(mockUser);
984
+
985
+ const eventSpy = jest.fn();
986
+ userService.on('userCreated', eventSpy);
987
+
988
+ await userService.createUser(userData);
989
+
990
+ expect(eventSpy).toHaveBeenCalledWith(mockUser);
991
+ });
992
+ });
993
+
994
+ describe('getUserById', () => {
995
+ it('should return user when found', async () => {
996
+ const userId = 'user-123';
997
+ const mockUser = {
998
+ id: userId,
999
+ email: 'test@example.com',
1000
+ name: 'Test User',
1001
+ password: 'hashed-password'
1002
+ };
1003
+
1004
+ mockDatabase.collection().findOne.mockResolvedValue(mockUser);
1005
+
1006
+ const result = await userService.getUserById(userId);
1007
+
1008
+ expect(result).toEqual({
1009
+ id: mockUser.id,
1010
+ email: mockUser.email,
1011
+ name: mockUser.name
1012
+ });
1013
+ });
1014
+
1015
+ it('should return null when user not found', async () => {
1016
+ const userId = 'nonexistent-user';
1017
+
1018
+ mockDatabase.collection().findOne.mockResolvedValue(null);
1019
+
1020
+ const result = await userService.getUserById(userId);
1021
+
1022
+ expect(result).toBeNull();
1023
+ });
1024
+
1025
+ it('should use cache for subsequent calls', async () => {
1026
+ const userId = 'user-123';
1027
+ const mockUser = {
1028
+ id: userId,
1029
+ email: 'test@example.com',
1030
+ name: 'Test User',
1031
+ password: 'hashed-password'
1032
+ };
1033
+
1034
+ mockDatabase.collection().findOne.mockResolvedValue(mockUser);
1035
+
1036
+ // First call
1037
+ await userService.getUserById(userId);
1038
+ // Second call (should use cache)
1039
+ await userService.getUserById(userId);
1040
+
1041
+ // Database should be called only once
1042
+ expect(mockDatabase.collection().findOne).toHaveBeenCalledTimes(1);
1043
+ });
1044
+ });
1045
+
1046
+ describe('processUsersBatch', () => {
1047
+ it('should process users successfully', async () => {
1048
+ const userIds = ['user-1', 'user-2', 'user-3'];
1049
+
1050
+ mockDatabase.collection().findOne.mockImplementation((query) => {
1051
+ const user = {
1052
+ id: query.id,
1053
+ email: `${query.id}@example.com`,
1054
+ name: `User ${query.id}`,
1055
+ password: 'hashed-password'
1056
+ };
1057
+ return Promise.resolve(user);
1058
+ });
1059
+
1060
+ const results = await userService.processUsersBatch(userIds);
1061
+
1062
+ expect(results).toHaveLength(3);
1063
+ expect(results[0]).toEqual({
1064
+ userId: 'user-1',
1065
+ status: 'fulfilled',
1066
+ value: expect.objectContaining({ id: 'user-1' }),
1067
+ reason: null
1068
+ });
1069
+ });
1070
+
1071
+ it('should handle errors gracefully', async () => {
1072
+ const userIds = ['user-1', 'invalid-user'];
1073
+
1074
+ mockDatabase.collection().findOne.mockImplementation((query) => {
1075
+ if (query.id === 'invalid-user') {
1076
+ return Promise.reject(new Error('Database error'));
1077
+ }
1078
+ return Promise.resolve({
1079
+ id: query.id,
1080
+ email: `${query.id}@example.com`,
1081
+ name: `User ${query.id}`,
1082
+ password: 'hashed-password'
1083
+ });
1084
+ });
1085
+
1086
+ const results = await userService.processUsersBatch(userIds);
1087
+
1088
+ expect(results).toHaveLength(2);
1089
+ expect(results[0].status).toBe('fulfilled');
1090
+ expect(results[1].status).toBe('rejected');
1091
+ expect(results[1].reason).toContain('Database error');
1092
+ });
1093
+ });
1094
+
1095
+ describe('getAllUsersStream', () => {
1096
+ it('should stream users one by one', async () => {
1097
+ const mockUsers = [
1098
+ { id: 'user-1', email: 'user1@example.com', name: 'User 1', password: 'hash1' },
1099
+ { id: 'user-2', email: 'user2@example.com', name: 'User 2', password: 'hash2' }
1100
+ ];
1101
+
1102
+ const mockCursor = {
1103
+ hasNext: jest.fn()
1104
+ .mockResolvedValueOnce(true)
1105
+ .mockResolvedValueOnce(true)
1106
+ .mockResolvedValueOnce(false),
1107
+ next: jest.fn()
1108
+ .mockResolvedValueOnce(mockUsers[0])
1109
+ .mockResolvedValueOnce(mockUsers[1]),
1110
+ close: jest.fn().mockResolvedValue()
1111
+ };
1112
+
1113
+ mockDatabase.collection().find.mockResolvedValue(mockCursor);
1114
+
1115
+ const users = [];
1116
+ for await (const user of userService.getAllUsersStream()) {
1117
+ users.push(user);
1118
+ }
1119
+
1120
+ expect(users).toHaveLength(2);
1121
+ expect(users[0]).toEqual({
1122
+ id: 'user-1',
1123
+ email: 'user1@example.com',
1124
+ name: 'User 1'
1125
+ });
1126
+ expect(mockCursor.close).toHaveBeenCalled();
1127
+ });
1128
+ });
1129
+ });
1130
+ ```
1131
+
1132
+ ### Integration Testing with Test Databases
1133
+
1134
+ ```javascript
1135
+ // test/integration/api.test.js
1136
+ const request = require('supertest');
1137
+ const app = require('../../src/app');
1138
+ const { MongoMemoryServer } = require('mongodb-memory-server');
1139
+ const { MongoClient } = require('mongodb');
1140
+
1141
+ describe('User API Integration Tests', () => {
1142
+ let mongoServer;
1143
+ let client;
1144
+ let database;
1145
+
1146
+ beforeAll(async () => {
1147
+ // Start in-memory MongoDB server
1148
+ mongoServer = await MongoMemoryServer.create();
1149
+ const mongoUri = mongoServer.getUri();
1150
+
1151
+ // Connect to test database
1152
+ client = new MongoClient(mongoUri);
1153
+ await client.connect();
1154
+ database = client.db('testdb');
1155
+
1156
+ // Mock database in app
1157
+ require('../../src/config/database').setDatabase(database);
1158
+ });
1159
+
1160
+ afterAll(async () => {
1161
+ await client.close();
1162
+ await mongoServer.stop();
1163
+ });
1164
+
1165
+ beforeEach(async () => {
1166
+ // Clean up database before each test
1167
+ await database.collection('users').deleteMany({});
1168
+ });
1169
+
1170
+ describe('POST /api/users', () => {
1171
+ it('should create a new user', async () => {
1172
+ const userData = {
1173
+ email: 'integration@example.com',
1174
+ name: 'Integration User',
1175
+ password: 'Password123'
1176
+ };
1177
+
1178
+ const response = await request(app)
1179
+ .post('/api/users')
1180
+ .send(userData)
1181
+ .expect(201);
1182
+
1183
+ expect(response.body.success).toBe(true);
1184
+ expect(response.body.data.email).toBe(userData.email);
1185
+ expect(response.body.data.name).toBe(userData.name);
1186
+ expect(response.body.data.password).toBeUndefined(); // Password should not be returned
1187
+
1188
+ // Verify user was actually created in database
1189
+ const userInDb = await database.collection('users').findOne({ email: userData.email });
1190
+ expect(userInDb).toBeTruthy();
1191
+ expect(userInDb.name).toBe(userData.name);
1192
+ });
1193
+
1194
+ it('should return validation error for invalid data', async () => {
1195
+ const invalidUserData = {
1196
+ email: 'invalid-email',
1197
+ name: 'A', // Too short
1198
+ password: '123' // Too short
1199
+ };
1200
+
1201
+ const response = await request(app)
1202
+ .post('/api/users')
1203
+ .send(invalidUserData)
1204
+ .expect(400);
68
1205
 
69
- ## Outputs
70
- - Test/lint execution plan tailored to the selected language.
71
- - List of key language idioms and review checkpoints.
1206
+ expect(response.body.error).toBe('Validation failed');
1207
+ expect(response.body.details).toHaveLength(3);
1208
+ });
72
1209
 
73
- ## Failure Modes
74
- - When the language runtime or package manager is not installed.
75
- - When the main language cannot be determined in a multilingual project.
1210
+ it('should return error for duplicate email', async () => {
1211
+ const userData = {
1212
+ email: 'duplicate@example.com',
1213
+ name: 'User One',
1214
+ password: 'Password123'
1215
+ };
76
1216
 
77
- ## Dependencies
78
- - Access to the project file is required using the Read/Grep tool.
79
- - When used with `Skill("moai-foundation-langs")`, it is easy to share cross-language conventions.
1217
+ // Create first user
1218
+ await request(app)
1219
+ .post('/api/users')
1220
+ .send(userData)
1221
+ .expect(201);
80
1222
 
81
- ## References
82
- - MDN Web Docs. "JavaScript Guide." https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide (accessed 2025-03-29).
83
- - Jest. "Getting Started." https://jestjs.io/docs/getting-started (accessed 2025-03-29).
1223
+ // Try to create user with same email
1224
+ const response = await request(app)
1225
+ .post('/api/users')
1226
+ .send({
1227
+ ...userData,
1228
+ name: 'User Two'
1229
+ })
1230
+ .expect(500);
84
1231
 
85
- ## Changelog
86
- - 2025-03-29: Input/output/failure response/reference information for each language has been specified.
1232
+ expect(response.body.error).toContain('duplicate');
1233
+ });
1234
+ });
87
1235
 
88
- ## Works well with
1236
+ describe('GET /api/users', () => {
1237
+ beforeEach(async () => {
1238
+ // Insert test data
1239
+ await database.collection('users').insertMany([
1240
+ {
1241
+ id: 'user-1',
1242
+ email: 'user1@example.com',
1243
+ name: 'User One',
1244
+ password: 'hashed-pass',
1245
+ createdAt: new Date()
1246
+ },
1247
+ {
1248
+ id: 'user-2',
1249
+ email: 'user2@example.com',
1250
+ name: 'User Two',
1251
+ password: 'hashed-pass',
1252
+ createdAt: new Date()
1253
+ }
1254
+ ]);
1255
+ });
1256
+
1257
+ it('should return paginated users', async () => {
1258
+ const response = await request(app)
1259
+ .get('/api/users?page=1&limit=10')
1260
+ .expect(200);
1261
+
1262
+ expect(response.body.success).toBe(true);
1263
+ expect(response.body.data.users).toHaveLength(2);
1264
+ expect(response.body.data.pagination).toEqual({
1265
+ page: 1,
1266
+ limit: 10,
1267
+ total: 2,
1268
+ pages: 1
1269
+ });
1270
+ });
1271
+
1272
+ it('should search users by name', async () => {
1273
+ const response = await request(app)
1274
+ .get('/api/users?search=One')
1275
+ .expect(200);
1276
+
1277
+ expect(response.body.data.users).toHaveLength(1);
1278
+ expect(response.body.data.users[0].name).toBe('User One');
1279
+ });
1280
+ });
1281
+
1282
+ describe('GET /api/users/:id', () => {
1283
+ let userId;
1284
+
1285
+ beforeEach(async () => {
1286
+ // Create a test user
1287
+ const result = await database.collection('users').insertOne({
1288
+ id: 'test-user-123',
1289
+ email: 'getuser@example.com',
1290
+ name: 'Get User',
1291
+ password: 'hashed-pass',
1292
+ createdAt: new Date()
1293
+ });
1294
+ userId = 'test-user-123';
1295
+ });
1296
+
1297
+ it('should return user when valid ID is provided', async () => {
1298
+ const response = await request(app)
1299
+ .get(`/api/users/${userId}`)
1300
+ .expect(200);
1301
+
1302
+ expect(response.body.success).toBe(true);
1303
+ expect(response.body.data.email).toBe('getuser@example.com');
1304
+ expect(response.body.data.password).toBeUndefined();
1305
+ });
1306
+
1307
+ it('should return 404 for non-existent user', async () => {
1308
+ const response = await request(app)
1309
+ .get('/api/users/non-existent-id')
1310
+ .expect(404);
1311
+
1312
+ expect(response.body.error).toBe('User not found');
1313
+ });
1314
+
1315
+ it('should return 400 for invalid ID format', async () => {
1316
+ const response = await request(app)
1317
+ .get('/api/users/invalid-id')
1318
+ .expect(400);
1319
+
1320
+ expect(response.body.error).toBe('Validation failed');
1321
+ });
1322
+ });
1323
+ });
1324
+ ```
1325
+
1326
+ ## Security Best Practices
1327
+
1328
+ ### Input Validation and Sanitization
1329
+
1330
+ ```javascript
1331
+ // src/middleware/validation.js
1332
+ const { body, param, query, validationResult } = require('express-validator');
1333
+ const createDOMPurify = require('dompurify');
1334
+ const { JSDOM } = require('jsdom');
1335
+
1336
+ // Create DOMPurify instance
1337
+ const window = new JSDOM('').window;
1338
+ const dompurify = createDOMPurify(window);
1339
+
1340
+ class SecurityValidator {
1341
+ // XSS prevention
1342
+ static sanitizeHtml(input) {
1343
+ if (typeof input !== 'string') {
1344
+ return input;
1345
+ }
1346
+
1347
+ return dompurify.sanitize(input, {
1348
+ ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
1349
+ ALLOWED_ATTR: ['href', 'title'],
1350
+ ALLOW_DATA_ATTR: false
1351
+ });
1352
+ }
1353
+
1354
+ // SQL injection prevention
1355
+ static sanitizeSql(input) {
1356
+ if (typeof input !== 'string') {
1357
+ return input;
1358
+ }
1359
+
1360
+ // Remove dangerous SQL characters and patterns
1361
+ return input
1362
+ .replace(/['"\;]/g, '')
1363
+ .replace(/--/g, '')
1364
+ .replace(/\/\*/g, '')
1365
+ .replace(/\*\//g, '')
1366
+ .replace(/drop\s+table/i, '')
1367
+ .replace(/delete\s+from/i, '')
1368
+ .replace(/insert\s+into/i, '')
1369
+ .replace(/update\s+\w+\s+set/i, '');
1370
+ }
1371
+
1372
+ // Email validation with advanced checks
1373
+ static isValidEmail(email) {
1374
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
1375
+
1376
+ if (!emailRegex.test(email)) {
1377
+ return false;
1378
+ }
1379
+
1380
+ // Additional checks
1381
+ const [localPart, domain] = email.split('@');
1382
+
1383
+ // Local part validation
1384
+ if (localPart.length > 64) return false;
1385
+ if (localPart.startsWith('.') || localPart.endsWith('.')) return false;
1386
+ if (localPart.includes('..')) return false;
1387
+
1388
+ // Domain validation
1389
+ if (domain.length > 253) return false;
1390
+ if (!domain.includes('.')) return false;
1391
+
1392
+ // Check for disposable email domains
1393
+ const disposableDomains = ['10minutemail.com', 'tempmail.org', 'guerrillamail.com'];
1394
+ if (disposableDomains.some(d => domain.toLowerCase().includes(d))) {
1395
+ return false;
1396
+ }
1397
+
1398
+ return true;
1399
+ }
1400
+
1401
+ // Password strength validation
1402
+ static validatePasswordStrength(password) {
1403
+ const issues = [];
1404
+
1405
+ if (password.length < 8) {
1406
+ issues.push('Password must be at least 8 characters long');
1407
+ }
1408
+
1409
+ if (password.length > 128) {
1410
+ issues.push('Password must be less than 128 characters');
1411
+ }
1412
+
1413
+ if (!/[a-z]/.test(password)) {
1414
+ issues.push('Password must contain at least one lowercase letter');
1415
+ }
1416
+
1417
+ if (!/[A-Z]/.test(password)) {
1418
+ issues.push('Password must contain at least one uppercase letter');
1419
+ }
1420
+
1421
+ if (!/\d/.test(password)) {
1422
+ issues.push('Password must contain at least one number');
1423
+ }
1424
+
1425
+ if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
1426
+ issues.push('Password must contain at least one special character');
1427
+ }
1428
+
1429
+ // Check for common patterns
1430
+ const commonPatterns = [
1431
+ /123456/,
1432
+ /password/i,
1433
+ /qwerty/i,
1434
+ /admin/i,
1435
+ /letmein/i
1436
+ ];
1437
+
1438
+ if (commonPatterns.some(pattern => pattern.test(password))) {
1439
+ issues.push('Password contains common patterns that make it weak');
1440
+ }
1441
+
1442
+ // Check for sequential characters
1443
+ const isSequential = (str) => {
1444
+ for (let i = 0; i < str.length - 2; i++) {
1445
+ const char1 = str.charCodeAt(i);
1446
+ const char2 = str.charCodeAt(i + 1);
1447
+ const char3 = str.charCodeAt(i + 2);
1448
+
1449
+ if (char2 === char1 + 1 && char3 === char2 + 1) {
1450
+ return true;
1451
+ }
1452
+ }
1453
+ return false;
1454
+ };
1455
+
1456
+ if (isSequential(password.toLowerCase())) {
1457
+ issues.push('Password contains sequential characters');
1458
+ }
1459
+
1460
+ return {
1461
+ isValid: issues.length === 0,
1462
+ issues,
1463
+ score: Math.max(0, 100 - (issues.length * 20))
1464
+ };
1465
+ }
1466
+
1467
+ // Rate limiting by IP and user
1468
+ static createRateLimiter(options = {}) {
1469
+ const rateLimit = require('express-rate-limit');
1470
+
1471
+ return rateLimit({
1472
+ windowMs: options.windowMs || 15 * 60 * 1000, // 15 minutes
1473
+ max: options.max || 100, // limit each IP
1474
+ message: {
1475
+ error: 'Too many requests, please try again later',
1476
+ retryAfter: options.windowMs / 1000
1477
+ },
1478
+ standardHeaders: true,
1479
+ legacyHeaders: false,
1480
+ keyGenerator: options.keyGenerator || ((req) => {
1481
+ return req.ip + (req.user ? `:${req.user.id}` : '');
1482
+ }),
1483
+ skip: options.skip || ((req) => {
1484
+ // Skip rate limiting for certain routes or trusted IPs
1485
+ return req.path.startsWith('/health') || req.path.startsWith('/metrics');
1486
+ })
1487
+ });
1488
+ }
1489
+
1490
+ // Content Security Policy
1491
+ static getCSPHeaders() {
1492
+ return {
1493
+ 'Content-Security-Policy': [
1494
+ "default-src 'self'",
1495
+ "script-src 'self' 'unsafe-inline' https://trusted-cdn.com",
1496
+ "style-src 'self' 'unsafe-inline'",
1497
+ "img-src 'self' data: https:",
1498
+ "font-src 'self'",
1499
+ "connect-src 'self' https://api.example.com",
1500
+ "frame-ancestors 'none'",
1501
+ "base-uri 'self'",
1502
+ "form-action 'self'"
1503
+ ].join('; '),
1504
+ 'X-Content-Type-Options': 'nosniff',
1505
+ 'X-Frame-Options': 'DENY',
1506
+ 'X-XSS-Protection': '1; mode=block',
1507
+ 'Referrer-Policy': 'strict-origin-when-cross-origin',
1508
+ 'Permissions-Policy': 'camera=(), microphone=(), geolocation=()'
1509
+ };
1510
+ }
1511
+ }
1512
+
1513
+ module.exports = SecurityValidator;
1514
+ ```
1515
+
1516
+ ### Authentication and Authorization
1517
+
1518
+ ```javascript
1519
+ // src/middleware/auth.js
1520
+ const jwt = require('jsonwebtoken');
1521
+ const bcrypt = require('bcrypt');
1522
+ const crypto = require('crypto');
1523
+ const UserService = require('../services/userService');
1524
+
1525
+ class AuthMiddleware {
1526
+ constructor(jwtSecret = process.env.JWT_SECRET) {
1527
+ this.jwtSecret = jwtSecret;
1528
+ this.userService = new UserService(database); // Assuming database is available
1529
+ }
1530
+
1531
+ // JWT token generation
1532
+ generateToken(payload, options = {}) {
1533
+ const defaultOptions = {
1534
+ expiresIn: '24h',
1535
+ issuer: 'your-app',
1536
+ audience: 'your-app-users'
1537
+ };
1538
+
1539
+ return jwt.sign(payload, this.jwtSecret, { ...defaultOptions, ...options });
1540
+ }
1541
+
1542
+ // JWT token verification
1543
+ verifyToken(token) {
1544
+ try {
1545
+ return jwt.verify(token, this.jwtSecret);
1546
+ } catch (error) {
1547
+ if (error.name === 'TokenExpiredError') {
1548
+ throw new Error('Token expired');
1549
+ } else if (error.name === 'JsonWebTokenError') {
1550
+ throw new Error('Invalid token');
1551
+ }
1552
+ throw new Error('Token verification failed');
1553
+ }
1554
+ }
1555
+
1556
+ // Authentication middleware
1557
+ authenticate() {
1558
+ return async (req, res, next) => {
1559
+ try {
1560
+ const authHeader = req.headers.authorization;
1561
+
1562
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
1563
+ return res.status(401).json({
1564
+ error: 'Authorization header required'
1565
+ });
1566
+ }
1567
+
1568
+ const token = authHeader.substring(7);
1569
+ const decoded = this.verifyToken(token);
1570
+
1571
+ // Get user from database to ensure they still exist
1572
+ const user = await this.userService.getUserById(decoded.sub);
1573
+ if (!user) {
1574
+ return res.status(401).json({
1575
+ error: 'User not found'
1576
+ });
1577
+ }
1578
+
1579
+ // Add user to request object
1580
+ req.user = user;
1581
+ req.token = token;
1582
+
1583
+ next();
1584
+ } catch (error) {
1585
+ return res.status(401).json({
1586
+ error: error.message
1587
+ });
1588
+ }
1589
+ };
1590
+ }
1591
+
1592
+ // Role-based authorization
1593
+ requireRole(roles) {
1594
+ return (req, res, next) => {
1595
+ if (!req.user) {
1596
+ return res.status(401).json({
1597
+ error: 'Authentication required'
1598
+ });
1599
+ }
1600
+
1601
+ const userRoles = Array.isArray(req.user.roles) ? req.user.roles : [req.user.role];
1602
+ const requiredRoles = Array.isArray(roles) ? roles : [roles];
1603
+
1604
+ const hasRequiredRole = requiredRoles.some(role => userRoles.includes(role));
1605
+
1606
+ if (!hasRequiredRole) {
1607
+ return res.status(403).json({
1608
+ error: 'Insufficient permissions',
1609
+ required: requiredRoles,
1610
+ current: userRoles
1611
+ });
1612
+ }
1613
+
1614
+ next();
1615
+ };
1616
+ }
1617
+
1618
+ // Permission-based authorization
1619
+ requirePermission(permission) {
1620
+ return (req, res, next) => {
1621
+ if (!req.user) {
1622
+ return res.status(401).json({
1623
+ error: 'Authentication required'
1624
+ });
1625
+ }
1626
+
1627
+ const userPermissions = req.user.permissions || [];
1628
+
1629
+ if (!userPermissions.includes(permission)) {
1630
+ return res.status(403).json({
1631
+ error: 'Insufficient permissions',
1632
+ required: permission,
1633
+ current: userPermissions
1634
+ });
1635
+ }
1636
+
1637
+ next();
1638
+ };
1639
+ }
1640
+
1641
+ // Resource ownership check
1642
+ requireOwnership(resourceIdParam = 'id') {
1643
+ return async (req, res, next) => {
1644
+ try {
1645
+ if (!req.user) {
1646
+ return res.status(401).json({
1647
+ error: 'Authentication required'
1648
+ });
1649
+ }
1650
+
1651
+ const resourceId = req.params[resourceIdParam];
1652
+ const resource = await this.getResourceById(resourceId, req.path);
1653
+
1654
+ if (!resource) {
1655
+ return res.status(404).json({
1656
+ error: 'Resource not found'
1657
+ });
1658
+ }
1659
+
1660
+ // Check if user owns the resource or is admin
1661
+ const isOwner = resource.userId === req.user.id;
1662
+ const isAdmin = req.user.role === 'admin';
1663
+
1664
+ if (!isOwner && !isAdmin) {
1665
+ return res.status(403).json({
1666
+ error: 'Access denied: You can only access your own resources'
1667
+ });
1668
+ }
1669
+
1670
+ req.resource = resource;
1671
+ next();
1672
+ } catch (error) {
1673
+ return res.status(500).json({
1674
+ error: 'Authorization check failed'
1675
+ });
1676
+ }
1677
+ };
1678
+ }
1679
+
1680
+ // Password hashing and verification
1681
+ async hashPassword(password) {
1682
+ const saltRounds = 12;
1683
+ return bcrypt.hash(password, saltRounds);
1684
+ }
1685
+
1686
+ async verifyPassword(password, hashedPassword) {
1687
+ return bcrypt.compare(password, hashedPassword);
1688
+ }
1689
+
1690
+ // Password reset token generation
1691
+ generateResetToken() {
1692
+ return crypto.randomBytes(32).toString('hex');
1693
+ }
1694
+
1695
+ // Session management
1696
+ async createSession(user, req) {
1697
+ const sessionToken = crypto.randomBytes(32).toString('hex');
1698
+
1699
+ // Store session in database or Redis
1700
+ await this.userService.createSession({
1701
+ token: sessionToken,
1702
+ userId: user.id,
1703
+ userAgent: req.get('User-Agent'),
1704
+ ipAddress: req.ip,
1705
+ createdAt: new Date(),
1706
+ expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours
1707
+ });
1708
+
1709
+ return sessionToken;
1710
+ }
1711
+
1712
+ async invalidateSession(token) {
1713
+ await this.userService.deleteSession(token);
1714
+ }
1715
+
1716
+ // Helper method to get resource by ID
1717
+ async getResourceById(id, path) {
1718
+ // This is a simplified example
1719
+ // In practice, you'd have different strategies for different resource types
1720
+
1721
+ if (path.includes('/users')) {
1722
+ return await this.userService.getUserById(id);
1723
+ }
1724
+
1725
+ if (path.includes('/posts')) {
1726
+ return await this.userService.getPostById(id);
1727
+ }
1728
+
1729
+ return null;
1730
+ }
1731
+ }
1732
+
1733
+ module.exports = AuthMiddleware;
1734
+ ```
1735
+
1736
+ ## Integration Patterns
1737
+
1738
+ ### REST API with GraphQL Integration
1739
+
1740
+ ```javascript
1741
+ // src/graphql/resolver.js
1742
+ const { gql } = require('apollo-server-express');
1743
+ const UserService = require('../services/userService');
1744
+
1745
+ const userService = new UserService(database);
1746
+
1747
+ // GraphQL type definitions
1748
+ const typeDefs = gql`
1749
+ type User {
1750
+ id: ID!
1751
+ email: String!
1752
+ name: String!
1753
+ createdAt: String!
1754
+ updatedAt: String!
1755
+ }
1756
+
1757
+ type Query {
1758
+ users(limit: Int, offset: Int, search: String): [User!]!
1759
+ user(id: ID!): User
1760
+ }
1761
+
1762
+ type Mutation {
1763
+ createUser(input: CreateUserInput!): User!
1764
+ updateUser(id: ID!, input: UpdateUserInput!): User!
1765
+ deleteUser(id: ID!): Boolean!
1766
+ }
1767
+
1768
+ input CreateUserInput {
1769
+ email: String!
1770
+ name: String!
1771
+ password: String!
1772
+ }
1773
+
1774
+ input UpdateUserInput {
1775
+ name: String
1776
+ email: String
1777
+ }
1778
+ `;
1779
+
1780
+ // GraphQL resolvers
1781
+ const resolvers = {
1782
+ Query: {
1783
+ users: async (_, { limit = 10, offset = 0, search }) => {
1784
+ try {
1785
+ const filter = {};
1786
+ if (search) {
1787
+ filter.$or = [
1788
+ { name: { $regex: search, $options: 'i' } },
1789
+ { email: { $regex: search, $options: 'i' } }
1790
+ ];
1791
+ }
1792
+
1793
+ const users = await userService.getUsers(filter, { limit, offset });
1794
+ return users;
1795
+ } catch (error) {
1796
+ throw new Error(`Failed to fetch users: ${error.message}`);
1797
+ }
1798
+ },
1799
+
1800
+ user: async (_, { id }) => {
1801
+ try {
1802
+ const user = await userService.getUserById(id);
1803
+ if (!user) {
1804
+ throw new Error('User not found');
1805
+ }
1806
+ return user;
1807
+ } catch (error) {
1808
+ throw new Error(`Failed to fetch user: ${error.message}`);
1809
+ }
1810
+ }
1811
+ },
1812
+
1813
+ Mutation: {
1814
+ createUser: async (_, { input }) => {
1815
+ try {
1816
+ const user = await userService.createUser(input);
1817
+ return user;
1818
+ } catch (error) {
1819
+ throw new Error(`Failed to create user: ${error.message}`);
1820
+ }
1821
+ },
1822
+
1823
+ updateUser: async (_, { id, input }) => {
1824
+ try {
1825
+ const user = await userService.updateUser(id, input);
1826
+ if (!user) {
1827
+ throw new Error('User not found');
1828
+ }
1829
+ return user;
1830
+ } catch (error) {
1831
+ throw new Error(`Failed to update user: ${error.message}`);
1832
+ }
1833
+ },
1834
+
1835
+ deleteUser: async (_, { id }) => {
1836
+ try {
1837
+ await userService.deleteUser(id);
1838
+ return true;
1839
+ } catch (error) {
1840
+ throw new Error(`Failed to delete user: ${error.message}`);
1841
+ }
1842
+ }
1843
+ }
1844
+ };
1845
+
1846
+ module.exports = { typeDefs, resolvers };
1847
+ ```
1848
+
1849
+ ### WebSocket Integration
1850
+
1851
+ ```javascript
1852
+ // src/websocket/socketHandler.js
1853
+ const { Server } = require('ws');
1854
+ const jwt = require('jsonwebtoken');
1855
+ const UserService = require('../services/userService');
1856
+
1857
+ class SocketHandler {
1858
+ constructor(server, options = {}) {
1859
+ this.wss = new Server({
1860
+ server,
1861
+ path: options.path || '/ws',
1862
+ verifyClient: this.verifyClient.bind(this)
1863
+ });
1864
+
1865
+ this.clients = new Map(); // userId -> WebSocket connection
1866
+ this.rooms = new Map(); // room -> Set of userIds
1867
+
1868
+ this.setupEventHandlers();
1869
+ }
1870
+
1871
+ // Client verification
1872
+ async verifyClient(info) {
1873
+ try {
1874
+ const token = this.extractToken(info.req);
1875
+ if (!token) {
1876
+ return false;
1877
+ }
1878
+
1879
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
1880
+ const user = await new UserService(database).getUserById(decoded.sub);
1881
+
1882
+ if (!user) {
1883
+ return false;
1884
+ }
1885
+
1886
+ info.req.user = user;
1887
+ return true;
1888
+ } catch (error) {
1889
+ return false;
1890
+ }
1891
+ }
1892
+
1893
+ // Extract JWT token from request
1894
+ extractToken(req) {
1895
+ const authHeader = req.headers.authorization;
1896
+ if (authHeader && authHeader.startsWith('Bearer ')) {
1897
+ return authHeader.substring(7);
1898
+ }
1899
+
1900
+ // Also check query parameters for fallback
1901
+ const urlParams = new URL(req.url, `http://${req.headers.host}`).searchParams;
1902
+ return urlParams.get('token');
1903
+ }
1904
+
1905
+ // Setup WebSocket event handlers
1906
+ setupEventHandlers() {
1907
+ this.wss.on('connection', (ws, req) => {
1908
+ this.handleConnection(ws, req);
1909
+ });
1910
+
1911
+ this.wss.on('error', (error) => {
1912
+ console.error('WebSocket error:', error);
1913
+ });
1914
+ }
1915
+
1916
+ // Handle new connection
1917
+ handleConnection(ws, req) {
1918
+ const user = req.user;
1919
+
1920
+ // Store client connection
1921
+ this.clients.set(user.id, {
1922
+ ws,
1923
+ user,
1924
+ joinedAt: new Date(),
1925
+ lastActivity: new Date()
1926
+ });
1927
+
1928
+ // Send welcome message
1929
+ this.sendToUser(user.id, {
1930
+ type: 'connection',
1931
+ message: 'Connected successfully',
1932
+ timestamp: new Date().toISOString()
1933
+ });
1934
+
1935
+ // Setup message handler
1936
+ ws.on('message', (message) => {
1937
+ this.handleMessage(user.id, message);
1938
+ });
1939
+
1940
+ // Setup close handler
1941
+ ws.on('close', () => {
1942
+ this.handleDisconnection(user.id);
1943
+ });
1944
+
1945
+ // Setup error handler
1946
+ ws.on('error', (error) => {
1947
+ console.error(`WebSocket error for user ${user.id}:`, error);
1948
+ });
1949
+
1950
+ // Update last activity
1951
+ ws.on('pong', () => {
1952
+ const client = this.clients.get(user.id);
1953
+ if (client) {
1954
+ client.lastActivity = new Date();
1955
+ }
1956
+ });
1957
+
1958
+ console.log(`User ${user.id} connected via WebSocket`);
1959
+ }
1960
+
1961
+ // Handle incoming messages
1962
+ async handleMessage(userId, message) {
1963
+ try {
1964
+ const client = this.clients.get(userId);
1965
+ if (!client) return;
1966
+
1967
+ client.lastActivity = new Date();
1968
+
1969
+ const data = JSON.parse(message);
1970
+
1971
+ switch (data.type) {
1972
+ case 'join_room':
1973
+ await this.handleJoinRoom(userId, data.room);
1974
+ break;
1975
+
1976
+ case 'leave_room':
1977
+ await this.handleLeaveRoom(userId, data.room);
1978
+ break;
1979
+
1980
+ case 'send_message':
1981
+ await this.handleSendMessage(userId, data);
1982
+ break;
1983
+
1984
+ case 'typing':
1985
+ await this.handleTyping(userId, data);
1986
+ break;
1987
+
1988
+ default:
1989
+ this.sendToUser(userId, {
1990
+ type: 'error',
1991
+ message: 'Unknown message type',
1992
+ timestamp: new Date().toISOString()
1993
+ });
1994
+ }
1995
+ } catch (error) {
1996
+ console.error(`Error handling message from user ${userId}:`, error);
1997
+ this.sendToUser(userId, {
1998
+ type: 'error',
1999
+ message: 'Failed to process message',
2000
+ timestamp: new Date().toISOString()
2001
+ });
2002
+ }
2003
+ }
2004
+
2005
+ // Handle room joining
2006
+ async handleJoinRoom(userId, roomName) {
2007
+ const room = this.rooms.get(roomName) || new Set();
2008
+ room.add(userId);
2009
+ this.rooms.set(roomName, room);
2010
+
2011
+ // Notify room members
2012
+ this.broadcastToRoom(roomName, {
2013
+ type: 'user_joined',
2014
+ userId,
2015
+ room: roomName,
2016
+ timestamp: new Date().toISOString()
2017
+ }, userId);
2018
+
2019
+ // Send confirmation to user
2020
+ this.sendToUser(userId, {
2021
+ type: 'joined_room',
2022
+ room: roomName,
2023
+ timestamp: new Date().toISOString()
2024
+ });
2025
+ }
2026
+
2027
+ // Handle room leaving
2028
+ async handleLeaveRoom(userId, roomName) {
2029
+ const room = this.rooms.get(roomName);
2030
+ if (room) {
2031
+ room.delete(userId);
2032
+
2033
+ if (room.size === 0) {
2034
+ this.rooms.delete(roomName);
2035
+ }
2036
+
2037
+ // Notify room members
2038
+ this.broadcastToRoom(roomName, {
2039
+ type: 'user_left',
2040
+ userId,
2041
+ room: roomName,
2042
+ timestamp: new Date().toISOString()
2043
+ }, userId);
2044
+ }
2045
+
2046
+ // Send confirmation to user
2047
+ this.sendToUser(userId, {
2048
+ type: 'left_room',
2049
+ room: roomName,
2050
+ timestamp: new Date().toISOString()
2051
+ });
2052
+ }
2053
+
2054
+ // Handle message sending
2055
+ async handleSendMessage(userId, data) {
2056
+ const { room, message, type = 'text' } = data;
2057
+
2058
+ // Save message to database (implement this)
2059
+ const savedMessage = await this.saveMessage({
2060
+ userId,
2061
+ room,
2062
+ message,
2063
+ type,
2064
+ timestamp: new Date()
2065
+ });
2066
+
2067
+ // Broadcast to room members
2068
+ this.broadcastToRoom(room, {
2069
+ type: 'new_message',
2070
+ message: savedMessage,
2071
+ timestamp: new Date().toISOString()
2072
+ });
2073
+ }
2074
+
2075
+ // Handle typing indicators
2076
+ async handleTyping(userId, data) {
2077
+ const { room, isTyping } = data;
2078
+
2079
+ this.broadcastToRoom(room, {
2080
+ type: 'typing_indicator',
2081
+ userId,
2082
+ isTyping,
2083
+ timestamp: new Date().toISOString()
2084
+ }, userId);
2085
+ }
2086
+
2087
+ // Send message to specific user
2088
+ sendToUser(userId, message) {
2089
+ const client = this.clients.get(userId);
2090
+ if (client && client.ws.readyState === client.ws.OPEN) {
2091
+ client.ws.send(JSON.stringify(message));
2092
+ }
2093
+ }
2094
+
2095
+ // Broadcast message to room
2096
+ broadcastToRoom(roomName, message, excludeUserId = null) {
2097
+ const room = this.rooms.get(roomName);
2098
+ if (!room) return;
2099
+
2100
+ room.forEach(userId => {
2101
+ if (userId !== excludeUserId) {
2102
+ this.sendToUser(userId, message);
2103
+ }
2104
+ });
2105
+ }
2106
+
2107
+ // Handle client disconnection
2108
+ handleDisconnection(userId) {
2109
+ // Remove from all rooms
2110
+ for (const [roomName, room] of this.rooms.entries()) {
2111
+ room.delete(userId);
2112
+
2113
+ if (room.size === 0) {
2114
+ this.rooms.delete(roomName);
2115
+ } else {
2116
+ // Notify remaining room members
2117
+ this.broadcastToRoom(roomName, {
2118
+ type: 'user_disconnected',
2119
+ userId,
2120
+ timestamp: new Date().toISOString()
2121
+ });
2122
+ }
2123
+ }
2124
+
2125
+ // Remove client
2126
+ this.clients.delete(userId);
2127
+ console.log(`User ${userId} disconnected`);
2128
+ }
2129
+
2130
+ // Get connection statistics
2131
+ getStats() {
2132
+ return {
2133
+ connectedClients: this.clients.size,
2134
+ activeRooms: this.rooms.size,
2135
+ rooms: Array.from(this.rooms.entries()).map(([name, users]) => ({
2136
+ name,
2137
+ userCount: users.size
2138
+ }))
2139
+ };
2140
+ }
2141
+
2142
+ // Cleanup inactive connections
2143
+ cleanup() {
2144
+ const now = new Date();
2145
+ const timeoutMs = 5 * 60 * 1000; // 5 minutes
2146
+
2147
+ for (const [userId, client] of this.clients.entries()) {
2148
+ if (now - client.lastActivity > timeoutMs) {
2149
+ client.ws.terminate();
2150
+ this.handleDisconnection(userId);
2151
+ }
2152
+ }
2153
+ }
2154
+
2155
+ // Save message to database (implement this)
2156
+ async saveMessage(messageData) {
2157
+ // This would save to your database
2158
+ return {
2159
+ id: crypto.randomUUID(),
2160
+ ...messageData
2161
+ };
2162
+ }
2163
+ }
2164
+
2165
+ module.exports = SocketHandler;
2166
+ ```
2167
+
2168
+ ## Modern Development Workflow
2169
+
2170
+ ### Configuration Management
2171
+
2172
+ ```javascript
2173
+ // src/config/index.js
2174
+ const path = require('path');
2175
+ require('dotenv').config();
2176
+
2177
+ class Config {
2178
+ constructor() {
2179
+ this.env = process.env.NODE_ENV || 'development';
2180
+ this.isDevelopment = this.env === 'development';
2181
+ this.isProduction = this.env === 'production';
2182
+ this.isTest = this.env === 'test';
2183
+
2184
+ this.loadConfiguration();
2185
+ }
2186
+
2187
+ loadConfiguration() {
2188
+ this.server = {
2189
+ host: process.env.HOST || '0.0.0.0',
2190
+ port: parseInt(process.env.PORT) || 3000,
2191
+ cors: {
2192
+ origins: this.parseArray(process.env.CORS_ORIGINS) || ['http://localhost:3000']
2193
+ }
2194
+ };
2195
+
2196
+ this.database = {
2197
+ url: process.env.DATABASE_URL || 'mongodb://localhost:27017/myapp',
2198
+ options: {
2199
+ useNewUrlParser: true,
2200
+ useUnifiedTopology: true,
2201
+ maxPoolSize: parseInt(process.env.DB_MAX_POOL_SIZE) || 10,
2202
+ serverSelectionTimeoutMS: parseInt(process.env.DB_TIMEOUT) || 5000,
2203
+ }
2204
+ };
2205
+
2206
+ this.redis = {
2207
+ url: process.env.REDIS_URL || 'redis://localhost:6379',
2208
+ options: {
2209
+ retryDelayOnFailover: 100,
2210
+ enableReadyCheck: false,
2211
+ maxRetriesPerRequest: null
2212
+ }
2213
+ };
2214
+
2215
+ this.auth = {
2216
+ jwtSecret: process.env.JWT_SECRET || this.generateSecret(),
2217
+ jwtExpiration: process.env.JWT_EXPIRATION || '24h',
2218
+ bcryptRounds: parseInt(process.env.BCRYPT_ROUNDS) || 12
2219
+ };
2220
+
2221
+ this.rateLimit = {
2222
+ max: parseInt(process.env.RATE_LIMIT_MAX) || 100,
2223
+ windowMs: parseInt(process.env.RATE_LIMIT_WINDOW) || 15 * 60 * 1000
2224
+ };
2225
+
2226
+ this.logging = {
2227
+ level: process.env.LOG_LEVEL || 'info',
2228
+ format: process.env.LOG_FORMAT || 'combined',
2229
+ file: process.env.LOG_FILE
2230
+ };
2231
+
2232
+ this.upload = {
2233
+ maxFileSize: parseInt(process.env.MAX_FILE_SIZE) || 5 * 1024 * 1024, // 5MB
2234
+ allowedTypes: this.parseArray(process.env.ALLOWED_FILE_TYPES) || ['image/jpeg', 'image/png'],
2235
+ destination: process.env.UPLOAD_DESTINATION || './uploads'
2236
+ };
2237
+
2238
+ this.email = {
2239
+ provider: process.env.EMAIL_PROVIDER || 'sendgrid',
2240
+ from: process.env.EMAIL_FROM,
2241
+ apiKey: process.env.EMAIL_API_KEY
2242
+ };
2243
+
2244
+ this.monitoring = {
2245
+ enableMetrics: process.env.ENABLE_METRICS === 'true',
2246
+ metricsPort: parseInt(process.env.METRICS_PORT) || 9090,
2247
+ healthCheckInterval: parseInt(process.env.HEALTH_CHECK_INTERVAL) || 30000
2248
+ };
2249
+ }
2250
+
2251
+ parseArray(value) {
2252
+ if (!value) return null;
2253
+ if (Array.isArray(value)) return value;
2254
+ return value.split(',').map(item => item.trim());
2255
+ }
2256
+
2257
+ generateSecret() {
2258
+ if (this.isProduction) {
2259
+ throw new Error('JWT_SECRET must be set in production');
2260
+ }
2261
+ return require('crypto').randomBytes(64).toString('hex');
2262
+ }
2263
+
2264
+ validate() {
2265
+ const errors = [];
2266
+
2267
+ if (this.isProduction && !process.env.JWT_SECRET) {
2268
+ errors.push('JWT_SECRET is required in production');
2269
+ }
2270
+
2271
+ if (!this.database.url) {
2272
+ errors.push('DATABASE_URL is required');
2273
+ }
2274
+
2275
+ if (errors.length > 0) {
2276
+ throw new Error(`Configuration validation failed: ${errors.join(', ')}`);
2277
+ }
2278
+ }
2279
+
2280
+ getDatabaseConfig() {
2281
+ return {
2282
+ url: this.database.url,
2283
+ options: this.database.options
2284
+ };
2285
+ }
2286
+
2287
+ getRedisConfig() {
2288
+ return {
2289
+ url: this.redis.url,
2290
+ options: this.redis.options
2291
+ };
2292
+ }
2293
+ }
2294
+
2295
+ // Create singleton instance
2296
+ const config = new Config();
2297
+
2298
+ // Validate configuration on startup
2299
+ config.validate();
2300
+
2301
+ module.exports = config;
2302
+ ```
2303
+
2304
+ ### Package.json with Modern Scripts
2305
+
2306
+ ```json
2307
+ {
2308
+ "name": "my-javascript-project",
2309
+ "version": "1.0.0",
2310
+ "description": "Modern Node.js application",
2311
+ "main": "src/app.js",
2312
+ "type": "commonjs",
2313
+ "scripts": {
2314
+ "start": "node src/app.js",
2315
+ "dev": "nodemon src/app.js",
2316
+ "dev:debug": "nodemon --inspect src/app.js",
2317
+ "test": "jest",
2318
+ "test:watch": "jest --watch",
2319
+ "test:coverage": "jest --coverage",
2320
+ "test:integration": "jest --testPathPattern=test/integration",
2321
+ "lint": "eslint src/ test/",
2322
+ "lint:fix": "eslint src/ test/ --fix",
2323
+ "format": "prettier --write src/ test/",
2324
+ "validate": "npm run lint && npm run test",
2325
+ "build": "echo 'No build step required for JavaScript'",
2326
+ "clean": "rm -rf node_modules package-lock.json",
2327
+ "fresh": "npm run clean && npm install",
2328
+ "security:audit": "npm audit",
2329
+ "security:fix": "npm audit fix",
2330
+ "db:migrate": "node scripts/migrate.js",
2331
+ "db:seed": "node scripts/seed.js",
2332
+ "logs": "tail -f logs/app.log",
2333
+ "docker:build": "docker build -t my-app .",
2334
+ "docker:run": "docker run -p 3000:3000 my-app"
2335
+ },
2336
+ "dependencies": {
2337
+ "express": "^4.21.0",
2338
+ "helmet": "^7.1.0",
2339
+ "cors": "^2.8.5",
2340
+ "compression": "^1.7.5",
2341
+ "morgan": "^1.10.0",
2342
+ "express-rate-limit": "^7.4.0",
2343
+ "express-validator": "^7.1.0",
2344
+ "bcrypt": "^5.1.1",
2345
+ "jsonwebtoken": "^9.0.2",
2346
+ "mongoose": "^8.8.1",
2347
+ "redis": "^4.7.0",
2348
+ "dotenv": "^16.4.5",
2349
+ "winston": "^3.15.0",
2350
+ "joi": "^17.13.3",
2351
+ "multer": "^1.4.5-lts.1",
2352
+ "nodemailer": "^6.9.8",
2353
+ "apollo-server-express": "^3.12.1",
2354
+ "graphql": "^16.9.0",
2355
+ "ws": "^8.18.0",
2356
+ "uuid": "^10.0.0"
2357
+ },
2358
+ "devDependencies": {
2359
+ "nodemon": "^3.1.4",
2360
+ "jest": "^30.0.4",
2361
+ "supertest": "^7.0.0",
2362
+ "eslint": "^9.0.0",
2363
+ "eslint-config-standard": "^17.1.0",
2364
+ "eslint-plugin-import": "^2.31.0",
2365
+ "eslint-plugin-node": "^11.1.0",
2366
+ "eslint-plugin-promise": "^7.1.0",
2367
+ "prettier": "^3.3.3",
2368
+ "@types/jest": "^30.0.4",
2369
+ "mongodb-memory-server": "^9.16.0"
2370
+ },
2371
+ "engines": {
2372
+ "node": ">=18.0.0",
2373
+ "npm": ">=8.0.0"
2374
+ },
2375
+ "keywords": [
2376
+ "nodejs",
2377
+ "express",
2378
+ "javascript",
2379
+ "api",
2380
+ "backend"
2381
+ ],
2382
+ "author": "Your Name",
2383
+ "license": "MIT"
2384
+ }
2385
+ ```
2386
+
2387
+ ---
89
2388
 
90
- - alfred-trust-validation (coverage verification)
91
- - alfred-code-reviewer (JavaScript-specific review)
92
- - alfred-debugger-pro (JavaScript debugging)
2389
+ **Created by**: MoAI Language Skill Factory
2390
+ **Last Updated**: 2025-11-06
2391
+ **Version**: 2.0.0
2392
+ **JavaScript Target**: Node.js 22.x + Modern ES2025 Features
93
2393
 
94
- ## Best Practices
95
- - Enable automatic validation by matching your linter with the language's official style guide.
96
- - Fix test/build pipelines with reproducible commands in CI.
2394
+ This skill provides comprehensive JavaScript development guidance with 2025 best practices, covering everything from legacy code maintenance to modern Node.js backend development and browser compatibility strategies.