groundswell 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/subtask-planning/prp-base-create.md +120 -0
- package/.claude/commands/subtask-planning/prp-base-execute.md +65 -0
- package/.claude/commands/task-breakdown.md +94 -0
- package/.claude/system_prompts/task-breakdown.md +1 -0
- package/CHANGELOG.md +188 -0
- package/PRD.md +543 -0
- package/README.md +99 -5
- package/examples/README.md +15 -1
- package/examples/examples/11-reparenting-workflows.ts +269 -0
- package/examples/index.ts +4 -0
- package/package-lock.json +2398 -0
- package/package.json +3 -1
- package/plan/001_d3bb02af4886/TEST_RESULTS.md +259 -0
- package/plan/001_d3bb02af4886/bug_fix_tasks.json +484 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S1/PRP.md +488 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S2/PRP.md +581 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S3/PRP.md +687 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S1/PRP.md +492 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/PRP.md +932 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/concurrent_error_testing_patterns.md +1109 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/vitest_concurrent_testing.md +802 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/workflow_engine_test_references.md +603 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md +564 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md +518 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/PRP.md +1252 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/PRP.md +364 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/CODEBASE_INVENTORY.md +114 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/DECORATOR_DOCUMENTATION_PATTERNS.md +205 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/PRD_LOCATION_ANALYSIS.md +199 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/ULTRATHINK_PRP_PLAN.md +134 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/PRP.md +495 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/research/console_error_inventory.md +435 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S2/PRP.md +506 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S3/PRP.md +612 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/PRP.md +558 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/research/external_research.md +788 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S2/PRP.md +460 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S3/PRP.md +454 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/PRP.md +520 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/RECOMMENDATION.md +417 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/external_workflow_engines_research.md +760 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/security_implications_analysis.md +245 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S2/PRP.md +792 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/PRP.md +535 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/TEST_EXECUTION_REPORT.md +190 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/PRP.md +654 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/TEST_FIX_REPORT.md +227 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/KEY_FINDINGS.md +345 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/QUICK_REFERENCE.md +193 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/test_maintenance_research.md +1323 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/BREAKING_CHANGES_AUDIT.md +1011 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/PRP.md +927 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S2/PRP.md +505 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/architecture/logger_child_signature_analysis.md +401 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/child_implementation_research.md +142 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/test_patterns_research.md +112 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/vitest_patterns_research.md +159 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/PRP.md +549 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/VERIFICATION_REPORT.md +368 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/edge_case_analysis.md +172 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/usage_inventory.md +175 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md +696 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S4/PRP.md +860 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md +1066 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01-testing-aggregated-errors.md +1103 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md +789 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02-error-merge-strategy-testing-guide.md +1098 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02_aggregate_error_patterns.md +1037 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03-promise-allsettled-testing-patterns.md +916 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03_error_merging_strategies.md +1045 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/04_github_stackoverflow_examples.md +890 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/05_comprehensive_summary.md +822 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/INDEX.md +668 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md +706 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/README.md +265 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/RESEARCH_REPORT.md +655 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S4/research/vitest_testing_patterns.md +1103 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T3S2/PRP.md +426 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/PRP.md +506 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/QUICK_REFERENCE.md +114 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/RESEARCH_SUMMARY.md +316 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/vitest_observer_error_logging_best_practices.md +754 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S3/PRP.md +612 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/PRP.md +719 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/README.md +215 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/analysis.md +765 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S3/PRP.md +718 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/DECISION.md +149 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/PRP.md +470 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/ULTRATHINK_PLAN.md +332 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/codebase_workflow_name_analysis.md +167 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/external_best_practices.md +265 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/validation_patterns.md +273 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S1/workflow_engine_ancestry_api_research.md +760 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S3-PRP.md +434 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S1/PRP.md +717 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/PRP.md +472 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/VALIDATION_REPORT.md +125 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/research/ULTRATHINK_PRP_PLAN.md +301 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/error-logging-best-practices.md +1170 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/research_typescript_partial_and_overloads.md +940 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-quick-reference.md +151 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-research.md +650 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md +259 -0
- package/plan/001_d3bb02af4886/bugfix/P1M1T1S1/PRP.md +457 -0
- package/plan/001_d3bb02af4886/bugfix/RESEARCH_SUMMARY.md +346 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md +311 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md +1565 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md +288 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/promise_all_analysis.md +741 -0
- package/plan/001_d3bb02af4886/docs/PRP/P1M1T1S4-functional-workflow-error-state-capture-test.md +652 -0
- package/plan/001_d3bb02af4886/docs/PRP/PRP.md +527 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S1-PRP.md +415 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S2-PRP.md +378 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S4-PRP.md +713 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M2T1S4-PRP.md +370 -0
- package/plan/001_d3bb02af4886/docs/PRP_P1M3T1S3.md +499 -0
- package/plan/001_d3bb02af4886/docs/TEST_RESULTS.md +230 -0
- package/plan/001_d3bb02af4886/docs/bugfix/ANALYSIS_PRD_VS_IMPLEMENTATION.md +1134 -0
- package/plan/001_d3bb02af4886/docs/bugfix/GAP_ANALYSIS_SUMMARY.md +179 -0
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/PRP.md +629 -0
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/validation-report.md +214 -0
- package/plan/001_d3bb02af4886/docs/bugfix/PRP_P1M4T2S3.md +629 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_PRP.md +529 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_QUICK_REFERENCE.md +142 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_README.md +304 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_TEST_RESULTS.md +558 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_VALIDATION_SUMMARY.md +256 -0
- package/plan/001_d3bb02af4886/docs/bugfix/system_context.md +346 -0
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/bug_analysis.md +415 -0
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/implementation_patterns.md +489 -0
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/system_context.md +218 -0
- package/plan/001_d3bb02af4886/docs/bugfix_INITIATION_SUMMARY.md +380 -0
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_PATTERNS.md +1923 -0
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_QUICK_REF.md +319 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/codebase-context.md +115 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/cycle-detection-algorithms.md +134 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/test-patterns.md +153 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/workflow-class.md +132 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_BEST_PRACTICES.md +716 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_QUICK_REF.md +186 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/GROUNDSWELL_DECORATOR_EXAMPLES.md +604 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/INDEX.md +213 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/codebase_structure.md +30 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/existing_test_pattern.md +56 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/getRootObservers_implementation.md +53 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/test_conventions.md +49 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/PRP.md +958 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/QUICK_REFERENCE.md +339 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/README.md +305 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/SUMMARY.md +433 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md +1574 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/test-pattern-examples.md +1014 -0
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_QUICK_REF.md +376 -0
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_RESEARCH.md +1507 -0
- package/plan/001_d3bb02af4886/docs/research/bugfix_typescript_patterns.md +949 -0
- package/plan/001_d3bb02af4886/docs/research/error-testing-research.md +619 -0
- package/plan/001_d3bb02af4886/docs/research/error_handling_patterns.md +723 -0
- package/plan/{research → 001_d3bb02af4886/docs/research/general}/introspection-security-guide.md +56 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/PRP_TEMPLATE.md +460 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/QUICK_REFERENCE.md +324 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/README.md +175 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/RESEARCH_REPORT.md +499 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/SUMMARY.md +163 -0
- package/plan/bugfix/BUG_FIX_SUMMARY.md +961 -0
- package/src/__tests__/adversarial/attachChild-performance.test.ts +216 -0
- package/src/__tests__/adversarial/circular-reference.test.ts +101 -0
- package/src/__tests__/adversarial/complex-circular-reference.test.ts +139 -0
- package/src/__tests__/adversarial/concurrent-task-failures.test.ts +571 -0
- package/src/__tests__/adversarial/deep-analysis.test.ts +729 -0
- package/src/__tests__/adversarial/deep-hierarchy-stress.test.ts +213 -0
- package/src/__tests__/adversarial/e2e-prd-validation.test.ts +448 -0
- package/src/__tests__/adversarial/edge-case.test.ts +703 -0
- package/src/__tests__/adversarial/error-merge-strategy.test.ts +760 -0
- package/src/__tests__/adversarial/incremental-performance.test.ts +140 -0
- package/src/__tests__/adversarial/node-map-update-benchmarks.test.ts +457 -0
- package/src/__tests__/adversarial/observer-propagation.test.ts +487 -0
- package/src/__tests__/adversarial/parent-validation.test.ts +143 -0
- package/src/__tests__/adversarial/prd-12-2-compliance.test.ts +611 -0
- package/src/__tests__/adversarial/prd-compliance.test.ts +731 -0
- package/src/__tests__/compatibility/backward-compatibility.test.ts +1572 -0
- package/src/__tests__/helpers/index.ts +18 -0
- package/src/__tests__/helpers/tree-verification.ts +257 -0
- package/src/__tests__/integration/bidirectional-consistency.test.ts +847 -0
- package/src/__tests__/integration/observer-logging.test.ts +643 -0
- package/src/__tests__/integration/tree-mirroring.test.ts +37 -0
- package/src/__tests__/integration/workflow-reparenting.test.ts +303 -0
- package/src/__tests__/unit/context.test.ts +79 -0
- package/src/__tests__/unit/logger.test.ts +293 -0
- package/src/__tests__/unit/observable.test.ts +321 -0
- package/src/__tests__/unit/tree-debugger-incremental.test.ts +170 -0
- package/src/__tests__/unit/utils/workflow-error-utils.test.ts +209 -0
- package/src/__tests__/unit/workflow-detachChild.test.ts +100 -0
- package/src/__tests__/unit/workflow-emitEvent-childDetached.test.ts +153 -0
- package/src/__tests__/unit/workflow-isDescendantOf.test.ts +180 -0
- package/src/__tests__/unit/workflow.test.ts +277 -1
- package/src/core/agent.ts +21 -1
- package/src/core/logger.ts +27 -2
- package/src/core/workflow-context.ts +6 -4
- package/src/core/workflow.ts +252 -14
- package/src/debugger/tree-debugger.ts +52 -7
- package/src/decorators/task.ts +65 -2
- package/src/index.ts +4 -2
- package/src/types/decorators.ts +8 -1
- package/src/types/events.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/observable.ts +32 -3
- package/src/utils/workflow-error-utils.ts +56 -0
- package/tsconfig.json +1 -1
- package/llms_full.txt +0 -5890
- package/tasks.json +0 -0
- /package/plan/{backlog.json → 001_d3bb02af4886/backlog.json} +0 -0
- /package/plan/{P1P2/PRP.md → 001_d3bb02af4886/docs/PRP/P1P2-PRP.md} +0 -0
- /package/plan/{P3P4/PRP.md → 001_d3bb02af4886/docs/PRP/P3P4-PRP.md} +0 -0
- /package/plan/{P4P5/PRP.md → 001_d3bb02af4886/docs/PRP/P4P5-PRP.md} +0 -0
- /package/plan/{architecture → 001_d3bb02af4886/docs/architecture}/external_deps.md +0 -0
- /package/plan/{architecture → 001_d3bb02af4886/docs/architecture}/system_context.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_BEST_PRACTICES.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_CODE_PATTERNS.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_INTEGRATION_GUIDE.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_RESEARCH_INDEX.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/REFLECTION_INDEX.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/REFLECTION_RESEARCH_REPORT.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/RESEARCH_SUMMARY.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/anthropic-sdk.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/async-local-storage.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-code-patterns.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-decision-matrix.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-implementation-guide.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-integration-guide.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-patterns.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-quick-reference.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/zod-schema.md +0 -0
- /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/caching-lru.md +0 -0
- /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/introspection-tools.md +0 -0
- /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/reflection-patterns.md +0 -0
- /package/plan/{P4P5/research → 001_d3bb02af4886/docs/research/P4P5}/RESEARCH_SUMMARY.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/INTROSPECTION_RESEARCH_SUMMARY.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/README-INTROSPECTION.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/agent-introspection-patterns.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/introspection-tool-examples.md +0 -0
- /package/{PRPs/PRDs/001-hierarchical-workflow-engine.md → plan/001_d3bb02af4886/prd_snapshot.md} +0 -0
|
@@ -0,0 +1,1045 @@
|
|
|
1
|
+
# Error Merging Strategies from Popular Libraries
|
|
2
|
+
|
|
3
|
+
**Research Date:** 2026-01-12
|
|
4
|
+
**Status:** Comprehensive Research Report
|
|
5
|
+
**Target:** P1M2T2S2 - Error Aggregation Implementation
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Executive Summary
|
|
10
|
+
|
|
11
|
+
This document provides comprehensive research on error merging strategies from popular JavaScript/TypeScript libraries and frameworks. It covers patterns from React, Angular, Node.js ecosystem, and production-grade error handling libraries.
|
|
12
|
+
|
|
13
|
+
**Key Finding:** Production libraries use diverse error merging strategies, but common patterns emerge: hierarchical error aggregation, context preservation, error categorization, and statistics generation. The most sophisticated implementations combine these approaches for comprehensive error handling.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
|
|
19
|
+
1. [React Error Handling Patterns](#1-react-error-handling-patterns)
|
|
20
|
+
2. [Angular Error Handling Patterns](#2-angular-error-handling-patterns)
|
|
21
|
+
3. [Node.js Ecosystem Patterns](#3-nodejs-ecosystem-patterns)
|
|
22
|
+
4. [Popular Error Handling Libraries](#4-popular-error-handling-libraries)
|
|
23
|
+
5. [Cross-Cutting Best Practices](#5-cross-cutting-best-practices)
|
|
24
|
+
6. [Implementation Recommendations](#6-implementation-recommendations)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. React Error Handling Patterns
|
|
29
|
+
|
|
30
|
+
### 1.1 Error Boundaries
|
|
31
|
+
|
|
32
|
+
React Error Boundaries catch errors in component trees and can aggregate multiple errors:
|
|
33
|
+
|
|
34
|
+
**Pattern:**
|
|
35
|
+
```typescript
|
|
36
|
+
class ErrorBoundary extends React.Component<
|
|
37
|
+
{ children: React.ReactNode },
|
|
38
|
+
{ hasError: boolean; errors: Error[] }
|
|
39
|
+
> {
|
|
40
|
+
constructor(props: { children: React.ReactNode }) {
|
|
41
|
+
super(props);
|
|
42
|
+
this.state = { hasError: false, errors: [] };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static getDerivedStateFromError(error: Error) {
|
|
46
|
+
return { hasError: true };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
|
50
|
+
this.setState(prevState => ({
|
|
51
|
+
errors: [...prevState.errors, error],
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
// Log to error reporting service
|
|
55
|
+
logErrorToService(error, errorInfo);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
render() {
|
|
59
|
+
if (this.state.hasError) {
|
|
60
|
+
return <h1>Something went wrong.</h1>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return this.props.children;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Key Insights:**
|
|
69
|
+
- Accumulates errors in state
|
|
70
|
+
- Preserves component stack information
|
|
71
|
+
- Delegates actual aggregation to error reporting services
|
|
72
|
+
|
|
73
|
+
### 1.2 React Concurrent Error Handling
|
|
74
|
+
|
|
75
|
+
React 18+ with concurrent features uses suspense and error boundaries together:
|
|
76
|
+
|
|
77
|
+
**Pattern:**
|
|
78
|
+
```typescript
|
|
79
|
+
function App() {
|
|
80
|
+
return (
|
|
81
|
+
<ErrorBoundary fallback={<ErrorPage />}>
|
|
82
|
+
<Suspense fallback={<Loading />}>
|
|
83
|
+
<AsyncComponent1 />
|
|
84
|
+
<AsyncComponent2 />
|
|
85
|
+
<AsyncComponent3 />
|
|
86
|
+
</Suspense>
|
|
87
|
+
</ErrorBoundary>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Multiple error boundaries can catch errors from different sections
|
|
92
|
+
function SectionWithErrorHandling() {
|
|
93
|
+
return (
|
|
94
|
+
<>
|
|
95
|
+
<ErrorBoundary fallback={<Section1Error />}>
|
|
96
|
+
<Section1 />
|
|
97
|
+
</ErrorBoundary>
|
|
98
|
+
<ErrorBoundary fallback={<Section2Error />}>
|
|
99
|
+
<Section2 />
|
|
100
|
+
</ErrorBoundary>
|
|
101
|
+
</>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Key Insights:**
|
|
107
|
+
- Nested error boundaries provide isolation
|
|
108
|
+
- Each boundary handles its own errors
|
|
109
|
+
- Prevents cascading failures
|
|
110
|
+
|
|
111
|
+
### 1.3 React Error Reporting Libraries
|
|
112
|
+
|
|
113
|
+
Popular React error libraries like Sentry React SDK aggregate errors:
|
|
114
|
+
|
|
115
|
+
**Sentry Pattern (simplified):**
|
|
116
|
+
```typescript
|
|
117
|
+
import * as Sentry from '@sentry/react';
|
|
118
|
+
|
|
119
|
+
Sentry.init({
|
|
120
|
+
dsn: 'your-dsn',
|
|
121
|
+
beforeSend(event, hint) {
|
|
122
|
+
// Modify or filter events before sending
|
|
123
|
+
if (event.exception) {
|
|
124
|
+
// Aggregate similar errors
|
|
125
|
+
event.exception.values = aggregateExceptions(event.exception.values);
|
|
126
|
+
}
|
|
127
|
+
return event;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
function App() {
|
|
132
|
+
return (
|
|
133
|
+
<Sentry.ErrorBoundary fallback={<ErrorFallback />}>
|
|
134
|
+
<YourApp />
|
|
135
|
+
</Sentry.ErrorBoundary>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function aggregateExceptions(exceptions: Exception[]) {
|
|
140
|
+
// Group by error type and message
|
|
141
|
+
const grouped = new Map<string, Exception[]>();
|
|
142
|
+
|
|
143
|
+
for (const exc of exceptions) {
|
|
144
|
+
const key = `${exc.type}:${exc.value}`;
|
|
145
|
+
if (!grouped.has(key)) {
|
|
146
|
+
grouped.set(key, []);
|
|
147
|
+
}
|
|
148
|
+
grouped.get(key)!.push(exc);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Return aggregated exceptions
|
|
152
|
+
return Array.from(grouped.values()).map(group => {
|
|
153
|
+
return {
|
|
154
|
+
type: group[0].type,
|
|
155
|
+
value: `${group.length}x ${group[0].value}`,
|
|
156
|
+
stacktrace: group[0].stacktrace,
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Key Insights:**
|
|
163
|
+
- Client-side aggregation before sending to server
|
|
164
|
+
- Groups similar errors to reduce noise
|
|
165
|
+
- Preserves stack traces from first occurrence
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 2. Angular Error Handling Patterns
|
|
170
|
+
|
|
171
|
+
### 2.1 Global Error Handler
|
|
172
|
+
|
|
173
|
+
Angular provides a global error handler that can be customized:
|
|
174
|
+
|
|
175
|
+
**Pattern:**
|
|
176
|
+
```typescript
|
|
177
|
+
import { ErrorHandler, Injectable } from '@angular/core';
|
|
178
|
+
|
|
179
|
+
interface AggregatedError {
|
|
180
|
+
message: string;
|
|
181
|
+
errors: Error[];
|
|
182
|
+
timestamp: number;
|
|
183
|
+
context?: {
|
|
184
|
+
component?: string;
|
|
185
|
+
route?: string;
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@Injectable()
|
|
190
|
+
export class CustomErrorHandler implements ErrorHandler {
|
|
191
|
+
private errors: Error[] = [];
|
|
192
|
+
private aggregationWindowMs = 5000;
|
|
193
|
+
private lastAggregation = 0;
|
|
194
|
+
|
|
195
|
+
handleError(error: any) {
|
|
196
|
+
this.errors.push(error);
|
|
197
|
+
|
|
198
|
+
// Check if we should aggregate and report
|
|
199
|
+
const now = Date.now();
|
|
200
|
+
if (now - this.lastAggregation > this.aggregationWindowMs) {
|
|
201
|
+
this.reportAggregatedErrors();
|
|
202
|
+
this.lastAggregation = now;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private reportAggregatedErrors() {
|
|
207
|
+
if (this.errors.length === 0) return;
|
|
208
|
+
|
|
209
|
+
const aggregated: AggregatedError = {
|
|
210
|
+
message: `${this.errors.length} error(s) occurred`,
|
|
211
|
+
errors: this.errors,
|
|
212
|
+
timestamp: Date.now(),
|
|
213
|
+
context: this.captureContext(),
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Send to error reporting service
|
|
217
|
+
this.sendToErrorService(aggregated);
|
|
218
|
+
|
|
219
|
+
// Clear accumulated errors
|
|
220
|
+
this.errors = [];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private captureContext() {
|
|
224
|
+
// Capture component, route, etc.
|
|
225
|
+
return {
|
|
226
|
+
component: this.getCurrentComponent(),
|
|
227
|
+
route: this.getCurrentRoute(),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private sendToErrorService(error: AggregatedError) {
|
|
232
|
+
// Implementation depends on error service
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private getCurrentComponent(): string {
|
|
236
|
+
// Implementation
|
|
237
|
+
return '';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private getCurrentRoute(): string {
|
|
241
|
+
// Implementation
|
|
242
|
+
return '';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Key Insights:**
|
|
248
|
+
- Time-based aggregation window
|
|
249
|
+
- Captures Angular-specific context (component, route)
|
|
250
|
+
- Batches errors to reduce reporting overhead
|
|
251
|
+
|
|
252
|
+
### 2.2 HTTP Error Interceptor
|
|
253
|
+
|
|
254
|
+
Angular HTTP interceptors can aggregate API errors:
|
|
255
|
+
|
|
256
|
+
**Pattern:**
|
|
257
|
+
```typescript
|
|
258
|
+
import { Injectable } from '@angular/core';
|
|
259
|
+
import {
|
|
260
|
+
HttpEvent,
|
|
261
|
+
HttpInterceptor,
|
|
262
|
+
HttpHandler,
|
|
263
|
+
HttpRequest,
|
|
264
|
+
HttpErrorResponse,
|
|
265
|
+
} from '@angular/common/http';
|
|
266
|
+
import { Observable, throwError, from } from 'rxjs';
|
|
267
|
+
import { catchError, mergeMap } from 'rxjs/operators';
|
|
268
|
+
|
|
269
|
+
interface ApiError {
|
|
270
|
+
url: string;
|
|
271
|
+
status: number;
|
|
272
|
+
message: string;
|
|
273
|
+
timestamp: number;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@Injectable()
|
|
277
|
+
export class HttpErrorInterceptor implements HttpInterceptor {
|
|
278
|
+
private apiErrors: ApiError[] = [];
|
|
279
|
+
private aggregationThreshold = 5;
|
|
280
|
+
|
|
281
|
+
intercept(
|
|
282
|
+
req: HttpRequest<unknown>,
|
|
283
|
+
next: HttpHandler
|
|
284
|
+
): Observable<HttpEvent<unknown>> {
|
|
285
|
+
return next.handle(req).pipe(
|
|
286
|
+
catchError((error: HttpErrorResponse) => {
|
|
287
|
+
const apiError: ApiError = {
|
|
288
|
+
url: req.url,
|
|
289
|
+
status: error.status,
|
|
290
|
+
message: error.message,
|
|
291
|
+
timestamp: Date.now(),
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
this.apiErrors.push(apiError);
|
|
295
|
+
|
|
296
|
+
// Check if we've reached threshold
|
|
297
|
+
if (this.apiErrors.length >= this.aggregationThreshold) {
|
|
298
|
+
this.reportAggregatedErrors();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return throwError(() => error);
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private reportAggregatedErrors() {
|
|
307
|
+
if (this.apiErrors.length === 0) return;
|
|
308
|
+
|
|
309
|
+
// Group by status code
|
|
310
|
+
const byStatus = new Map<number, ApiError[]>();
|
|
311
|
+
for (const error of this.apiErrors) {
|
|
312
|
+
if (!byStatus.has(error.status)) {
|
|
313
|
+
byStatus.set(error.status, []);
|
|
314
|
+
}
|
|
315
|
+
byStatus.get(error.status)!.push(error);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Create aggregated error
|
|
319
|
+
const aggregated = {
|
|
320
|
+
message: `${this.apiErrors.length} API error(s) occurred`,
|
|
321
|
+
byStatus: Object.fromEntries(byStatus),
|
|
322
|
+
timestamp: Date.now(),
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// Send to error service
|
|
326
|
+
this.sendToErrorService(aggregated);
|
|
327
|
+
|
|
328
|
+
// Clear accumulated errors
|
|
329
|
+
this.apiErrors = [];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private sendToErrorService(error: unknown) {
|
|
333
|
+
// Implementation
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Key Insights:**
|
|
339
|
+
- Groups errors by HTTP status code
|
|
340
|
+
- Threshold-based aggregation
|
|
341
|
+
- Preserves request context (URL, status)
|
|
342
|
+
|
|
343
|
+
### 2.3 RxJS Error Handling
|
|
344
|
+
|
|
345
|
+
Angular uses RxJS extensively, which has its own error aggregation patterns:
|
|
346
|
+
|
|
347
|
+
**Pattern:**
|
|
348
|
+
```typescript
|
|
349
|
+
import { Observable, forkJoin, of } from 'rxjs';
|
|
350
|
+
import { catchError } from 'rxjs/operators';
|
|
351
|
+
|
|
352
|
+
interface Result<T, E = Error> {
|
|
353
|
+
success: boolean;
|
|
354
|
+
value?: T;
|
|
355
|
+
error?: E;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Execute multiple observables and collect all errors
|
|
359
|
+
function executeAllWithErrors<T>(
|
|
360
|
+
observables: Observable<T>[]
|
|
361
|
+
): Observable<Result<T>[]> {
|
|
362
|
+
return forkJoin(
|
|
363
|
+
observables.map(obs =>
|
|
364
|
+
obs.pipe(
|
|
365
|
+
catchError(error => of({
|
|
366
|
+
success: false,
|
|
367
|
+
error,
|
|
368
|
+
}))
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
).pipe(
|
|
372
|
+
catchError(errors => of([{
|
|
373
|
+
success: false,
|
|
374
|
+
error: errors,
|
|
375
|
+
}]))
|
|
376
|
+
) as Observable<Result<T>[]>;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Key Insights:**
|
|
381
|
+
- RxJS operators enable error transformation
|
|
382
|
+
- forkJoin with catchError allows all observables to complete
|
|
383
|
+
- Returns structured results with success/failure indicators
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 3. Node.js Ecosystem Patterns
|
|
388
|
+
|
|
389
|
+
### 3.1 Express Error Handling
|
|
390
|
+
|
|
391
|
+
Express.js has built-in error handling middleware:
|
|
392
|
+
|
|
393
|
+
**Pattern:**
|
|
394
|
+
```typescript
|
|
395
|
+
import express, { Request, Response, NextFunction } from 'express';
|
|
396
|
+
|
|
397
|
+
interface AggregatedError {
|
|
398
|
+
message: string;
|
|
399
|
+
errors: Array<{
|
|
400
|
+
message: string;
|
|
401
|
+
stack?: string;
|
|
402
|
+
timestamp: number;
|
|
403
|
+
route?: string;
|
|
404
|
+
method?: string;
|
|
405
|
+
}>;
|
|
406
|
+
totalErrors: number;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const app = express();
|
|
410
|
+
|
|
411
|
+
// Error collection middleware
|
|
412
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
413
|
+
res.locals.errors = res.locals.errors || [];
|
|
414
|
+
next();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Error handling middleware
|
|
418
|
+
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
419
|
+
if (!res.locals.errors) {
|
|
420
|
+
res.locals.errors = [];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
res.locals.errors.push({
|
|
424
|
+
message: err.message,
|
|
425
|
+
stack: err.stack,
|
|
426
|
+
timestamp: Date.now(),
|
|
427
|
+
route: req.path,
|
|
428
|
+
method: req.method,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
next(err);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Final error handler
|
|
435
|
+
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
436
|
+
if (res.locals.errors && res.locals.errors.length > 0) {
|
|
437
|
+
const aggregated: AggregatedError = {
|
|
438
|
+
message: `${res.locals.errors.length} error(s) occurred`,
|
|
439
|
+
errors: res.locals.errors,
|
|
440
|
+
totalErrors: res.locals.errors.length,
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// Log aggregated errors
|
|
444
|
+
console.error(JSON.stringify(aggregated, null, 2));
|
|
445
|
+
|
|
446
|
+
// Send error response
|
|
447
|
+
res.status(500).json({
|
|
448
|
+
error: aggregated.message,
|
|
449
|
+
totalErrors: aggregated.totalErrors,
|
|
450
|
+
});
|
|
451
|
+
} else {
|
|
452
|
+
// Single error
|
|
453
|
+
res.status(500).json({
|
|
454
|
+
error: err.message,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Key Insights:**
|
|
461
|
+
- Middleware chain allows error accumulation
|
|
462
|
+
- Preserves request context (route, method)
|
|
463
|
+
- Final middleware aggregates and reports
|
|
464
|
+
|
|
465
|
+
### 3.2 Async/Await Error Aggregation
|
|
466
|
+
|
|
467
|
+
Node.js async/await patterns with Promise.allSettled:
|
|
468
|
+
|
|
469
|
+
**Pattern:**
|
|
470
|
+
```typescript
|
|
471
|
+
interface OperationResult<T, E = Error> {
|
|
472
|
+
success: boolean;
|
|
473
|
+
value?: T;
|
|
474
|
+
error?: E;
|
|
475
|
+
operationId?: string;
|
|
476
|
+
operationName?: string;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async function executeAll<T>(
|
|
480
|
+
operations: Array<{
|
|
481
|
+
id: string;
|
|
482
|
+
name: string;
|
|
483
|
+
fn: () => Promise<T>;
|
|
484
|
+
}>
|
|
485
|
+
): Promise<OperationResult<T>[]> {
|
|
486
|
+
const results = await Promise.allSettled(
|
|
487
|
+
operations.map(op => op.fn())
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
return operations.map((op, index) => {
|
|
491
|
+
const result = results[index];
|
|
492
|
+
|
|
493
|
+
if (result.status === 'fulfilled') {
|
|
494
|
+
return {
|
|
495
|
+
success: true,
|
|
496
|
+
value: result.value,
|
|
497
|
+
operationId: op.id,
|
|
498
|
+
operationName: op.name,
|
|
499
|
+
};
|
|
500
|
+
} else {
|
|
501
|
+
const error = result.reason instanceof Error
|
|
502
|
+
? result.reason
|
|
503
|
+
: new Error(String(result.reason));
|
|
504
|
+
|
|
505
|
+
return {
|
|
506
|
+
success: false,
|
|
507
|
+
error,
|
|
508
|
+
operationId: op.id,
|
|
509
|
+
operationName: op.name,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Usage
|
|
516
|
+
const results = await executeAll([
|
|
517
|
+
{ id: '1', name: 'fetchUser', fn: fetchUser },
|
|
518
|
+
{ id: '2', name: 'fetchPosts', fn: fetchPosts },
|
|
519
|
+
{ id: '3', name: 'fetchComments', fn: fetchComments },
|
|
520
|
+
]);
|
|
521
|
+
|
|
522
|
+
const failures = results.filter(r => !r.success);
|
|
523
|
+
if (failures.length > 0) {
|
|
524
|
+
const aggregated = {
|
|
525
|
+
message: `${failures.length} operation(s) failed`,
|
|
526
|
+
errors: failures,
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
console.error(JSON.stringify(aggregated, null, 2));
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**Key Insights:**
|
|
534
|
+
- Promise.allSettled enables complete error visibility
|
|
535
|
+
- Preserves operation context (id, name)
|
|
536
|
+
- Returns structured results for easy filtering
|
|
537
|
+
|
|
538
|
+
### 3.3 Cluster/Multi-Process Error Handling
|
|
539
|
+
|
|
540
|
+
Node.js cluster module aggregating errors from workers:
|
|
541
|
+
|
|
542
|
+
**Pattern:**
|
|
543
|
+
```typescript
|
|
544
|
+
import cluster from 'cluster';
|
|
545
|
+
import os from 'os';
|
|
546
|
+
|
|
547
|
+
interface WorkerError {
|
|
548
|
+
workerId: number;
|
|
549
|
+
pid: number;
|
|
550
|
+
error: Error;
|
|
551
|
+
timestamp: number;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (cluster.isPrimary) {
|
|
555
|
+
const workerErrors: WorkerError[] = [];
|
|
556
|
+
|
|
557
|
+
// Fork workers
|
|
558
|
+
const numCPUs = os.cpus().length;
|
|
559
|
+
for (let i = 0; i < numCPUs; i++) {
|
|
560
|
+
cluster.fork();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Listen for errors from workers
|
|
564
|
+
cluster.on('exit', (worker, code, signal) => {
|
|
565
|
+
console.log(`Worker ${worker.process.pid} died`);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// Custom error message handler
|
|
569
|
+
cluster.on('message', (worker, message) => {
|
|
570
|
+
if (message.type === 'error') {
|
|
571
|
+
workerErrors.push({
|
|
572
|
+
workerId: worker.id,
|
|
573
|
+
pid: worker.process.pid!,
|
|
574
|
+
error: message.error,
|
|
575
|
+
timestamp: Date.now(),
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
// Aggregate and report if threshold reached
|
|
579
|
+
if (workerErrors.length >= 10) {
|
|
580
|
+
reportAggregatedErrors(workerErrors);
|
|
581
|
+
workerErrors.length = 0; // Clear array
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
function reportAggregatedErrors(errors: WorkerError[]) {
|
|
587
|
+
// Group by error type
|
|
588
|
+
const byType = new Map<string, WorkerError[]>();
|
|
589
|
+
for (const err of errors) {
|
|
590
|
+
const type = err.error.name;
|
|
591
|
+
if (!byType.has(type)) {
|
|
592
|
+
byType.set(type, []);
|
|
593
|
+
}
|
|
594
|
+
byType.get(type)!.push(err);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
console.error('Aggregated worker errors:', {
|
|
598
|
+
total: errors.length,
|
|
599
|
+
byType: Object.fromEntries(byType),
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
} else {
|
|
603
|
+
// Worker process
|
|
604
|
+
process.on('uncaughtException', (error: Error) => {
|
|
605
|
+
// Send error to primary
|
|
606
|
+
if (process.send) {
|
|
607
|
+
process.send({
|
|
608
|
+
type: 'error',
|
|
609
|
+
error: {
|
|
610
|
+
name: error.name,
|
|
611
|
+
message: error.message,
|
|
612
|
+
stack: error.stack,
|
|
613
|
+
},
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Exit to allow worker restart
|
|
618
|
+
process.exit(1);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
// Worker logic here
|
|
622
|
+
}
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
**Key Insights:**
|
|
626
|
+
- Inter-process communication for error aggregation
|
|
627
|
+
- Groups errors by type
|
|
628
|
+
- Threshold-based reporting
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## 4. Popular Error Handling Libraries
|
|
633
|
+
|
|
634
|
+
### 4.1 Sentry (Browser/Node)
|
|
635
|
+
|
|
636
|
+
Sentry is a popular error tracking service with SDK for multiple platforms:
|
|
637
|
+
|
|
638
|
+
**Key Features:**
|
|
639
|
+
- Automatic error aggregation
|
|
640
|
+
- Grouping by stacktrace similarity
|
|
641
|
+
- Breadcrumbs for error context
|
|
642
|
+
- Release tracking
|
|
643
|
+
- User context
|
|
644
|
+
|
|
645
|
+
**Pattern:**
|
|
646
|
+
```typescript
|
|
647
|
+
import * as Sentry from '@sentry/node';
|
|
648
|
+
|
|
649
|
+
Sentry.init({
|
|
650
|
+
dsn: 'your-dsn',
|
|
651
|
+
beforeSend(event, hint) {
|
|
652
|
+
// Modify event before sending
|
|
653
|
+
if (event.exception) {
|
|
654
|
+
// Sentry automatically groups by stacktrace
|
|
655
|
+
// You can add custom grouping
|
|
656
|
+
event.fingerprint = customFingerprint(event);
|
|
657
|
+
}
|
|
658
|
+
return event;
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
function customFingerprint(event: Event): string[] {
|
|
663
|
+
// Custom fingerprinting logic
|
|
664
|
+
if (event.request) {
|
|
665
|
+
return [event.request.url!, event.exception?.values?.[0].type!];
|
|
666
|
+
}
|
|
667
|
+
return ['{{ default }}'];
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
**Key Insights:**
|
|
672
|
+
- Server-side aggregation by stacktrace
|
|
673
|
+
- Custom fingerprinting for grouping
|
|
674
|
+
- Rich context preservation
|
|
675
|
+
|
|
676
|
+
### 4.2 p-retry (Node.js)
|
|
677
|
+
|
|
678
|
+
Library for retrying failed promises:
|
|
679
|
+
|
|
680
|
+
**Pattern:**
|
|
681
|
+
```typescript
|
|
682
|
+
import pRetry from 'p-retry';
|
|
683
|
+
import pSettle from 'p-settle';
|
|
684
|
+
|
|
685
|
+
interface RetryResult<T> {
|
|
686
|
+
attempts: number;
|
|
687
|
+
value?: T;
|
|
688
|
+
error?: Error;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
async function executeWithRetry<T>(
|
|
692
|
+
fn: () => Promise<T>,
|
|
693
|
+
options: { retries: number; minTimeout: number }
|
|
694
|
+
): Promise<RetryResult<T>> {
|
|
695
|
+
try {
|
|
696
|
+
const value = await pRetry(fn, {
|
|
697
|
+
retries: options.retries,
|
|
698
|
+
minTimeout: options.minTimeout,
|
|
699
|
+
onFailedAttempt: (error) => {
|
|
700
|
+
console.error(`Attempt ${error.attemptNumber} failed`);
|
|
701
|
+
},
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
return {
|
|
705
|
+
attempts: 1,
|
|
706
|
+
value,
|
|
707
|
+
};
|
|
708
|
+
} catch (error) {
|
|
709
|
+
return {
|
|
710
|
+
attempts: options.retries + 1,
|
|
711
|
+
error: error as Error,
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Aggregate multiple operations with retry
|
|
717
|
+
async function executeAllWithRetry<T>(
|
|
718
|
+
operations: Array<() => Promise<T>>,
|
|
719
|
+
retryOptions: { retries: number; minTimeout: number }
|
|
720
|
+
): Promise<RetryResult<T>[]> {
|
|
721
|
+
const results = await Promise.allSettled(
|
|
722
|
+
operations.map(op => executeWithRetry(op, retryOptions))
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
return results.map(result => {
|
|
726
|
+
if (result.status === 'fulfilled') {
|
|
727
|
+
return result.value;
|
|
728
|
+
} else {
|
|
729
|
+
return {
|
|
730
|
+
attempts: retryOptions.retries + 1,
|
|
731
|
+
error: new Error(String(result.reason)),
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
**Key Insights:**
|
|
739
|
+
- Retry logic built-in
|
|
740
|
+
- Tracks attempt count
|
|
741
|
+
- Provides hooks for logging
|
|
742
|
+
|
|
743
|
+
### 4.3 VError (Verror from Joyent)
|
|
744
|
+
|
|
745
|
+
Verror is a multi-error handling library:
|
|
746
|
+
|
|
747
|
+
**Pattern:**
|
|
748
|
+
```typescript
|
|
749
|
+
import VError from 'verror';
|
|
750
|
+
|
|
751
|
+
// Create multi-error
|
|
752
|
+
const error1 = new Error('First error');
|
|
753
|
+
const error2 = new Error('Second error');
|
|
754
|
+
const error3 = new Error('Third error');
|
|
755
|
+
|
|
756
|
+
const multiError = new VError.MultiError([error1, error2, error3]);
|
|
757
|
+
|
|
758
|
+
console.log(multiError.message); // 'First of 3 errors: First error'
|
|
759
|
+
console.log(VError.fullStack(multiError)); // Full stack with all errors
|
|
760
|
+
console.log(VError.info(multiError)); // Structured error info
|
|
761
|
+
|
|
762
|
+
// Find causes
|
|
763
|
+
const causes = VError.cause(multiError);
|
|
764
|
+
if (causes instanceof VError.MultiError) {
|
|
765
|
+
const errors = causes.errors();
|
|
766
|
+
console.log(`Contains ${errors.length} errors`);
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
**Key Insights:**
|
|
771
|
+
- Specialized MultiError class
|
|
772
|
+
- Preserves all stack traces
|
|
773
|
+
- Full stack printing
|
|
774
|
+
- Cause chaining
|
|
775
|
+
|
|
776
|
+
### 4.4 Aggregate-Error (npm package)
|
|
777
|
+
|
|
778
|
+
Simple aggregate error implementation:
|
|
779
|
+
|
|
780
|
+
**Pattern:**
|
|
781
|
+
```typescript
|
|
782
|
+
import AggregateError from 'aggregate-error';
|
|
783
|
+
|
|
784
|
+
const errors = [
|
|
785
|
+
new Error('First error'),
|
|
786
|
+
new Error('Second error'),
|
|
787
|
+
new Error('Third error'),
|
|
788
|
+
];
|
|
789
|
+
|
|
790
|
+
const aggregate = new AggregateError(errors);
|
|
791
|
+
|
|
792
|
+
console.log(aggregate.name); // 'AggregateError'
|
|
793
|
+
console.log(aggregate.message); // Combined error messages
|
|
794
|
+
console.log(aggregate.errors); // Array of errors
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
**Key Insights:**
|
|
798
|
+
- Simple, focused implementation
|
|
799
|
+
- Polyfills AggregateError for older environments
|
|
800
|
+
- Combines messages for better readability
|
|
801
|
+
|
|
802
|
+
---
|
|
803
|
+
|
|
804
|
+
## 5. Cross-Cutting Best Practices
|
|
805
|
+
|
|
806
|
+
### 5.1 Error Context Preservation
|
|
807
|
+
|
|
808
|
+
**Pattern:**
|
|
809
|
+
```typescript
|
|
810
|
+
interface ErrorContext {
|
|
811
|
+
operation?: string;
|
|
812
|
+
component?: string;
|
|
813
|
+
route?: string;
|
|
814
|
+
userId?: string;
|
|
815
|
+
timestamp?: number;
|
|
816
|
+
metadata?: Record<string, unknown>;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
interface ContextualizedError {
|
|
820
|
+
error: Error;
|
|
821
|
+
context: ErrorContext;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function createContextualizedError(
|
|
825
|
+
error: Error,
|
|
826
|
+
context: ErrorContext
|
|
827
|
+
): ContextualizedError {
|
|
828
|
+
return {
|
|
829
|
+
error,
|
|
830
|
+
context: {
|
|
831
|
+
timestamp: Date.now(),
|
|
832
|
+
...context,
|
|
833
|
+
},
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function aggregateContextualizedErrors(
|
|
838
|
+
errors: ContextualizedError[],
|
|
839
|
+
message?: string
|
|
840
|
+
): Error {
|
|
841
|
+
const aggregated = new Error(
|
|
842
|
+
message || `${errors.length} error(s) occurred`
|
|
843
|
+
);
|
|
844
|
+
|
|
845
|
+
// Attach context to error
|
|
846
|
+
(aggregated as any).errors = errors;
|
|
847
|
+
(aggregated as any).getContext = () => errors.map(e => e.context);
|
|
848
|
+
|
|
849
|
+
return aggregated;
|
|
850
|
+
}
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
### 5.2 Error Categorization
|
|
854
|
+
|
|
855
|
+
**Pattern:**
|
|
856
|
+
```typescript
|
|
857
|
+
enum ErrorCategory {
|
|
858
|
+
NETWORK = 'network',
|
|
859
|
+
VALIDATION = 'validation',
|
|
860
|
+
AUTHORIZATION = 'authorization',
|
|
861
|
+
SYSTEM = 'system',
|
|
862
|
+
UNKNOWN = 'unknown',
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
interface CategorizedError {
|
|
866
|
+
error: Error;
|
|
867
|
+
category: ErrorCategory;
|
|
868
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function categorizeError(error: Error): ErrorCategory {
|
|
872
|
+
const message = error.message.toLowerCase();
|
|
873
|
+
|
|
874
|
+
if (message.includes('network') || message.includes('fetch')) {
|
|
875
|
+
return ErrorCategory.NETWORK;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if (message.includes('validation') || message.includes('invalid')) {
|
|
879
|
+
return ErrorCategory.VALIDATION;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (message.includes('unauthorized') || message.includes('forbidden')) {
|
|
883
|
+
return ErrorCategory.AUTHORIZATION;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (message.includes('econnrefused') || message.includes('enotfound')) {
|
|
887
|
+
return ErrorCategory.SYSTEM;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
return ErrorCategory.UNKNOWN;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function aggregateCategorizedErrors(
|
|
894
|
+
errors: CategorizedError[]
|
|
895
|
+
): Record<ErrorCategory, CategorizedError[]> {
|
|
896
|
+
const aggregated: Record<ErrorCategory, CategorizedError[]> = {
|
|
897
|
+
[ErrorCategory.NETWORK]: [],
|
|
898
|
+
[ErrorCategory.VALIDATION]: [],
|
|
899
|
+
[ErrorCategory.AUTHORIZATION]: [],
|
|
900
|
+
[ErrorCategory.SYSTEM]: [],
|
|
901
|
+
[ErrorCategory.UNKNOWN]: [],
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
for (const err of errors) {
|
|
905
|
+
aggregated[err.category].push(err);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return aggregated;
|
|
909
|
+
}
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
### 5.3 Error Statistics
|
|
913
|
+
|
|
914
|
+
**Pattern:**
|
|
915
|
+
```typescript
|
|
916
|
+
interface ErrorStatistics {
|
|
917
|
+
totalErrors: number;
|
|
918
|
+
uniqueErrors: number;
|
|
919
|
+
errorsByType: Record<string, number>;
|
|
920
|
+
errorsByMessage: Record<string, number>;
|
|
921
|
+
firstOccurrence: number;
|
|
922
|
+
lastOccurrence: number;
|
|
923
|
+
timeRange: number;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function calculateErrorStatistics(errors: Error[]): ErrorStatistics {
|
|
927
|
+
const errorsByType: Record<string, number> = {};
|
|
928
|
+
const errorsByMessage: Record<string, number> = {};
|
|
929
|
+
const uniqueMessages = new Set<string>();
|
|
930
|
+
|
|
931
|
+
let firstOccurrence = Infinity;
|
|
932
|
+
let lastOccurrence = -Infinity;
|
|
933
|
+
|
|
934
|
+
for (const error of errors) {
|
|
935
|
+
const type = error.constructor.name;
|
|
936
|
+
errorsByType[type] = (errorsByType[type] || 0) + 1;
|
|
937
|
+
|
|
938
|
+
const message = error.message;
|
|
939
|
+
errorsByMessage[message] = (errorsByMessage[message] || 0) + 1;
|
|
940
|
+
uniqueMessages.add(message);
|
|
941
|
+
|
|
942
|
+
const timestamp = (error as any).timestamp || Date.now();
|
|
943
|
+
if (timestamp < firstOccurrence) firstOccurrence = timestamp;
|
|
944
|
+
if (timestamp > lastOccurrence) lastOccurrence = timestamp;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
return {
|
|
948
|
+
totalErrors: errors.length,
|
|
949
|
+
uniqueErrors: uniqueMessages.size,
|
|
950
|
+
errorsByType,
|
|
951
|
+
errorsByMessage,
|
|
952
|
+
firstOccurrence,
|
|
953
|
+
lastOccurrence,
|
|
954
|
+
timeRange: lastOccurrence - firstOccurrence,
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
---
|
|
960
|
+
|
|
961
|
+
## 6. Implementation Recommendations
|
|
962
|
+
|
|
963
|
+
### 6.1 For Groundswell Workflow Engine
|
|
964
|
+
|
|
965
|
+
Based on research findings, recommend implementing:
|
|
966
|
+
|
|
967
|
+
1. **Hierarchical Error Aggregation**
|
|
968
|
+
- Preserve workflow hierarchy context
|
|
969
|
+
- Include workflow ID, name, and path
|
|
970
|
+
- Maintain parent-child relationships
|
|
971
|
+
|
|
972
|
+
2. **Error Categorization**
|
|
973
|
+
- Distinguish between workflow errors, validation errors, system errors
|
|
974
|
+
- Enable category-based filtering and reporting
|
|
975
|
+
|
|
976
|
+
3. **Rich Error Context**
|
|
977
|
+
- Timestamps for each error
|
|
978
|
+
- Workflow state at time of error
|
|
979
|
+
- Relevant log entries
|
|
980
|
+
- Stack traces preserved
|
|
981
|
+
|
|
982
|
+
4. **Error Statistics**
|
|
983
|
+
- Total error count
|
|
984
|
+
- Errors by workflow
|
|
985
|
+
- Errors by type
|
|
986
|
+
- Success/failure rates
|
|
987
|
+
|
|
988
|
+
### 6.2 Recommended Implementation Structure
|
|
989
|
+
|
|
990
|
+
```typescript
|
|
991
|
+
interface WorkflowAggregateError {
|
|
992
|
+
name: 'WorkflowAggregateError';
|
|
993
|
+
message: string;
|
|
994
|
+
errors: Array<{
|
|
995
|
+
workflowId: string;
|
|
996
|
+
workflowName: string;
|
|
997
|
+
error: WorkflowError;
|
|
998
|
+
timestamp: number;
|
|
999
|
+
}>;
|
|
1000
|
+
parentContext: {
|
|
1001
|
+
workflowId: string;
|
|
1002
|
+
workflowName: string;
|
|
1003
|
+
taskName: string;
|
|
1004
|
+
};
|
|
1005
|
+
stats: {
|
|
1006
|
+
totalChildren: number;
|
|
1007
|
+
failedChildren: number;
|
|
1008
|
+
successRate: number;
|
|
1009
|
+
errorsByType: Record<string, number>;
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
---
|
|
1015
|
+
|
|
1016
|
+
## References
|
|
1017
|
+
|
|
1018
|
+
### Official Documentation
|
|
1019
|
+
1. React Error Boundaries - https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary
|
|
1020
|
+
2. Angular ErrorHandler - https://angular.io/api/core/ErrorHandler
|
|
1021
|
+
3. Express Error Handling - https://expressjs.com/en/guide/error-handling.html
|
|
1022
|
+
|
|
1023
|
+
### Community Libraries
|
|
1024
|
+
4. Sentry JavaScript SDK - https://docs.sentry.io/platforms/javascript/
|
|
1025
|
+
5. p-retry - https://github.com/sindresorhus/p-retry
|
|
1026
|
+
6. p-settle - https://github.com/sindresorhus/p-settle
|
|
1027
|
+
7. VError - https://www.npmjs.com/package/verror
|
|
1028
|
+
8. aggregate-error - https://github.com/sindresorhus/aggregate-error
|
|
1029
|
+
|
|
1030
|
+
### GitHub Repositories
|
|
1031
|
+
9. Facebook React - Error handling patterns
|
|
1032
|
+
10. Angular Angular - Error handling implementation
|
|
1033
|
+
11. Expressjs Express - Error middleware
|
|
1034
|
+
|
|
1035
|
+
### Groundswell-Specific
|
|
1036
|
+
12. Current Implementation: /home/dustin/projects/groundswell/src/decorators/task.ts
|
|
1037
|
+
13. Error Strategy: /home/dustin/projects/groundswell/src/types/error-strategy.ts
|
|
1038
|
+
14. WorkflowError: /home/dustin/projects/groundswell/src/types/error.ts
|
|
1039
|
+
|
|
1040
|
+
---
|
|
1041
|
+
|
|
1042
|
+
**Document Version:** 1.0
|
|
1043
|
+
**Last Updated:** 2026-01-12
|
|
1044
|
+
**Status:** Complete
|
|
1045
|
+
**Next Review:** After P1M2T2S2 Implementation
|