mdcontext 0.0.1 → 0.2.0
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/.changeset/README.md +28 -0
- package/.changeset/config.json +11 -0
- package/.claude/settings.local.json +25 -0
- package/.github/workflows/ci.yml +83 -0
- package/.github/workflows/claude-code-review.yml +44 -0
- package/.github/workflows/claude.yml +85 -0
- package/.github/workflows/release.yml +113 -0
- package/.tldrignore +112 -0
- package/BACKLOG.md +338 -0
- package/CONTRIBUTING.md +186 -0
- package/NOTES/NOTES +44 -0
- package/README.md +434 -11
- package/biome.json +36 -0
- package/cspell.config.yaml +14 -0
- package/dist/chunk-23UPXDNL.js +3044 -0
- package/dist/chunk-2W7MO2DL.js +1366 -0
- package/dist/chunk-3NUAZGMA.js +1689 -0
- package/dist/chunk-7TOWB2XB.js +366 -0
- package/dist/chunk-7XOTOADQ.js +3065 -0
- package/dist/chunk-AH2PDM2K.js +3042 -0
- package/dist/chunk-BNXWSZ63.js +3742 -0
- package/dist/chunk-BTL5DJVU.js +3222 -0
- package/dist/chunk-HDHYG7E4.js +104 -0
- package/dist/chunk-HLR4KZBP.js +3234 -0
- package/dist/chunk-IP3FRFEB.js +1045 -0
- package/dist/chunk-KHU56VDO.js +3042 -0
- package/dist/chunk-KRYIFLQR.js +88 -0
- package/dist/chunk-LBSDNLEM.js +287 -0
- package/dist/chunk-MNTQ7HCP.js +2643 -0
- package/dist/chunk-MUJELQQ6.js +1387 -0
- package/dist/chunk-MXJGMSLV.js +2199 -0
- package/dist/chunk-N6QJGC3Z.js +2636 -0
- package/dist/chunk-OBELGBPM.js +1713 -0
- package/dist/chunk-OT7R5XTA.js +3192 -0
- package/dist/chunk-P7X4RA2T.js +106 -0
- package/dist/chunk-PIDUQNC2.js +3185 -0
- package/dist/chunk-POGCDIH4.js +3187 -0
- package/dist/chunk-PSIEOQGZ.js +3043 -0
- package/dist/chunk-PVRT3IHA.js +3238 -0
- package/dist/chunk-QNN4TT23.js +1430 -0
- package/dist/chunk-RE3R45RJ.js +3042 -0
- package/dist/chunk-S7E6TFX6.js +803 -0
- package/dist/chunk-SG6GLU4U.js +1378 -0
- package/dist/chunk-SJCDV2ST.js +274 -0
- package/dist/chunk-SYE5XLF3.js +104 -0
- package/dist/chunk-T5VLYBZD.js +103 -0
- package/dist/chunk-TOQB7VWU.js +3238 -0
- package/dist/chunk-VFNMZ4ZQ.js +3228 -0
- package/dist/chunk-VVTGZNBT.js +1629 -0
- package/dist/chunk-W7Q4RFEV.js +104 -0
- package/dist/chunk-XTYYVRLO.js +3190 -0
- package/dist/chunk-Y6MDYVJD.js +3063 -0
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +5458 -0
- package/dist/index.d.ts +653 -0
- package/dist/index.js +79 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +472 -0
- package/dist/schema-BAWSG7KY.js +22 -0
- package/dist/schema-E3QUPL26.js +20 -0
- package/dist/schema-EHL7WUT6.js +20 -0
- package/docs/019-USAGE.md +625 -0
- package/docs/020-current-implementation.md +364 -0
- package/docs/021-DOGFOODING-FINDINGS.md +175 -0
- package/docs/BACKLOG.md +80 -0
- package/docs/CONFIG.md +1123 -0
- package/docs/DESIGN.md +439 -0
- package/docs/ERRORS.md +383 -0
- package/docs/PROJECT.md +88 -0
- package/docs/ROADMAP.md +407 -0
- package/docs/summarization.md +320 -0
- package/docs/test-links.md +9 -0
- package/justfile +40 -0
- package/package.json +74 -9
- package/pnpm-workspace.yaml +5 -0
- package/research/INDEX.md +315 -0
- package/research/code-review/README.md +90 -0
- package/research/code-review/cli-error-handling-review.md +979 -0
- package/research/code-review/code-review-validation-report.md +464 -0
- package/research/code-review/main-ts-review.md +1128 -0
- package/research/config-analysis/01-current-implementation.md +470 -0
- package/research/config-analysis/02-strategy-recommendation.md +428 -0
- package/research/config-analysis/03-task-candidates.md +715 -0
- package/research/config-analysis/033-research-configuration-management.md +828 -0
- package/research/config-analysis/034-research-effect-cli-config.md +1504 -0
- package/research/config-analysis/04-consolidated-task-candidates.md +277 -0
- package/research/config-docs/SUMMARY.md +357 -0
- package/research/config-docs/TEST-RESULTS.md +776 -0
- package/research/config-docs/TODO.md +542 -0
- package/research/config-docs/analysis.md +744 -0
- package/research/config-docs/fix-validation.md +502 -0
- package/research/config-docs/help-audit.md +264 -0
- package/research/config-docs/help-system-analysis.md +890 -0
- package/research/dogfood/consolidated-tool-evaluation.md +373 -0
- package/research/dogfood/strategy-a/a-synthesis.md +184 -0
- package/research/dogfood/strategy-a/a1-docs.md +226 -0
- package/research/dogfood/strategy-a/a2-amorphic.md +156 -0
- package/research/dogfood/strategy-a/a3-llm.md +164 -0
- package/research/dogfood/strategy-b/b-synthesis.md +228 -0
- package/research/dogfood/strategy-b/b1-architecture.md +207 -0
- package/research/dogfood/strategy-b/b2-gaps.md +258 -0
- package/research/dogfood/strategy-b/b3-workflows.md +250 -0
- package/research/dogfood/strategy-c/c-synthesis.md +451 -0
- package/research/dogfood/strategy-c/c1-explorer.md +192 -0
- package/research/dogfood/strategy-c/c2-diver-memory.md +145 -0
- package/research/dogfood/strategy-c/c3-diver-control.md +148 -0
- package/research/dogfood/strategy-c/c4-diver-failure.md +151 -0
- package/research/dogfood/strategy-c/c5-diver-execution.md +221 -0
- package/research/dogfood/strategy-c/c6-diver-org.md +221 -0
- package/research/effect-cli-error-handling.md +845 -0
- package/research/effect-errors-as-values.md +943 -0
- package/research/errors-task-analysis/00-consolidated-tasks.md +207 -0
- package/research/errors-task-analysis/cli-commands-analysis.md +909 -0
- package/research/errors-task-analysis/embeddings-analysis.md +709 -0
- package/research/errors-task-analysis/index-search-analysis.md +812 -0
- package/research/frontmatter/COMMENTS-ARE-SKIPPED.md +149 -0
- package/research/frontmatter/LLM-CODE-NAVIGATION.md +276 -0
- package/research/issue-review.md +603 -0
- package/research/llm-summarization/agent-cli-tools-2026.md +1082 -0
- package/research/llm-summarization/alternative-providers-2026.md +1428 -0
- package/research/llm-summarization/anthropic-2026.md +367 -0
- package/research/llm-summarization/claude-cli-integration.md +1706 -0
- package/research/llm-summarization/cli-integration-patterns.md +3155 -0
- package/research/llm-summarization/openai-2026.md +473 -0
- package/research/llm-summarization/openai-compatible-providers-2026.md +1022 -0
- package/research/llm-summarization/opencode-cli-integration.md +1552 -0
- package/research/llm-summarization/prompt-engineering-2026.md +1426 -0
- package/research/llm-summarization/prototype-results.md +56 -0
- package/research/llm-summarization/provider-switching-patterns-2026.md +2153 -0
- package/research/llm-summarization/typescript-llm-libraries-2026.md +2436 -0
- package/research/mdcontext-error-analysis.md +521 -0
- package/research/mdcontext-pudding/00-EXECUTIVE-SUMMARY.md +282 -0
- package/research/mdcontext-pudding/01-index-embed.md +956 -0
- package/research/mdcontext-pudding/02-search-COMMANDS.md +142 -0
- package/research/mdcontext-pudding/02-search-SUMMARY.md +146 -0
- package/research/mdcontext-pudding/02-search.md +970 -0
- package/research/mdcontext-pudding/03-context.md +779 -0
- package/research/mdcontext-pudding/04-navigation-and-analytics.md +803 -0
- package/research/mdcontext-pudding/04-tree.md +704 -0
- package/research/mdcontext-pudding/05-config.md +1038 -0
- package/research/mdcontext-pudding/06-links-summary.txt +87 -0
- package/research/mdcontext-pudding/06-links.md +679 -0
- package/research/mdcontext-pudding/07-stats.md +693 -0
- package/research/mdcontext-pudding/BUG-FIX-PLAN.md +388 -0
- package/research/mdcontext-pudding/P0-BUG-VALIDATION.md +167 -0
- package/research/mdcontext-pudding/README.md +168 -0
- package/research/mdcontext-pudding/TESTING-SUMMARY.md +128 -0
- package/research/npm_publish/011-npm-workflow-research-agent2.md +792 -0
- package/research/npm_publish/012-npm-workflow-research-agent1.md +530 -0
- package/research/npm_publish/013-npm-workflow-research-agent3.md +722 -0
- package/research/npm_publish/014-npm-workflow-synthesis.md +556 -0
- package/research/npm_publish/031-npm-workflow-task-analysis.md +134 -0
- package/research/research-quality-review.md +834 -0
- package/research/semantic-search/002-research-embedding-models.md +490 -0
- package/research/semantic-search/003-research-rag-alternatives.md +523 -0
- package/research/semantic-search/004-research-vector-search.md +841 -0
- package/research/semantic-search/032-research-semantic-search.md +427 -0
- package/research/semantic-search/embedding-text-analysis.md +156 -0
- package/research/semantic-search/multi-word-failure-reproduction.md +171 -0
- package/research/semantic-search/query-processing-analysis.md +207 -0
- package/research/semantic-search/root-cause-and-solution.md +114 -0
- package/research/semantic-search/threshold-validation-report.md +69 -0
- package/research/semantic-search/vector-search-analysis.md +63 -0
- package/research/task-management-2026/00-synthesis-recommendations.md +295 -0
- package/research/task-management-2026/01-ai-workflow-tools.md +416 -0
- package/research/task-management-2026/02-agent-framework-patterns.md +476 -0
- package/research/task-management-2026/03-lightweight-file-based.md +567 -0
- package/research/task-management-2026/04-established-tools-ai-features.md +541 -0
- package/research/task-management-2026/linear/01-core-features-workflow.md +771 -0
- package/research/task-management-2026/linear/02-api-integrations.md +930 -0
- package/research/task-management-2026/linear/03-ai-features.md +368 -0
- package/research/task-management-2026/linear/04-pricing-setup.md +205 -0
- package/research/task-management-2026/linear/05-usage-patterns-best-practices.md +605 -0
- package/research/test-path-issues.md +276 -0
- package/review/ALP-76/1-error-type-design.md +962 -0
- package/review/ALP-76/2-error-handling-patterns.md +906 -0
- package/review/ALP-76/3-error-presentation.md +624 -0
- package/review/ALP-76/4-test-coverage.md +625 -0
- package/review/ALP-76/5-migration-completeness.md +440 -0
- package/review/ALP-76/6-effect-best-practices.md +755 -0
- package/scripts/apply-branch-protection.sh +47 -0
- package/scripts/branch-protection-templates.json +79 -0
- package/scripts/prototype-summarization.ts +346 -0
- package/scripts/rebuild-hnswlib.js +58 -0
- package/scripts/setup-branch-protection.sh +64 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/active-provider.json +7 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.json +541 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/bm25.meta.json +5 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/config.json +8 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/documents.json +60 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/links.json +13 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/.mdcontext/indexes/sections.json +1197 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/configuration-management.md +99 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/distributed-systems.md +92 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/error-handling.md +78 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/failure-automation.md +55 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/job-context.md +69 -0
- package/src/__tests__/fixtures/semantic-search/multi-word-corpus/process-orchestration.md +99 -0
- package/src/cli/argv-preprocessor.test.ts +210 -0
- package/src/cli/argv-preprocessor.ts +202 -0
- package/src/cli/cli.test.ts +627 -0
- package/src/cli/commands/backlinks.ts +54 -0
- package/src/cli/commands/config-cmd.ts +642 -0
- package/src/cli/commands/context.ts +285 -0
- package/src/cli/commands/duplicates.ts +122 -0
- package/src/cli/commands/embeddings.ts +529 -0
- package/src/cli/commands/index-cmd.ts +480 -0
- package/src/cli/commands/index.ts +16 -0
- package/src/cli/commands/links.ts +52 -0
- package/src/cli/commands/search.ts +1281 -0
- package/src/cli/commands/stats.ts +149 -0
- package/src/cli/commands/tree.ts +128 -0
- package/src/cli/config-layer.ts +176 -0
- package/src/cli/error-handler.test.ts +235 -0
- package/src/cli/error-handler.ts +655 -0
- package/src/cli/flag-schemas.ts +341 -0
- package/src/cli/help.ts +588 -0
- package/src/cli/index.ts +9 -0
- package/src/cli/main.ts +435 -0
- package/src/cli/options.ts +41 -0
- package/src/cli/shared-error-handling.ts +199 -0
- package/src/cli/typo-suggester.test.ts +105 -0
- package/src/cli/typo-suggester.ts +130 -0
- package/src/cli/utils.ts +259 -0
- package/src/config/file-provider.test.ts +320 -0
- package/src/config/file-provider.ts +273 -0
- package/src/config/index.ts +72 -0
- package/src/config/integration.test.ts +667 -0
- package/src/config/precedence.test.ts +277 -0
- package/src/config/precedence.ts +451 -0
- package/src/config/schema.test.ts +414 -0
- package/src/config/schema.ts +603 -0
- package/src/config/service.test.ts +320 -0
- package/src/config/service.ts +243 -0
- package/src/config/testing.test.ts +264 -0
- package/src/config/testing.ts +110 -0
- package/src/core/index.ts +1 -0
- package/src/core/types.ts +113 -0
- package/src/duplicates/detector.test.ts +183 -0
- package/src/duplicates/detector.ts +414 -0
- package/src/duplicates/index.ts +18 -0
- package/src/embeddings/embedding-namespace.test.ts +300 -0
- package/src/embeddings/embedding-namespace.ts +947 -0
- package/src/embeddings/heading-boost.test.ts +222 -0
- package/src/embeddings/hnsw-build-options.test.ts +198 -0
- package/src/embeddings/hyde.test.ts +272 -0
- package/src/embeddings/hyde.ts +264 -0
- package/src/embeddings/index.ts +10 -0
- package/src/embeddings/openai-provider.ts +414 -0
- package/src/embeddings/pricing.json +22 -0
- package/src/embeddings/provider-constants.ts +204 -0
- package/src/embeddings/provider-errors.test.ts +967 -0
- package/src/embeddings/provider-errors.ts +565 -0
- package/src/embeddings/provider-factory.test.ts +240 -0
- package/src/embeddings/provider-factory.ts +225 -0
- package/src/embeddings/provider-integration.test.ts +788 -0
- package/src/embeddings/query-preprocessing.test.ts +187 -0
- package/src/embeddings/semantic-search-threshold.test.ts +508 -0
- package/src/embeddings/semantic-search.ts +1270 -0
- package/src/embeddings/types.ts +359 -0
- package/src/embeddings/vector-store.ts +708 -0
- package/src/embeddings/voyage-provider.ts +313 -0
- package/src/errors/errors.test.ts +845 -0
- package/src/errors/index.ts +533 -0
- package/src/index/ignore-patterns.test.ts +354 -0
- package/src/index/ignore-patterns.ts +305 -0
- package/src/index/index.ts +4 -0
- package/src/index/indexer.ts +684 -0
- package/src/index/storage.ts +260 -0
- package/src/index/types.ts +147 -0
- package/src/index/watcher.ts +189 -0
- package/src/index.ts +30 -0
- package/src/integration/search-keyword.test.ts +678 -0
- package/src/mcp/server.ts +612 -0
- package/src/parser/index.ts +1 -0
- package/src/parser/parser.test.ts +291 -0
- package/src/parser/parser.ts +394 -0
- package/src/parser/section-filter.test.ts +277 -0
- package/src/parser/section-filter.ts +392 -0
- package/src/search/__tests__/hybrid-search.test.ts +650 -0
- package/src/search/bm25-store.ts +366 -0
- package/src/search/cross-encoder.test.ts +253 -0
- package/src/search/cross-encoder.ts +406 -0
- package/src/search/fuzzy-search.test.ts +419 -0
- package/src/search/fuzzy-search.ts +273 -0
- package/src/search/hybrid-search.ts +448 -0
- package/src/search/path-matcher.test.ts +276 -0
- package/src/search/path-matcher.ts +33 -0
- package/src/search/query-parser.test.ts +260 -0
- package/src/search/query-parser.ts +319 -0
- package/src/search/searcher.test.ts +280 -0
- package/src/search/searcher.ts +724 -0
- package/src/search/wink-bm25.d.ts +30 -0
- package/src/summarization/cli-providers/claude.ts +202 -0
- package/src/summarization/cli-providers/detection.test.ts +273 -0
- package/src/summarization/cli-providers/detection.ts +118 -0
- package/src/summarization/cli-providers/index.ts +8 -0
- package/src/summarization/cost.test.ts +139 -0
- package/src/summarization/cost.ts +102 -0
- package/src/summarization/error-handler.test.ts +127 -0
- package/src/summarization/error-handler.ts +111 -0
- package/src/summarization/index.ts +102 -0
- package/src/summarization/pipeline.test.ts +498 -0
- package/src/summarization/pipeline.ts +231 -0
- package/src/summarization/prompts.test.ts +269 -0
- package/src/summarization/prompts.ts +133 -0
- package/src/summarization/provider-factory.test.ts +396 -0
- package/src/summarization/provider-factory.ts +178 -0
- package/src/summarization/types.ts +184 -0
- package/src/summarize/budget-bugs.test.ts +620 -0
- package/src/summarize/formatters.ts +419 -0
- package/src/summarize/index.ts +20 -0
- package/src/summarize/summarizer.test.ts +275 -0
- package/src/summarize/summarizer.ts +597 -0
- package/src/summarize/verify-bugs.test.ts +238 -0
- package/src/types/huggingface-transformers.d.ts +66 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/tokens.test.ts +142 -0
- package/src/utils/tokens.ts +186 -0
- package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -0
- package/tests/fixtures/cli/.mdcontext/config.json +8 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/embeddings/openai_text-embedding-3-small_512/vectors.meta.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/indexes/documents.json +33 -0
- package/tests/fixtures/cli/.mdcontext/indexes/links.json +12 -0
- package/tests/fixtures/cli/.mdcontext/indexes/sections.json +247 -0
- package/tests/fixtures/cli/README.md +9 -0
- package/tests/fixtures/cli/api-reference.md +11 -0
- package/tests/fixtures/cli/getting-started.md +11 -0
- package/tests/integration/embed-index.test.ts +712 -0
- package/tests/integration/search-context.test.ts +469 -0
- package/tests/integration/search-semantic.test.ts +522 -0
- package/tsconfig.json +26 -0
- package/vitest.config.ts +16 -0
- package/vitest.setup.ts +12 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
# mdcontext Error Handling Analysis
|
|
2
|
+
|
|
3
|
+
## Executive Summary
|
|
4
|
+
|
|
5
|
+
The mdcontext codebase has a mixed error handling strategy. While it defines typed errors in `core/types.ts` (ParseError, IoError, IndexError) and uses Effect for error management, the actual implementation frequently loses type information by converting typed errors to generic `Error` objects. This document catalogs the current state and proposes a unified approach.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Current Error Handling Audit
|
|
10
|
+
|
|
11
|
+
### 1.1 Custom Error Classes
|
|
12
|
+
|
|
13
|
+
**Location: `/src/embeddings/openai-provider.ts`**
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
export class MissingApiKeyError extends Error {
|
|
17
|
+
constructor() {
|
|
18
|
+
super("OPENAI_API_KEY not set");
|
|
19
|
+
this.name = "MissingApiKeyError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class InvalidApiKeyError extends Error {
|
|
24
|
+
constructor(message?: string) {
|
|
25
|
+
super(message ?? "Invalid OPENAI_API_KEY");
|
|
26
|
+
this.name = "InvalidApiKeyError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Location: `/src/core/types.ts`** (Tagged unions - Effect style)
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
export interface ParseError {
|
|
35
|
+
readonly _tag: "ParseError";
|
|
36
|
+
readonly message: string;
|
|
37
|
+
readonly line?: number | undefined;
|
|
38
|
+
readonly column?: number | undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface IoError {
|
|
42
|
+
readonly _tag: "IoError";
|
|
43
|
+
readonly message: string;
|
|
44
|
+
readonly path: string;
|
|
45
|
+
readonly cause?: unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface IndexError {
|
|
49
|
+
readonly _tag: "IndexError";
|
|
50
|
+
readonly cause: "DiskFull" | "Permission" | "Corrupted" | "Unknown";
|
|
51
|
+
readonly message: string;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 1.2 The `handleApiKeyError` Utility
|
|
56
|
+
|
|
57
|
+
**Location: `/src/embeddings/openai-provider.ts:130-165`**
|
|
58
|
+
|
|
59
|
+
This is a key pattern in the codebase that demonstrates the problem:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
export const handleApiKeyError = <A, E>(
|
|
63
|
+
effect: Effect.Effect<A, E | MissingApiKeyError | InvalidApiKeyError>,
|
|
64
|
+
): Effect.Effect<A, E | Error> =>
|
|
65
|
+
effect.pipe(
|
|
66
|
+
Effect.catchIf(
|
|
67
|
+
(e): e is MissingApiKeyError => e instanceof MissingApiKeyError,
|
|
68
|
+
() =>
|
|
69
|
+
Effect.gen(function* () {
|
|
70
|
+
yield* Console.error("");
|
|
71
|
+
yield* Console.error("Error: OPENAI_API_KEY not set");
|
|
72
|
+
// ... more Console.error calls ...
|
|
73
|
+
return yield* Effect.fail(new Error("Missing API key")); // <-- Type lost here
|
|
74
|
+
}),
|
|
75
|
+
),
|
|
76
|
+
Effect.catchIf(
|
|
77
|
+
(e): e is InvalidApiKeyError => e instanceof InvalidApiKeyError,
|
|
78
|
+
(e) =>
|
|
79
|
+
Effect.gen(function* () {
|
|
80
|
+
// ... Console.error calls ...
|
|
81
|
+
return yield* Effect.fail(new Error("Invalid API key")); // <-- Type lost here
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Problems:**
|
|
88
|
+
|
|
89
|
+
1. Catches typed errors (`MissingApiKeyError`, `InvalidApiKeyError`)
|
|
90
|
+
2. Prints messages via `Console.error`
|
|
91
|
+
3. Converts to generic `Error` - losing type information for callers
|
|
92
|
+
4. Mixes error handling with error display (violates separation of concerns)
|
|
93
|
+
|
|
94
|
+
### 1.3 CLI Command Error Handling
|
|
95
|
+
|
|
96
|
+
**`/src/cli/commands/search.ts`**
|
|
97
|
+
|
|
98
|
+
Uses `handleApiKeyError` for semantic search operations:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const results =
|
|
102
|
+
yield *
|
|
103
|
+
semanticSearch(resolvedDir, query, {
|
|
104
|
+
limit,
|
|
105
|
+
threshold,
|
|
106
|
+
}).pipe(handleApiKeyError);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Also uses `Effect.catchAll(() => Effect.succeed(null))` pattern to swallow errors:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const estimate =
|
|
113
|
+
yield *
|
|
114
|
+
estimateEmbeddingCost(resolvedDir).pipe(
|
|
115
|
+
Effect.catchAll(() => Effect.succeed(null)),
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**`/src/cli/commands/index-cmd.ts`**
|
|
120
|
+
|
|
121
|
+
Same patterns - uses `handleApiKeyError` and swallows errors with `catchAll`:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const embedResult =
|
|
125
|
+
yield *
|
|
126
|
+
buildEmbeddings(resolvedDir, {
|
|
127
|
+
// ...
|
|
128
|
+
}).pipe(
|
|
129
|
+
handleApiKeyError,
|
|
130
|
+
Effect.catchAll(() => Effect.succeed(null)),
|
|
131
|
+
);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Watch mode uses callbacks for error handling (escapes Effect):
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const watcher =
|
|
138
|
+
yield *
|
|
139
|
+
watchDirectory(resolvedRoot, {
|
|
140
|
+
onError: (error) => {
|
|
141
|
+
console.error(`Watch error: ${error.message}`); // <-- Direct console.error
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**`/src/cli/main.ts`**
|
|
147
|
+
|
|
148
|
+
Top-level error handling for CLI validation errors:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
Effect.catchAll((error) =>
|
|
152
|
+
Effect.sync(() => {
|
|
153
|
+
if (isValidationError(error)) {
|
|
154
|
+
const message = formatCliError(error);
|
|
155
|
+
console.error(`\nError: ${message}`); // <-- Direct console.error
|
|
156
|
+
console.error('\nRun "mdcontext --help" for usage information.');
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
throw error; // <-- Re-throws, escapes Effect
|
|
160
|
+
}),
|
|
161
|
+
);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 2. Identified Problems
|
|
167
|
+
|
|
168
|
+
### 2.1 Type Information Loss
|
|
169
|
+
|
|
170
|
+
| Location | Original Error | Converted To |
|
|
171
|
+
| ------------------------ | -------------------- | ---------------------------------------------- |
|
|
172
|
+
| `openai-provider.ts:147` | `MissingApiKeyError` | `new Error('Missing API key')` |
|
|
173
|
+
| `openai-provider.ts:162` | `InvalidApiKeyError` | `new Error('Invalid API key')` |
|
|
174
|
+
| `storage.ts:25` | Unknown | `new Error(\`Failed to create directory...\`)` |
|
|
175
|
+
| `storage.ts:38` | Unknown | `new Error(\`Failed to read...\`)` |
|
|
176
|
+
| `storage.ts:50` | Unknown | `new Error(\`Failed to write...\`)` |
|
|
177
|
+
| `context.ts:86` | `ParseError` | `new Error(\`${e.\_tag}: ${e.message}\`)` |
|
|
178
|
+
| `tree.ts:36` | `ParseError` | `new Error(\`${e.\_tag}: ${e.message}\`)` |
|
|
179
|
+
| `mcp/server.ts:260` | Various | `new Error(\`${e.\_tag}: ${e.message}\`)` |
|
|
180
|
+
| `semantic-search.ts:371` | `InvalidApiKeyError` | `new Error(\`Embedding failed: ...\`)` |
|
|
181
|
+
|
|
182
|
+
### 2.2 Direct `console.error` / `console.log` Usage
|
|
183
|
+
|
|
184
|
+
| Location | Context |
|
|
185
|
+
| ---------------------------- | ----------------------- |
|
|
186
|
+
| `main.ts:136-137` | CLI validation errors |
|
|
187
|
+
| `index-cmd.ts:94-102` | Watch mode logging |
|
|
188
|
+
| `help.ts:258-292` | Help display |
|
|
189
|
+
| `argv-preprocessor.ts:91-92` | Argument parsing errors |
|
|
190
|
+
| `mcp/server.ts:481` | Fatal server errors |
|
|
191
|
+
|
|
192
|
+
### 2.3 Error Swallowing with `Effect.catchAll`
|
|
193
|
+
|
|
194
|
+
The pattern `Effect.catchAll(() => Effect.succeed(null))` is used in multiple places, silently swallowing errors:
|
|
195
|
+
|
|
196
|
+
- `search.ts:348` - Cost estimation
|
|
197
|
+
- `search.ts:376` - Embedding building
|
|
198
|
+
- `search.ts:424` - Embedding building (retry)
|
|
199
|
+
- `index-cmd.ts:232` - Cost estimation prompt
|
|
200
|
+
- `index-cmd.ts:272` - Embedding building
|
|
201
|
+
- `summarizer.ts:505` - File parsing
|
|
202
|
+
|
|
203
|
+
### 2.4 Errors Escaping Effect System
|
|
204
|
+
|
|
205
|
+
| Location | Pattern |
|
|
206
|
+
| -------------------------- | ----------------------------------------------------------------------------- |
|
|
207
|
+
| `openai-provider.ts:59` | `throw new MissingApiKeyError()` in constructor |
|
|
208
|
+
| `openai-provider.ts:97-99` | `throw new InvalidApiKeyError(error.message)` / `throw error` in async method |
|
|
209
|
+
| `main.ts:141` | `throw error` to re-throw unhandled errors |
|
|
210
|
+
| `watcher.ts:71-81` | `Effect.runPromise` with try/catch for callback |
|
|
211
|
+
|
|
212
|
+
### 2.5 Mixed Error Paradigms
|
|
213
|
+
|
|
214
|
+
The codebase uses three different error paradigms:
|
|
215
|
+
|
|
216
|
+
1. **Effect tagged unions** (`ParseError`, `IoError`, `IndexError` in `core/types.ts`)
|
|
217
|
+
2. **JavaScript Error subclasses** (`MissingApiKeyError`, `InvalidApiKeyError`)
|
|
218
|
+
3. **Generic Error objects** (`new Error(...)` throughout)
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 3. Complete Error Type Catalog
|
|
223
|
+
|
|
224
|
+
### 3.1 Defined Error Types (Effect style)
|
|
225
|
+
|
|
226
|
+
| Type | Location | Fields |
|
|
227
|
+
| ------------ | --------------- | ------------------------------------- |
|
|
228
|
+
| `ParseError` | `core/types.ts` | `_tag`, `message`, `line?`, `column?` |
|
|
229
|
+
| `IoError` | `core/types.ts` | `_tag`, `message`, `path`, `cause?` |
|
|
230
|
+
| `IndexError` | `core/types.ts` | `_tag`, `cause`, `message` |
|
|
231
|
+
|
|
232
|
+
### 3.2 Defined Error Classes (JavaScript style)
|
|
233
|
+
|
|
234
|
+
| Class | Location | Purpose |
|
|
235
|
+
| -------------------- | ------------------------------- | ---------------- |
|
|
236
|
+
| `MissingApiKeyError` | `embeddings/openai-provider.ts` | No API key set |
|
|
237
|
+
| `InvalidApiKeyError` | `embeddings/openai-provider.ts` | API key rejected |
|
|
238
|
+
|
|
239
|
+
### 3.3 Ad-hoc Error Messages (Generic Error)
|
|
240
|
+
|
|
241
|
+
| Message Pattern | Location | Count |
|
|
242
|
+
| -------------------------------------- | ----------------------------------- | ----- |
|
|
243
|
+
| `"Index not found..."` | `semantic-search.ts`, `searcher.ts` | 3 |
|
|
244
|
+
| `"Embeddings not found..."` | `semantic-search.ts` | 1 |
|
|
245
|
+
| `"Document not found in index..."` | `searcher.ts` | 1 |
|
|
246
|
+
| `"Failed to create/read/write..."` | `storage.ts` | 4 |
|
|
247
|
+
| `"Failed to walk directory..."` | `indexer.ts` | 1 |
|
|
248
|
+
| `"Parse error in..."` | `indexer.ts` | 1 |
|
|
249
|
+
| `"Embedding failed..."` | `semantic-search.ts` | 1 |
|
|
250
|
+
| `"Query embedding failed..."` | `semantic-search.ts` | 1 |
|
|
251
|
+
| `"Failed to generate query embedding"` | `semantic-search.ts` | 1 |
|
|
252
|
+
| `"At least one file is required..."` | `context.ts` | 1 |
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 4. Proposed Error Architecture
|
|
257
|
+
|
|
258
|
+
### 4.1 Unified Error Type Hierarchy
|
|
259
|
+
|
|
260
|
+
All errors should follow the Effect tagged union pattern:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Base error interface
|
|
264
|
+
interface MdContextError {
|
|
265
|
+
readonly _tag: string;
|
|
266
|
+
readonly message: string;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// File system errors
|
|
270
|
+
interface IoError extends MdContextError {
|
|
271
|
+
readonly _tag: "IoError";
|
|
272
|
+
readonly path: string;
|
|
273
|
+
readonly operation: "read" | "write" | "stat" | "mkdir" | "walk";
|
|
274
|
+
readonly cause?: unknown;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Parsing errors
|
|
278
|
+
interface ParseError extends MdContextError {
|
|
279
|
+
readonly _tag: "ParseError";
|
|
280
|
+
readonly path?: string;
|
|
281
|
+
readonly line?: number;
|
|
282
|
+
readonly column?: number;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Index errors
|
|
286
|
+
interface IndexError extends MdContextError {
|
|
287
|
+
readonly _tag: "IndexError";
|
|
288
|
+
readonly kind: "NotFound" | "Corrupted" | "VersionMismatch";
|
|
289
|
+
readonly path: string;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// API errors
|
|
293
|
+
interface ApiKeyError extends MdContextError {
|
|
294
|
+
readonly _tag: "ApiKeyError";
|
|
295
|
+
readonly kind: "Missing" | "Invalid";
|
|
296
|
+
readonly provider: string;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
interface EmbeddingError extends MdContextError {
|
|
300
|
+
readonly _tag: "EmbeddingError";
|
|
301
|
+
readonly kind: "GenerationFailed" | "RateLimited" | "QuotaExceeded";
|
|
302
|
+
readonly cause?: unknown;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Search errors
|
|
306
|
+
interface SearchError extends MdContextError {
|
|
307
|
+
readonly _tag: "SearchError";
|
|
308
|
+
readonly kind: "NoIndex" | "NoEmbeddings" | "DocumentNotFound";
|
|
309
|
+
readonly path?: string;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// CLI errors
|
|
313
|
+
interface CliError extends MdContextError {
|
|
314
|
+
readonly _tag: "CliError";
|
|
315
|
+
readonly kind: "ValidationError" | "MissingArgument" | "InvalidOption";
|
|
316
|
+
readonly details?: Record<string, unknown>;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Union type for all errors
|
|
320
|
+
type MdContextErrors =
|
|
321
|
+
| IoError
|
|
322
|
+
| ParseError
|
|
323
|
+
| IndexError
|
|
324
|
+
| ApiKeyError
|
|
325
|
+
| EmbeddingError
|
|
326
|
+
| SearchError
|
|
327
|
+
| CliError;
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### 4.2 Error Constructor Functions
|
|
331
|
+
|
|
332
|
+
Following Effect conventions:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// constructors/errors.ts
|
|
336
|
+
export const IoError = (
|
|
337
|
+
operation: IoError["operation"],
|
|
338
|
+
path: string,
|
|
339
|
+
message: string,
|
|
340
|
+
cause?: unknown,
|
|
341
|
+
): IoError => ({
|
|
342
|
+
_tag: "IoError",
|
|
343
|
+
operation,
|
|
344
|
+
path,
|
|
345
|
+
message,
|
|
346
|
+
cause,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
export const ApiKeyError = (
|
|
350
|
+
kind: ApiKeyError["kind"],
|
|
351
|
+
provider: string,
|
|
352
|
+
message?: string,
|
|
353
|
+
): ApiKeyError => ({
|
|
354
|
+
_tag: "ApiKeyError",
|
|
355
|
+
kind,
|
|
356
|
+
provider,
|
|
357
|
+
message:
|
|
358
|
+
message ??
|
|
359
|
+
(kind === "Missing"
|
|
360
|
+
? `${provider} API key not set`
|
|
361
|
+
: `Invalid ${provider} API key`),
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// etc.
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### 4.3 Error Handling Flow
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
371
|
+
│ Effect Pipeline │
|
|
372
|
+
├──────────────────────────────────────────────────────────────┤
|
|
373
|
+
│ │
|
|
374
|
+
│ Service Layer (returns typed Effect errors) │
|
|
375
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
376
|
+
│ │ embeddings/openai-provider.ts │ │
|
|
377
|
+
│ │ → Effect.Effect<Result, ApiKeyError | EmbeddingError> │ │
|
|
378
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
379
|
+
│ │ │
|
|
380
|
+
│ ▼ │
|
|
381
|
+
│ Business Logic (propagates or handles errors) │
|
|
382
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
383
|
+
│ │ search/semantic-search.ts │ │
|
|
384
|
+
│ │ → Effect.Effect<Results, SearchError | ApiKeyError> │ │
|
|
385
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
386
|
+
│ │ │
|
|
387
|
+
│ ▼ │
|
|
388
|
+
│ CLI Layer (formats errors for display) │
|
|
389
|
+
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
390
|
+
│ │ cli/error-renderer.ts │ │
|
|
391
|
+
│ │ formatError(error: MdContextErrors): FormattedOutput │ │
|
|
392
|
+
│ │ - User-friendly messages │ │
|
|
393
|
+
│ │ - Actionable suggestions │ │
|
|
394
|
+
│ │ - Exit codes │ │
|
|
395
|
+
│ └─────────────────────────────────────────────────────────┘ │
|
|
396
|
+
│ │
|
|
397
|
+
└──────────────────────────────────────────────────────────────┘
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 4.4 Error Renderer (Separation of Concerns)
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// cli/error-renderer.ts
|
|
404
|
+
|
|
405
|
+
interface FormattedError {
|
|
406
|
+
readonly message: string;
|
|
407
|
+
readonly details?: string;
|
|
408
|
+
readonly suggestions?: readonly string[];
|
|
409
|
+
readonly exitCode: number;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const formatError = (error: MdContextErrors): FormattedError => {
|
|
413
|
+
switch (error._tag) {
|
|
414
|
+
case "ApiKeyError":
|
|
415
|
+
return {
|
|
416
|
+
message:
|
|
417
|
+
error.kind === "Missing"
|
|
418
|
+
? "OPENAI_API_KEY not set"
|
|
419
|
+
: "Invalid OPENAI_API_KEY",
|
|
420
|
+
suggestions: [
|
|
421
|
+
"export OPENAI_API_KEY=sk-...",
|
|
422
|
+
"Or add to .env file in project root.",
|
|
423
|
+
],
|
|
424
|
+
exitCode: 1,
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
case "SearchError":
|
|
428
|
+
if (error.kind === "NoIndex") {
|
|
429
|
+
return {
|
|
430
|
+
message: "No index found.",
|
|
431
|
+
suggestions: [
|
|
432
|
+
"Run: mdcontext index /path/to/docs",
|
|
433
|
+
"Add --embed for semantic search capabilities",
|
|
434
|
+
],
|
|
435
|
+
exitCode: 1,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
// ... more cases
|
|
439
|
+
|
|
440
|
+
// ... other error types
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const renderError = (error: FormattedError): Effect.Effect<never> =>
|
|
445
|
+
Effect.gen(function* () {
|
|
446
|
+
yield* Console.error("");
|
|
447
|
+
yield* Console.error(`Error: ${error.message}`);
|
|
448
|
+
if (error.details) {
|
|
449
|
+
yield* Console.error(`Details: ${error.details}`);
|
|
450
|
+
}
|
|
451
|
+
if (error.suggestions?.length) {
|
|
452
|
+
yield* Console.error("");
|
|
453
|
+
for (const suggestion of error.suggestions) {
|
|
454
|
+
yield* Console.error(` ${suggestion}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
yield* Console.error("");
|
|
458
|
+
return yield* Effect.fail(error); // Keep as Effect failure for exit code
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### 4.5 Migration Strategy
|
|
463
|
+
|
|
464
|
+
1. **Phase 1: Define all error types** in `src/errors/types.ts`
|
|
465
|
+
2. **Phase 2: Create constructors** in `src/errors/constructors.ts`
|
|
466
|
+
3. **Phase 3: Create error renderer** in `src/cli/error-renderer.ts`
|
|
467
|
+
4. **Phase 4: Migrate openai-provider.ts** - replace Error classes with tagged unions
|
|
468
|
+
5. **Phase 5: Migrate storage.ts** - use IoError constructors
|
|
469
|
+
6. **Phase 6: Migrate semantic-search.ts** - use SearchError/EmbeddingError
|
|
470
|
+
7. **Phase 7: Migrate CLI commands** - use error renderer at top level
|
|
471
|
+
8. **Phase 8: Remove handleApiKeyError** - no longer needed
|
|
472
|
+
|
|
473
|
+
### 4.6 Key Principles
|
|
474
|
+
|
|
475
|
+
1. **Never lose type information** - Use tagged unions throughout
|
|
476
|
+
2. **Separate error definition from display** - Errors carry data, renderers format it
|
|
477
|
+
3. **Single point of error formatting** - All user-facing error output goes through renderer
|
|
478
|
+
4. **Effect-native errors** - Use `_tag` discriminators, not `instanceof`
|
|
479
|
+
5. **Actionable errors** - Include suggestions where possible
|
|
480
|
+
6. **Structured for logging** - JSON-serializable for debugging
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## 5. Files Requiring Changes
|
|
485
|
+
|
|
486
|
+
| File | Changes Needed |
|
|
487
|
+
| ----------------------------------- | ----------------------------------------------- |
|
|
488
|
+
| `src/core/types.ts` | Expand error types or move to dedicated module |
|
|
489
|
+
| `src/embeddings/openai-provider.ts` | Replace Error classes, remove handleApiKeyError |
|
|
490
|
+
| `src/embeddings/semantic-search.ts` | Use typed errors instead of generic Error |
|
|
491
|
+
| `src/index/storage.ts` | Use IoError constructor |
|
|
492
|
+
| `src/index/indexer.ts` | Use typed errors |
|
|
493
|
+
| `src/index/watcher.ts` | Use typed errors in callbacks |
|
|
494
|
+
| `src/search/searcher.ts` | Use SearchError |
|
|
495
|
+
| `src/cli/commands/search.ts` | Use error renderer |
|
|
496
|
+
| `src/cli/commands/index-cmd.ts` | Use error renderer |
|
|
497
|
+
| `src/cli/commands/context.ts` | Use error renderer |
|
|
498
|
+
| `src/cli/main.ts` | Centralize error handling |
|
|
499
|
+
| `src/mcp/server.ts` | Use typed errors |
|
|
500
|
+
| `src/summarize/summarizer.ts` | Propagate typed errors |
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
## 6. Implementation Notes
|
|
505
|
+
|
|
506
|
+
### Don't Do
|
|
507
|
+
|
|
508
|
+
- Don't use `new Error(...)` for known error conditions
|
|
509
|
+
- Don't mix `console.error` into business logic
|
|
510
|
+
- Don't swallow errors silently with `Effect.catchAll(() => Effect.succeed(null))`
|
|
511
|
+
- Don't convert typed errors to generic errors
|
|
512
|
+
- Don't use JavaScript Error subclasses (use tagged unions)
|
|
513
|
+
|
|
514
|
+
### Do
|
|
515
|
+
|
|
516
|
+
- Define all possible errors as tagged unions
|
|
517
|
+
- Use constructor functions for consistent error creation
|
|
518
|
+
- Keep error handling separate from error display
|
|
519
|
+
- Provide actionable suggestions in error messages
|
|
520
|
+
- Use Effect's type system to track possible errors
|
|
521
|
+
- Log full error context for debugging while showing friendly messages to users
|