mnemon-mcp 1.1.0 → 1.3.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/CHANGELOG.md +28 -1
- package/README.md +22 -17
- package/dist/date-extractor.d.ts +30 -0
- package/dist/date-extractor.d.ts.map +1 -0
- package/dist/date-extractor.js +185 -0
- package/dist/date-extractor.js.map +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +297 -239
- package/dist/db.js.map +1 -1
- package/dist/embedder.d.ts +1 -0
- package/dist/embedder.d.ts.map +1 -1
- package/dist/embedder.js.map +1 -1
- package/dist/import/config-loader.d.ts.map +1 -1
- package/dist/import/config-loader.js +7 -2
- package/dist/import/config-loader.js.map +1 -1
- package/dist/import/embed-backfill.d.ts +21 -0
- package/dist/import/embed-backfill.d.ts.map +1 -0
- package/dist/import/embed-backfill.js +187 -0
- package/dist/import/embed-backfill.js.map +1 -0
- package/dist/import/kb-import.d.ts.map +1 -1
- package/dist/import/kb-import.js +5 -2
- package/dist/import/kb-import.js.map +1 -1
- package/dist/import/md-parser.d.ts +2 -0
- package/dist/import/md-parser.d.ts.map +1 -1
- package/dist/import/md-parser.js +17 -0
- package/dist/import/md-parser.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +7 -2
- package/dist/server.js.map +1 -1
- package/dist/stemmer.d.ts +0 -11
- package/dist/stemmer.d.ts.map +1 -1
- package/dist/stemmer.js +28 -2
- package/dist/stemmer.js.map +1 -1
- package/dist/stop-words.d.ts.map +1 -1
- package/dist/stop-words.js +4 -0
- package/dist/stop-words.js.map +1 -1
- package/dist/tools/memory-add.d.ts.map +1 -1
- package/dist/tools/memory-add.js +10 -0
- package/dist/tools/memory-add.js.map +1 -1
- package/dist/tools/memory-export.js +2 -2
- package/dist/tools/memory-export.js.map +1 -1
- package/dist/tools/memory-inspect.js +1 -1
- package/dist/tools/memory-search.d.ts.map +1 -1
- package/dist/tools/memory-search.js +252 -68
- package/dist/tools/memory-search.js.map +1 -1
- package/dist/tools/session.js +1 -1
- package/dist/tools/session.js.map +1 -1
- package/dist/types.d.ts +8 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +1 -1
- package/dist/validation.js.map +1 -1
- package/dist/vector.d.ts.map +1 -1
- package/dist/vector.js +11 -9
- package/dist/vector.js.map +1 -1
- package/package.json +4 -2
- package/dist/.tsbuildinfo +0 -1
- package/dist/tools/style-extract.d.ts +0 -40
- package/dist/tools/style-extract.d.ts.map +0 -1
- package/dist/tools/style-extract.js +0 -43
- package/dist/tools/style-extract.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.0] - 2026-03-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Hybrid search** — FTS5 + vector via Reciprocal Rank Fusion (RRF, k=60). Auto-enabled when embedding provider is configured. L2 eval: 92.6/100 (up from 80.9 FTS-only)
|
|
14
|
+
- **Structured date extraction** — Russian natural language dates ("3 марта 2026", "в феврале–марте 2025") auto-parsed into SQL WHERE filters with query term stripping
|
|
15
|
+
- **Embedding backfill CLI** — `npm run embed:backfill` batch-embeds active memories via configured provider (OpenAI/Ollama). Supports `--force`, `--batch-size`, text truncation for token limits
|
|
16
|
+
- **Section-level `event_at` extraction** — import pipeline now extracts dates from H2/H3 section titles, not just filenames
|
|
17
|
+
- **Cross-reference query expansion** — queries mentioning entities expand to include related terms for better recall
|
|
18
|
+
- **Bilingual month expansion** — EN↔RU month names added at both index and query time ("march" matches "марта" and vice versa)
|
|
19
|
+
- **Document-type stop words** — "дневник", "журнал", "запись" forms filtered from FTS queries to reduce noise
|
|
20
|
+
- `source_file` field in `memory_search` response — enables eval file matching and provenance tracking
|
|
21
|
+
- `embedding_model` column (migration v6) — tracks which model embedded each memory
|
|
22
|
+
- 33 new tests: 6 md-parser + 27 date-extractor + 2 integration (229 total)
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- **BM25 score inversion** — `1/(1+|rank|)` inverted relevance ordering; corrected to `|rank|/(1+|rank|)` (+9 L2 points)
|
|
26
|
+
- **Single-quote FTS5 crash** — queries with apostrophes now escaped before MATCH
|
|
27
|
+
- **ё→е normalization** — "ёлка" now matches "елка" in both indexing and search
|
|
28
|
+
- **AND relaxation tuning** — relaxed from top-3 stems at 4+ tokens to top-2 at 3+, improving recall on short queries
|
|
29
|
+
- **session_id validation** — `memory_add` now verifies session_id FK exists before insert
|
|
30
|
+
- **Error sanitization** — tool errors return generic messages to MCP clients; full stack traces logged to stderr only
|
|
31
|
+
- **Date validation** — `isoDatePrefix` now rejects structurally invalid dates (month 13, day 00) via `Date.parse` refine
|
|
32
|
+
- **`search_log` retention** — probabilistic pruning removes entries older than 90 days (~1% of writes)
|
|
33
|
+
- Removed stale `dist/tools/style-extract.*` build artifacts from npm package
|
|
34
|
+
- Excluded `dist/.tsbuildinfo` (79 KB) from npm package
|
|
35
|
+
|
|
10
36
|
## [1.1.0] - 2026-03-17
|
|
11
37
|
|
|
12
38
|
### Added
|
|
@@ -56,7 +82,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
56
82
|
- 182 tests (unit + integration + validation)
|
|
57
83
|
- CI pipeline with build, test, and smoke tests
|
|
58
84
|
|
|
59
|
-
[Unreleased]: https://github.com/nikitacometa/mnemon-mcp/compare/v1.
|
|
85
|
+
[Unreleased]: https://github.com/nikitacometa/mnemon-mcp/compare/v1.2.0...HEAD
|
|
86
|
+
[1.2.0]: https://github.com/nikitacometa/mnemon-mcp/compare/v1.1.0...v1.2.0
|
|
60
87
|
[1.1.0]: https://github.com/nikitacometa/mnemon-mcp/compare/v1.0.1...v1.1.0
|
|
61
88
|
[1.0.1]: https://github.com/nikitacometa/mnemon-mcp/compare/v1.0.0...v1.0.1
|
|
62
89
|
[1.0.0]: https://github.com/nikitacometa/mnemon-mcp/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -169,16 +169,20 @@ That's it. Your agent now has persistent memory.
|
|
|
169
169
|
|
|
170
170
|
## Search
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
Four modes, all supporting layer / entity / scope / date / confidence filters:
|
|
173
173
|
|
|
174
|
-
**FTS mode** (default) — tokenized full-text search with BM25 ranking. Multi-word queries use AND; if too few results, OR supplements with a score penalty. Progressive AND relaxation tries top-3 most specific terms before falling back to full OR.
|
|
174
|
+
**FTS mode** (default without embeddings) — tokenized full-text search with BM25 ranking. Multi-word queries use AND; if too few results, OR supplements with a score penalty. Progressive AND relaxation tries top-3 most specific terms before falling back to full OR.
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
**Hybrid mode** (default when embeddings configured) — combines FTS5 + vector search via [Reciprocal Rank Fusion](https://www.singlestore.com/blog/hybrid-search-using-reciprocal-rank-fusion-in-sql/). Detects quoted entities in queries (e.g., `'Essentialism'`) and runs weighted sub-queries for cross-reference retrieval.
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
**Vector mode** — pure cosine similarity search over embeddings.
|
|
179
179
|
|
|
180
180
|
**Exact mode** — `LIKE` substring match for precise phrase lookups.
|
|
181
181
|
|
|
182
|
+
Scores: `bm25 × (0.3 + 0.7 × importance) × decay(layer) × recency`
|
|
183
|
+
|
|
184
|
+
Recency boost: `1 / (1 + daysSince / 365)` — gently rewards recently created memories without penalizing old ones.
|
|
185
|
+
|
|
182
186
|
### Stemming
|
|
183
187
|
|
|
184
188
|
Snowball stemmer applied at both **index time** and **query time** for English and Russian. This means `"running"` matches `"runs"`, and `"книги"` matches `"книга"`. Stop words are filtered from queries to improve precision.
|
|
@@ -444,25 +448,26 @@ Returns: array of sessions with `id`, `client`, `project`, `started_at`, `ended_
|
|
|
444
448
|
|
|
445
449
|
## How It Compares
|
|
446
450
|
|
|
447
|
-
| | **mnemon-mcp** | mem0 | basic-memory | Anthropic KG |
|
|
448
|
-
|
|
449
|
-
| **Architecture** | SQLite FTS5 | Cloud API + Qdrant | Markdown + vector | JSON file |
|
|
450
|
-
| **Memory structure** | 4 typed layers | Flat | Flat | Graph |
|
|
451
|
-
| **
|
|
452
|
-
| **
|
|
453
|
-
| **
|
|
454
|
-
| **
|
|
455
|
-
| **
|
|
456
|
-
| **
|
|
457
|
-
| **
|
|
458
|
-
| **
|
|
451
|
+
| | **mnemon-mcp** | mem0 | basic-memory | Engram | Anthropic KG |
|
|
452
|
+
|---|---|---|---|---|---|
|
|
453
|
+
| **Architecture** | SQLite FTS5 + vector | Cloud API + Qdrant | Markdown + vector | SQLite FTS5 | JSON file |
|
|
454
|
+
| **Memory structure** | 4 typed layers | Flat | Flat | Flat + sessions | Graph |
|
|
455
|
+
| **Search** | FTS5 + hybrid RRF | Semantic | Hybrid | FTS5 | Exact |
|
|
456
|
+
| **Fact versioning** | Superseding chains | Partial | No | No | No |
|
|
457
|
+
| **Stemming** | EN + RU (Snowball) | EN only | EN only | None | None |
|
|
458
|
+
| **Embeddings** | BYOK (OpenAI / Ollama) | Built-in | FastEmbed | None | None |
|
|
459
|
+
| **Dependencies** | 0 required | Qdrant, Neo4j | Python 3.12 | Go binary | None |
|
|
460
|
+
| **Cloud required** | No | Yes | No | No | No |
|
|
461
|
+
| **Cost** | Free | $19–249/mo | Free | Free | Free |
|
|
462
|
+
| **Setup** | `npm install -g` | Docker + API keys | pip + deps | Go install | Built-in |
|
|
463
|
+
| **License** | MIT | Apache 2.0 | AGPL | MIT | MIT |
|
|
459
464
|
|
|
460
465
|
## Development
|
|
461
466
|
|
|
462
467
|
```bash
|
|
463
468
|
npm run dev # run via tsx (no build step)
|
|
464
469
|
npm run build # TypeScript → dist/
|
|
465
|
-
npm test # vitest (
|
|
470
|
+
npm test # vitest (229 tests)
|
|
466
471
|
npm run bench # performance benchmarks
|
|
467
472
|
npm run db:backup # backup database
|
|
468
473
|
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* date-extractor — Parse Russian natural language date patterns from query strings.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Exact date: "3 марта 2026" → date_from=2026-03-03, date_to=2026-03-03
|
|
6
|
+
* - Month range: "в феврале–марте 2025" → date_from=2025-02-01, date_to=2025-03-31
|
|
7
|
+
* - Single month: "в мае 2025" → date_from=2025-05-01, date_to=2025-05-31
|
|
8
|
+
*
|
|
9
|
+
* Year-only patterns ("2025 года") intentionally not supported — too aggressive,
|
|
10
|
+
* causes regressions on non-temporal queries that mention years contextually.
|
|
11
|
+
*
|
|
12
|
+
* Returns a cleaned query with all matched date tokens stripped.
|
|
13
|
+
*/
|
|
14
|
+
export interface ExtractedDates {
|
|
15
|
+
date_from: string | null;
|
|
16
|
+
date_to: string | null;
|
|
17
|
+
cleanedQuery: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse Russian date expressions from a natural language query.
|
|
21
|
+
*
|
|
22
|
+
* Patterns (checked in order of specificity):
|
|
23
|
+
* 1. Exact date: <1-31> <month_gen> <year>
|
|
24
|
+
* 2. Month range: <month>[–-]<month> <year>
|
|
25
|
+
* 3. Single month: <month> <year>
|
|
26
|
+
*
|
|
27
|
+
* Matching is case-insensitive; ё is treated as е (same as FTS5 normalization).
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractDatesFromQuery(query: string): ExtractedDates;
|
|
30
|
+
//# sourceMappingURL=date-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-extractor.d.ts","sourceRoot":"","sources":["../src/date-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAiED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAmHnE"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* date-extractor — Parse Russian natural language date patterns from query strings.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - Exact date: "3 марта 2026" → date_from=2026-03-03, date_to=2026-03-03
|
|
6
|
+
* - Month range: "в феврале–марте 2025" → date_from=2025-02-01, date_to=2025-03-31
|
|
7
|
+
* - Single month: "в мае 2025" → date_from=2025-05-01, date_to=2025-05-31
|
|
8
|
+
*
|
|
9
|
+
* Year-only patterns ("2025 года") intentionally not supported — too aggressive,
|
|
10
|
+
* causes regressions on non-temporal queries that mention years contextually.
|
|
11
|
+
*
|
|
12
|
+
* Returns a cleaned query with all matched date tokens stripped.
|
|
13
|
+
*/
|
|
14
|
+
/** Month name → month number (1-based). All forms normalized to lowercase. */
|
|
15
|
+
const MONTH_MAP = {
|
|
16
|
+
// January
|
|
17
|
+
январь: 1, января: 1, январе: 1,
|
|
18
|
+
// February
|
|
19
|
+
февраль: 2, февраля: 2, феврале: 2,
|
|
20
|
+
// March
|
|
21
|
+
март: 3, марта: 3, марте: 3,
|
|
22
|
+
// April
|
|
23
|
+
апрель: 4, апреля: 4, апреле: 4,
|
|
24
|
+
// May
|
|
25
|
+
май: 5, мая: 5, мае: 5,
|
|
26
|
+
// June
|
|
27
|
+
июнь: 6, июня: 6, июне: 6,
|
|
28
|
+
// July
|
|
29
|
+
июль: 7, июля: 7, июле: 7,
|
|
30
|
+
// August
|
|
31
|
+
август: 8, августа: 8, августе: 8,
|
|
32
|
+
// September
|
|
33
|
+
сентябрь: 9, сентября: 9, сентябре: 9,
|
|
34
|
+
// October
|
|
35
|
+
октябрь: 10, октября: 10, октябре: 10,
|
|
36
|
+
// November
|
|
37
|
+
ноябрь: 11, ноября: 11, ноябре: 11,
|
|
38
|
+
// December
|
|
39
|
+
декабрь: 12, декабря: 12, декабре: 12,
|
|
40
|
+
};
|
|
41
|
+
/** Return true if year is a leap year. */
|
|
42
|
+
function isLeapYear(year) {
|
|
43
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
44
|
+
}
|
|
45
|
+
/** Return last day of month (1-based month). */
|
|
46
|
+
function lastDayOfMonth(month, year) {
|
|
47
|
+
switch (month) {
|
|
48
|
+
case 1:
|
|
49
|
+
case 3:
|
|
50
|
+
case 5:
|
|
51
|
+
case 7:
|
|
52
|
+
case 8:
|
|
53
|
+
case 10:
|
|
54
|
+
case 12:
|
|
55
|
+
return 31;
|
|
56
|
+
case 4:
|
|
57
|
+
case 6:
|
|
58
|
+
case 9:
|
|
59
|
+
case 11:
|
|
60
|
+
return 30;
|
|
61
|
+
case 2:
|
|
62
|
+
return isLeapYear(year) ? 29 : 28;
|
|
63
|
+
default:
|
|
64
|
+
return 30;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Format date parts to ISO string "YYYY-MM-DD". */
|
|
68
|
+
function toIso(year, month, day) {
|
|
69
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build regex source string for matching any month name.
|
|
73
|
+
* Sorted by length descending to ensure longer forms match first.
|
|
74
|
+
*/
|
|
75
|
+
function buildMonthPattern() {
|
|
76
|
+
const forms = Object.keys(MONTH_MAP).sort((a, b) => b.length - a.length);
|
|
77
|
+
return forms.join("|");
|
|
78
|
+
}
|
|
79
|
+
const MONTH_PATTERN = buildMonthPattern();
|
|
80
|
+
/**
|
|
81
|
+
* Parse Russian date expressions from a natural language query.
|
|
82
|
+
*
|
|
83
|
+
* Patterns (checked in order of specificity):
|
|
84
|
+
* 1. Exact date: <1-31> <month_gen> <year>
|
|
85
|
+
* 2. Month range: <month>[–-]<month> <year>
|
|
86
|
+
* 3. Single month: <month> <year>
|
|
87
|
+
*
|
|
88
|
+
* Matching is case-insensitive; ё is treated as е (same as FTS5 normalization).
|
|
89
|
+
*/
|
|
90
|
+
export function extractDatesFromQuery(query) {
|
|
91
|
+
// Normalize ё→е for matching, keep same string length so character offsets stay valid.
|
|
92
|
+
const norm = query.toLowerCase().replace(/ё/g, "е");
|
|
93
|
+
let date_from = null;
|
|
94
|
+
let date_to = null;
|
|
95
|
+
const stripRanges = [];
|
|
96
|
+
let matched = false;
|
|
97
|
+
let m;
|
|
98
|
+
// Pattern 1: Exact date — "3 марта 2026" or "3 марта 2026 года"
|
|
99
|
+
const exactPattern = new RegExp(`(\\d{1,2})\\s+(${MONTH_PATTERN})\\s+(\\d{4})(?:\\s+года?(?:у)?)?`, "gi");
|
|
100
|
+
exactPattern.lastIndex = 0;
|
|
101
|
+
while ((m = exactPattern.exec(norm)) !== null) {
|
|
102
|
+
const day = parseInt(m[1], 10);
|
|
103
|
+
const monthNum = MONTH_MAP[m[2]];
|
|
104
|
+
const year = parseInt(m[3], 10);
|
|
105
|
+
if (monthNum && day >= 1 && day <= 31 && year >= 1900 && year <= 2099) {
|
|
106
|
+
date_from = toIso(year, monthNum, day);
|
|
107
|
+
date_to = toIso(year, monthNum, day);
|
|
108
|
+
stripRanges.push([m.index, m.index + m[0].length]);
|
|
109
|
+
matched = true;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!matched) {
|
|
114
|
+
// Pattern 2: Month range — "феврале–марте 2025" or "феврале-марте 2025"
|
|
115
|
+
// Dash variants: hyphen (-), en-dash (–, \u2013), em-dash (—, \u2014), figure dash (\u2012)
|
|
116
|
+
const rangePattern = new RegExp(`(${MONTH_PATTERN})\\s*[\\-\\u2012\\u2013\\u2014]\\s*(${MONTH_PATTERN})\\s+(\\d{4})(?:\\s+года?(?:у)?)?`, "gi");
|
|
117
|
+
rangePattern.lastIndex = 0;
|
|
118
|
+
m = rangePattern.exec(norm);
|
|
119
|
+
if (m) {
|
|
120
|
+
const month1 = MONTH_MAP[m[1]];
|
|
121
|
+
const month2 = MONTH_MAP[m[2]];
|
|
122
|
+
const year = parseInt(m[3], 10);
|
|
123
|
+
if (month1 && month2 && year >= 1900 && year <= 2099) {
|
|
124
|
+
const fromMonth = Math.min(month1, month2);
|
|
125
|
+
const toMonth = Math.max(month1, month2);
|
|
126
|
+
date_from = toIso(year, fromMonth, 1);
|
|
127
|
+
date_to = toIso(year, toMonth, lastDayOfMonth(toMonth, year));
|
|
128
|
+
stripRanges.push([m.index, m.index + m[0].length]);
|
|
129
|
+
matched = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!matched) {
|
|
134
|
+
// Pattern 3: Single month + year — "в мае 2025"
|
|
135
|
+
const singlePattern = new RegExp(`(${MONTH_PATTERN})\\s+(\\d{4})(?:\\s+года?(?:у)?)?`, "gi");
|
|
136
|
+
singlePattern.lastIndex = 0;
|
|
137
|
+
m = singlePattern.exec(norm);
|
|
138
|
+
if (m) {
|
|
139
|
+
const monthNum = MONTH_MAP[m[1]];
|
|
140
|
+
const year = parseInt(m[2], 10);
|
|
141
|
+
if (monthNum && year >= 1900 && year <= 2099) {
|
|
142
|
+
date_from = toIso(year, monthNum, 1);
|
|
143
|
+
date_to = toIso(year, monthNum, lastDayOfMonth(monthNum, year));
|
|
144
|
+
stripRanges.push([m.index, m.index + m[0].length]);
|
|
145
|
+
matched = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Pattern 4 (year only) intentionally omitted — too aggressive.
|
|
150
|
+
// "цели на 2025 год" is not a temporal query, it's a topic query mentioning a year.
|
|
151
|
+
// Patterns 1–3 (exact date, month range, single month) are precise enough.
|
|
152
|
+
if (!matched) {
|
|
153
|
+
return { date_from: null, date_to: null, cleanedQuery: query };
|
|
154
|
+
}
|
|
155
|
+
// Strip matched date tokens from the original query.
|
|
156
|
+
// Before stripping, expand each range to absorb a leading "в/во " preposition
|
|
157
|
+
// directly before the date token (common in Russian: "в феврале–марте 2025").
|
|
158
|
+
// Use the normalized string to detect the preposition, but strip from original.
|
|
159
|
+
const expandedRanges = stripRanges.map(([start, end]) => {
|
|
160
|
+
// Look backward in norm for optional whitespace + "в" or "во" + whitespace
|
|
161
|
+
const prefix = norm.slice(0, start);
|
|
162
|
+
const prepMatch = /(?:^|(?<=\s))во?\s+$/.exec(prefix);
|
|
163
|
+
if (prepMatch) {
|
|
164
|
+
return [start - prepMatch[0].length, end];
|
|
165
|
+
}
|
|
166
|
+
return [start, end];
|
|
167
|
+
});
|
|
168
|
+
// Apply in reverse order so earlier offsets stay valid
|
|
169
|
+
let cleaned = query;
|
|
170
|
+
const sorted = expandedRanges.slice().sort((a, b) => b[0] - a[0]);
|
|
171
|
+
for (const [start, end] of sorted) {
|
|
172
|
+
cleaned = cleaned.slice(0, start) + " " + cleaned.slice(end);
|
|
173
|
+
}
|
|
174
|
+
// Word boundary pattern for Cyrillic + ASCII (JS \b doesn't work with Cyrillic)
|
|
175
|
+
const wb = "(?<![а-яёА-ЯЁa-zA-Z\\d])";
|
|
176
|
+
const we = "(?![а-яёА-ЯЁa-zA-Z\\d])";
|
|
177
|
+
// Strip residual year words not captured inside the pattern match
|
|
178
|
+
cleaned = cleaned.replace(new RegExp(`${wb}года?${we}`, "gi"), " ");
|
|
179
|
+
cleaned = cleaned.replace(new RegExp(`${wb}году${we}`, "gi"), " ");
|
|
180
|
+
// Collapse whitespace and trim surrounding punctuation
|
|
181
|
+
cleaned = cleaned.replace(/\s{2,}/g, " ").trim();
|
|
182
|
+
cleaned = cleaned.replace(/^[?!.,;:\s—–\-]+|[?!.,;:\s—–\-]+$/g, "").trim();
|
|
183
|
+
return { date_from, date_to, cleanedQuery: cleaned };
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=date-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-extractor.js","sourceRoot":"","sources":["../src/date-extractor.ts"],"names":[],"mappings":"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"}
|
package/dist/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAOtC,QAAA,MAAM,OAAO,QAA6D,CAAC;AAK3E;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,MAAgB,GAAG,QAAQ,CAAC,QAAQ,CAgBxE;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAOtC,QAAA,MAAM,OAAO,QAA6D,CAAC;AAK3E;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,MAAgB,GAAG,QAAQ,CAAC,QAAQ,CAgBxE;AA6bD,OAAO,EAAE,OAAO,EAAE,CAAC"}
|