mdcontext 0.1.0 → 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/config.json +9 -9
- package/.claude/settings.local.json +25 -0
- package/.github/workflows/claude-code-review.yml +44 -0
- package/.github/workflows/claude.yml +85 -0
- package/CONTRIBUTING.md +186 -0
- package/NOTES/NOTES +44 -0
- package/README.md +206 -3
- package/biome.json +1 -1
- 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 +85 -89
- 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 +718 -657
- 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 +1533 -1423
- 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.js +4072 -629
- package/dist/index.d.ts +420 -33
- package/dist/index.js +8 -15
- package/dist/mcp/server.js +103 -7
- 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 +44 -5
- package/docs/020-current-implementation.md +8 -8
- package/docs/021-DOGFOODING-FINDINGS.md +1 -1
- package/docs/CONFIG.md +1123 -0
- package/docs/ERRORS.md +383 -0
- package/docs/summarization.md +320 -0
- package/justfile +40 -0
- package/package.json +39 -33
- 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-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/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-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/research-quality-review.md +834 -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/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 +32 -37
- 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 +2 -2
- package/src/cli/cli.test.ts +230 -33
- package/src/cli/commands/config-cmd.ts +642 -0
- package/src/cli/commands/context.ts +97 -9
- package/src/cli/commands/duplicates.ts +122 -0
- package/src/cli/commands/embeddings.ts +529 -0
- package/src/cli/commands/index-cmd.ts +210 -30
- package/src/cli/commands/index.ts +3 -0
- package/src/cli/commands/search.ts +894 -64
- package/src/cli/commands/stats.ts +3 -0
- package/src/cli/commands/tree.ts +26 -5
- 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 +66 -0
- package/src/cli/help.ts +209 -7
- package/src/cli/main.ts +348 -58
- package/src/cli/options.ts +10 -0
- package/src/cli/shared-error-handling.ts +199 -0
- package/src/cli/utils.ts +150 -17
- 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/types.ts +6 -33
- 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 +2 -0
- package/src/embeddings/openai-provider.ts +332 -83
- 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 +780 -93
- package/src/embeddings/types.ts +293 -16
- package/src/embeddings/vector-store.ts +486 -77
- 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/indexer.ts +286 -48
- package/src/index/storage.ts +94 -30
- package/src/index/types.ts +40 -2
- package/src/index/watcher.ts +67 -9
- package/src/index.ts +22 -0
- package/src/integration/search-keyword.test.ts +678 -0
- package/src/mcp/server.ts +135 -6
- package/src/parser/parser.ts +18 -19
- package/src/parser/section-filter.test.ts +277 -0
- package/src/parser/section-filter.ts +125 -3
- 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/searcher.test.ts +99 -1
- package/src/search/searcher.ts +189 -67
- 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/summarizer.ts +104 -35
- package/src/types/huggingface-transformers.d.ts +66 -0
- package/tests/fixtures/cli/.mdcontext/active-provider.json +7 -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 +4 -4
- package/tests/fixtures/cli/.mdcontext/indexes/sections.json +14 -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/vitest.config.ts +1 -6
- package/AGENTS.md +0 -46
- package/tests/fixtures/cli/.mdcontext/vectors.bin +0 -0
- package/tests/fixtures/cli/.mdcontext/vectors.meta.json +0 -1264
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding Namespace Unit Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the embedding namespace management module ensuring correct
|
|
5
|
+
* namespace generation, parsing, and path handling.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as path from 'node:path'
|
|
9
|
+
import { describe, expect, it } from 'vitest'
|
|
10
|
+
import {
|
|
11
|
+
generateNamespace,
|
|
12
|
+
getEmbeddingsDir,
|
|
13
|
+
getMetaPath,
|
|
14
|
+
getNamespaceDir,
|
|
15
|
+
getVectorPath,
|
|
16
|
+
parseNamespace,
|
|
17
|
+
} from './embedding-namespace.js'
|
|
18
|
+
|
|
19
|
+
describe('Embedding Namespace', () => {
|
|
20
|
+
describe('generateNamespace', () => {
|
|
21
|
+
it('should generate namespace from provider, model, and dimensions', () => {
|
|
22
|
+
const result = generateNamespace('openai', 'text-embedding-3-small', 512)
|
|
23
|
+
expect(result).toBe('openai_text-embedding-3-small_512')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should lowercase provider and model', () => {
|
|
27
|
+
const result = generateNamespace('OpenAI', 'Text-Embedding-3-Small', 512)
|
|
28
|
+
expect(result).toBe('openai_text-embedding-3-small_512')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should sanitize special characters to underscores', () => {
|
|
32
|
+
const result = generateNamespace('my provider', 'my.model/v1', 1024)
|
|
33
|
+
expect(result).toBe('my_provider_my_model_v1_1024')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should preserve hyphens', () => {
|
|
37
|
+
const result = generateNamespace('openai', 'text-embedding-3-small', 512)
|
|
38
|
+
expect(result).toContain('text-embedding-3-small')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should throw for empty provider', () => {
|
|
42
|
+
expect(() => generateNamespace('', 'model', 512)).toThrow(
|
|
43
|
+
'Provider name cannot be empty',
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should throw for empty model', () => {
|
|
48
|
+
expect(() => generateNamespace('openai', '', 512)).toThrow(
|
|
49
|
+
'Model name cannot be empty',
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should throw for zero dimensions', () => {
|
|
54
|
+
expect(() => generateNamespace('openai', 'model', 0)).toThrow(
|
|
55
|
+
'Dimensions must be a positive number',
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('should throw for negative dimensions', () => {
|
|
60
|
+
expect(() => generateNamespace('openai', 'model', -1)).toThrow(
|
|
61
|
+
'Dimensions must be a positive number',
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should throw for non-finite dimensions', () => {
|
|
66
|
+
expect(() => generateNamespace('openai', 'model', Infinity)).toThrow(
|
|
67
|
+
'Dimensions must be a positive number',
|
|
68
|
+
)
|
|
69
|
+
expect(() => generateNamespace('openai', 'model', NaN)).toThrow(
|
|
70
|
+
'Dimensions must be a positive number',
|
|
71
|
+
)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should handle provider with only spaces (sanitized to underscores)', () => {
|
|
75
|
+
// Spaces get replaced with underscores, resulting in valid (if ugly) namespace
|
|
76
|
+
// Format: provider_model_dimensions, so 3 spaces become '___' + '_' separator + 'model' + '_' + '512'
|
|
77
|
+
const result = generateNamespace(' ', 'model', 512)
|
|
78
|
+
expect(result).toBe('____model_512')
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('parseNamespace', () => {
|
|
83
|
+
it('should parse valid namespace', () => {
|
|
84
|
+
const result = parseNamespace('openai_text-embedding-3-small_512')
|
|
85
|
+
expect(result).toEqual({
|
|
86
|
+
provider: 'openai',
|
|
87
|
+
model: 'text-embedding-3-small',
|
|
88
|
+
dimensions: 512,
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should handle model with underscores', () => {
|
|
93
|
+
const result = parseNamespace('voyage_voyage_3_5_lite_1024')
|
|
94
|
+
expect(result).toEqual({
|
|
95
|
+
provider: 'voyage',
|
|
96
|
+
model: 'voyage_3_5_lite',
|
|
97
|
+
dimensions: 1024,
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should return null for empty string', () => {
|
|
102
|
+
expect(parseNamespace('')).toBeNull()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should return null for string without underscores', () => {
|
|
106
|
+
expect(parseNamespace('nodimensions')).toBeNull()
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should return null for non-numeric dimensions', () => {
|
|
110
|
+
expect(parseNamespace('provider_model_abc')).toBeNull()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should return null for zero dimensions', () => {
|
|
114
|
+
expect(parseNamespace('provider_model_0')).toBeNull()
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should return null for negative dimensions', () => {
|
|
118
|
+
expect(parseNamespace('provider_model_-5')).toBeNull()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should return null for empty provider', () => {
|
|
122
|
+
expect(parseNamespace('_model_512')).toBeNull()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should return null for empty model', () => {
|
|
126
|
+
expect(parseNamespace('provider__512')).toBeNull()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should return null for single underscore', () => {
|
|
130
|
+
expect(parseNamespace('_')).toBeNull()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should return null for only dimensions', () => {
|
|
134
|
+
expect(parseNamespace('_512')).toBeNull()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should return null for dimensions with suffix', () => {
|
|
138
|
+
// Strict validation: "512abc" should be rejected
|
|
139
|
+
expect(parseNamespace('provider_model_512abc')).toBeNull()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should return null for dimensions with prefix', () => {
|
|
143
|
+
expect(parseNamespace('provider_model_abc512')).toBeNull()
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('getEmbeddingsDir', () => {
|
|
148
|
+
it('should return correct embeddings directory path', () => {
|
|
149
|
+
const result = getEmbeddingsDir('/project')
|
|
150
|
+
expect(result).toBe(path.join('/project', '.mdcontext', 'embeddings'))
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should handle trailing slash in root path', () => {
|
|
154
|
+
const result = getEmbeddingsDir('/project/')
|
|
155
|
+
expect(result).toBe(path.join('/project/', '.mdcontext', 'embeddings'))
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
describe('getNamespaceDir', () => {
|
|
160
|
+
it('should return correct namespace directory path', () => {
|
|
161
|
+
const result = getNamespaceDir(
|
|
162
|
+
'/project',
|
|
163
|
+
'openai_text-embedding-3-small_512',
|
|
164
|
+
)
|
|
165
|
+
expect(result).toBe(
|
|
166
|
+
path.join(
|
|
167
|
+
'/project',
|
|
168
|
+
'.mdcontext',
|
|
169
|
+
'embeddings',
|
|
170
|
+
'openai_text-embedding-3-small_512',
|
|
171
|
+
),
|
|
172
|
+
)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('should throw for namespace with forward slash', () => {
|
|
176
|
+
expect(() => getNamespaceDir('/project', 'invalid/namespace')).toThrow(
|
|
177
|
+
'Invalid namespace: contains path separators or traversal sequences',
|
|
178
|
+
)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should throw for namespace with backslash', () => {
|
|
182
|
+
expect(() => getNamespaceDir('/project', 'invalid\\namespace')).toThrow(
|
|
183
|
+
'Invalid namespace: contains path separators or traversal sequences',
|
|
184
|
+
)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should throw for namespace with path traversal', () => {
|
|
188
|
+
expect(() => getNamespaceDir('/project', '../../../etc')).toThrow(
|
|
189
|
+
'Invalid namespace: contains path separators or traversal sequences',
|
|
190
|
+
)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('should throw for namespace with double dots', () => {
|
|
194
|
+
expect(() => getNamespaceDir('/project', 'valid..name')).toThrow(
|
|
195
|
+
'Invalid namespace: contains path separators or traversal sequences',
|
|
196
|
+
)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('should throw for namespace with null byte', () => {
|
|
200
|
+
expect(() => getNamespaceDir('/project', 'namespace\0poisoned')).toThrow(
|
|
201
|
+
'Invalid namespace: contains path separators or traversal sequences',
|
|
202
|
+
)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('should allow valid namespace with hyphens and underscores', () => {
|
|
206
|
+
const result = getNamespaceDir(
|
|
207
|
+
'/project',
|
|
208
|
+
'openai_text-embedding-3-small_512',
|
|
209
|
+
)
|
|
210
|
+
expect(result).toContain('openai_text-embedding-3-small_512')
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe('getVectorPath', () => {
|
|
215
|
+
it('should return correct vector file path', () => {
|
|
216
|
+
const result = getVectorPath(
|
|
217
|
+
'/project',
|
|
218
|
+
'openai_text-embedding-3-small_512',
|
|
219
|
+
)
|
|
220
|
+
expect(result).toBe(
|
|
221
|
+
path.join(
|
|
222
|
+
'/project',
|
|
223
|
+
'.mdcontext',
|
|
224
|
+
'embeddings',
|
|
225
|
+
'openai_text-embedding-3-small_512',
|
|
226
|
+
'vectors.bin',
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('should throw for invalid namespace', () => {
|
|
232
|
+
expect(() => getVectorPath('/project', '../escape')).toThrow()
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
describe('getMetaPath', () => {
|
|
237
|
+
it('should return correct metadata file path', () => {
|
|
238
|
+
const result = getMetaPath(
|
|
239
|
+
'/project',
|
|
240
|
+
'openai_text-embedding-3-small_512',
|
|
241
|
+
)
|
|
242
|
+
expect(result).toBe(
|
|
243
|
+
path.join(
|
|
244
|
+
'/project',
|
|
245
|
+
'.mdcontext',
|
|
246
|
+
'embeddings',
|
|
247
|
+
'openai_text-embedding-3-small_512',
|
|
248
|
+
'vectors.meta.bin',
|
|
249
|
+
),
|
|
250
|
+
)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('should throw for invalid namespace', () => {
|
|
254
|
+
expect(() => getMetaPath('/project', 'invalid/path')).toThrow()
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
describe('generateNamespace and parseNamespace roundtrip', () => {
|
|
259
|
+
it('should roundtrip simple namespace', () => {
|
|
260
|
+
const original = {
|
|
261
|
+
provider: 'openai',
|
|
262
|
+
model: 'text-embedding',
|
|
263
|
+
dimensions: 512,
|
|
264
|
+
}
|
|
265
|
+
const namespace = generateNamespace(
|
|
266
|
+
original.provider,
|
|
267
|
+
original.model,
|
|
268
|
+
original.dimensions,
|
|
269
|
+
)
|
|
270
|
+
const parsed = parseNamespace(namespace)
|
|
271
|
+
expect(parsed).toEqual(original)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should roundtrip namespace with special characters (sanitized)', () => {
|
|
275
|
+
// Note: parseNamespace splits on first underscore for provider, last underscore for dimensions
|
|
276
|
+
// So 'my_provider_my_model_v1_1024' becomes:
|
|
277
|
+
// - provider: 'my' (first segment before first underscore)
|
|
278
|
+
// - model: 'provider_my_model_v1' (everything between first and last underscore)
|
|
279
|
+
// - dimensions: 1024 (after last underscore)
|
|
280
|
+
const namespace = generateNamespace('My Provider', 'my.model/v1', 1024)
|
|
281
|
+
expect(namespace).toBe('my_provider_my_model_v1_1024')
|
|
282
|
+
const parsed = parseNamespace(namespace)
|
|
283
|
+
expect(parsed).toEqual({
|
|
284
|
+
provider: 'my',
|
|
285
|
+
model: 'provider_my_model_v1',
|
|
286
|
+
dimensions: 1024,
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('should roundtrip voyage provider', () => {
|
|
291
|
+
const namespace = generateNamespace('voyage', 'voyage-3.5-lite', 1024)
|
|
292
|
+
const parsed = parseNamespace(namespace)
|
|
293
|
+
expect(parsed).toEqual({
|
|
294
|
+
provider: 'voyage',
|
|
295
|
+
model: 'voyage-3_5-lite',
|
|
296
|
+
dimensions: 1024,
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
})
|