@monoes/monograph 1.6.11 → 1.6.12

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.
Files changed (68) hide show
  1. package/dist/src/pipeline/phases/orm-helpers.d.ts +11 -0
  2. package/dist/src/pipeline/phases/orm-helpers.d.ts.map +1 -0
  3. package/dist/src/pipeline/phases/orm-helpers.js +72 -0
  4. package/dist/src/pipeline/phases/orm-helpers.js.map +1 -0
  5. package/dist/src/pipeline/phases/orm-mongoose.d.ts +8 -0
  6. package/dist/src/pipeline/phases/orm-mongoose.d.ts.map +1 -0
  7. package/dist/src/pipeline/phases/orm-mongoose.js +51 -0
  8. package/dist/src/pipeline/phases/orm-mongoose.js.map +1 -0
  9. package/dist/src/pipeline/phases/orm-prisma.d.ts +8 -0
  10. package/dist/src/pipeline/phases/orm-prisma.d.ts.map +1 -0
  11. package/dist/src/pipeline/phases/orm-prisma.js +41 -0
  12. package/dist/src/pipeline/phases/orm-prisma.js.map +1 -0
  13. package/dist/src/pipeline/phases/orm-sequelize.d.ts +12 -0
  14. package/dist/src/pipeline/phases/orm-sequelize.d.ts.map +1 -0
  15. package/dist/src/pipeline/phases/orm-sequelize.js +97 -0
  16. package/dist/src/pipeline/phases/orm-sequelize.js.map +1 -0
  17. package/dist/src/pipeline/phases/orm-sqlalchemy.d.ts +8 -0
  18. package/dist/src/pipeline/phases/orm-sqlalchemy.d.ts.map +1 -0
  19. package/dist/src/pipeline/phases/orm-sqlalchemy.js +42 -0
  20. package/dist/src/pipeline/phases/orm-sqlalchemy.js.map +1 -0
  21. package/dist/src/pipeline/phases/orm-typeorm.d.ts +10 -0
  22. package/dist/src/pipeline/phases/orm-typeorm.d.ts.map +1 -0
  23. package/dist/src/pipeline/phases/orm-typeorm.js +52 -0
  24. package/dist/src/pipeline/phases/orm-typeorm.js.map +1 -0
  25. package/dist/src/pipeline/phases/orm.d.ts.map +1 -1
  26. package/dist/src/pipeline/phases/orm.js +6 -336
  27. package/dist/src/pipeline/phases/orm.js.map +1 -1
  28. package/dist/src/pipeline/phases/scope-resolution-edges.d.ts +9 -0
  29. package/dist/src/pipeline/phases/scope-resolution-edges.d.ts.map +1 -0
  30. package/dist/src/pipeline/phases/scope-resolution-edges.js +30 -0
  31. package/dist/src/pipeline/phases/scope-resolution-edges.js.map +1 -0
  32. package/dist/src/pipeline/phases/scope-resolution-function-index.d.ts +31 -0
  33. package/dist/src/pipeline/phases/scope-resolution-function-index.d.ts.map +1 -0
  34. package/dist/src/pipeline/phases/scope-resolution-function-index.js +96 -0
  35. package/dist/src/pipeline/phases/scope-resolution-function-index.js.map +1 -0
  36. package/dist/src/pipeline/phases/scope-resolution-target.d.ts +6 -0
  37. package/dist/src/pipeline/phases/scope-resolution-target.d.ts.map +1 -0
  38. package/dist/src/pipeline/phases/scope-resolution-target.js +69 -0
  39. package/dist/src/pipeline/phases/scope-resolution-target.js.map +1 -0
  40. package/dist/src/pipeline/phases/scope-resolution.d.ts.map +1 -1
  41. package/dist/src/pipeline/phases/scope-resolution.js +3 -189
  42. package/dist/src/pipeline/phases/scope-resolution.js.map +1 -1
  43. package/dist/src/storage/fts-store-hybrid.d.ts +17 -0
  44. package/dist/src/storage/fts-store-hybrid.d.ts.map +1 -0
  45. package/dist/src/storage/fts-store-hybrid.js +119 -0
  46. package/dist/src/storage/fts-store-hybrid.js.map +1 -0
  47. package/dist/src/storage/fts-store-search.d.ts +29 -0
  48. package/dist/src/storage/fts-store-search.d.ts.map +1 -0
  49. package/dist/src/storage/fts-store-search.js +177 -0
  50. package/dist/src/storage/fts-store-search.js.map +1 -0
  51. package/dist/src/storage/fts-store-terms.d.ts +8 -0
  52. package/dist/src/storage/fts-store-terms.d.ts.map +1 -0
  53. package/dist/src/storage/fts-store-terms.js +174 -0
  54. package/dist/src/storage/fts-store-terms.js.map +1 -0
  55. package/dist/src/storage/fts-store.d.ts +12 -46
  56. package/dist/src/storage/fts-store.d.ts.map +1 -1
  57. package/dist/src/storage/fts-store.js +11 -459
  58. package/dist/src/storage/fts-store.js.map +1 -1
  59. package/dist/src/web/api-queries.d.ts +89 -0
  60. package/dist/src/web/api-queries.d.ts.map +1 -0
  61. package/dist/src/web/api-queries.js +245 -0
  62. package/dist/src/web/api-queries.js.map +1 -0
  63. package/dist/src/web/api.d.ts +2 -87
  64. package/dist/src/web/api.d.ts.map +1 -1
  65. package/dist/src/web/api.js +3 -244
  66. package/dist/src/web/api.js.map +1 -1
  67. package/dist/tsconfig.tsbuildinfo +1 -1
  68. package/package.json +1 -1
@@ -0,0 +1,177 @@
1
+ // File-size sweep: split out of fts-store.ts.
2
+ import { extractSearchTerms } from './fts-store-terms.js';
3
+ /**
4
+ * THE single conversion from FTS5's `rank` to monograph's score convention.
5
+ *
6
+ * SCORE CONVENTION: every score that leaves the search layer is
7
+ * **higher-is-better and non-negative**. SQLite FTS5's `rank` is the opposite
8
+ * (negative, and *more* negative means a *better* match), so a raw rank must
9
+ * never escape this module — forwarding one into a ranker that takes maxima
10
+ * lets an unrelated node sitting at 0 outrank a genuine match at -0.656.
11
+ *
12
+ * Maps |rank| through x/(1+x) into (0, 1): monotonically increasing, so
13
+ * relative BM25 order is preserved, and bounded, so it composes with the
14
+ * additive fuzzy/node-type bonuses without one term swamping the others.
15
+ */
16
+ export function relevanceFromFtsRank(rank) {
17
+ const magnitude = Math.abs(rank);
18
+ return magnitude / (1 + magnitude);
19
+ }
20
+ /**
21
+ * Quote a single FTS5 search term as a string literal when it contains characters
22
+ * that would otherwise be parsed as FTS5 syntax (quotes, parens, boolean keywords
23
+ * like AND/OR/NOT, colons, hyphens, etc). Wrapping in double-quotes forces FTS5 to
24
+ * treat the term as a literal string; any literal `"` inside the term is escaped by
25
+ * doubling it, per FTS5's own string-literal quoting rules.
26
+ */
27
+ function quoteFtsTerm(term) {
28
+ // Bare alphanumeric/underscore terms need no quoting and are the common case.
29
+ if (/^[A-Za-z0-9_]+$/.test(term))
30
+ return term;
31
+ const escaped = term.replace(/"/g, '""');
32
+ return `"${escaped}"`;
33
+ }
34
+ export function ftsSearch(db, query, limit, label) {
35
+ // Sanitize: strip only characters that break FTS5 MATCH syntax (* still stripped,
36
+ // trigram handles substring natively; " is now preserved since trigram doesn't need it removed)
37
+ const safeQuery = query.replace(/[*]/g, ' ').trim();
38
+ if (!safeQuery)
39
+ return [];
40
+ // Trigram handles substring matching natively — no need to append * to each term.
41
+ // Quote each term individually so characters like `"`, `(`, `)`, or bare boolean
42
+ // keywords (AND/OR/NOT) don't get parsed as FTS5 query syntax.
43
+ const ftsQuery = safeQuery.split(/\s+/).map(quoteFtsTerm).join(' ');
44
+ let matchSql = `
45
+ SELECT n.id, n.name, n.norm_label, n.file_path, n.label,
46
+ n.start_line, n.end_line, nodes_fts.rank
47
+ FROM nodes_fts
48
+ JOIN nodes n ON n.rowid = nodes_fts.rowid
49
+ WHERE nodes_fts MATCH ?
50
+ `;
51
+ const matchParams = [ftsQuery];
52
+ if (label) {
53
+ matchSql += ' AND n.label = ?';
54
+ matchParams.push(label);
55
+ }
56
+ matchSql += ' ORDER BY nodes_fts.rank LIMIT ?';
57
+ matchParams.push(limit);
58
+ let matchRows = [];
59
+ try {
60
+ matchRows = db.prepare(matchSql).all(...matchParams);
61
+ }
62
+ catch {
63
+ // FTS MATCH can throw on malformed queries; fall through to LIKE path
64
+ }
65
+ // ── Intent-extraction fallback: when the raw query looks like natural language
66
+ // and FTS returned nothing, extract code-relevant tokens and retry FTS. ──────
67
+ if (matchRows.length === 0) {
68
+ const extracted = extractSearchTerms(safeQuery);
69
+ if (extracted.length > 0) {
70
+ // Try each extracted term individually via FTS, then merge by best rank
71
+ const termFtsQuery = extracted.map(quoteFtsTerm).join(' OR ');
72
+ let termSql = `
73
+ SELECT n.id, n.name, n.norm_label, n.file_path, n.label,
74
+ n.start_line, n.end_line, nodes_fts.rank
75
+ FROM nodes_fts
76
+ JOIN nodes n ON n.rowid = nodes_fts.rowid
77
+ WHERE nodes_fts MATCH ?
78
+ `;
79
+ const termParams = [termFtsQuery];
80
+ if (label) {
81
+ termSql += ' AND n.label = ?';
82
+ termParams.push(label);
83
+ }
84
+ termSql += ' ORDER BY nodes_fts.rank LIMIT ?';
85
+ termParams.push(limit * 2);
86
+ try {
87
+ matchRows = db.prepare(termSql).all(...termParams);
88
+ }
89
+ catch {
90
+ /* FTS MATCH can throw on malformed queries */
91
+ }
92
+ // Also try individual LIKE for each extracted term to catch camelCase substrings
93
+ if (matchRows.length < limit) {
94
+ const seenIds = new Set(matchRows.map((r) => r.id));
95
+ for (const term of extracted) {
96
+ if (matchRows.length >= limit)
97
+ break;
98
+ const escapedTerm = term.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_');
99
+ const pat = `%${escapedTerm}%`;
100
+ let termLikeSql = `
101
+ SELECT n.id, n.name, n.norm_label, n.file_path, n.label,
102
+ n.start_line, n.end_line, 0 AS rank
103
+ FROM nodes n
104
+ WHERE (n.name LIKE ? ESCAPE '\\' OR n.norm_label LIKE ? ESCAPE '\\')
105
+ `;
106
+ const termLikeParams = [pat, pat];
107
+ if (label) {
108
+ termLikeSql += ' AND n.label = ?';
109
+ termLikeParams.push(label);
110
+ }
111
+ termLikeSql += ' LIMIT ?';
112
+ termLikeParams.push(limit);
113
+ const likeRows = db
114
+ .prepare(termLikeSql)
115
+ .all(...termLikeParams);
116
+ for (const r of likeRows) {
117
+ if (!seenIds.has(r.id)) {
118
+ matchRows.push(r);
119
+ seenIds.add(r.id);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ // Run the LIKE fallback whenever MATCH threw (malformed/boolean-keyword query) or
127
+ // returned zero rows — not just for short (≤2 char) queries. Short queries need it
128
+ // because trigram requires ≥3 characters to fire; longer queries need it whenever
129
+ // MATCH couldn't handle the syntax, so a query-syntax failure doesn't silently read
130
+ // as "no results".
131
+ if (safeQuery.length <= 2 || matchRows.length === 0) {
132
+ const escapedQuery = safeQuery.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_');
133
+ const likePattern = `%${escapedQuery}%`;
134
+ // Also try without file extension: "CLAUDE.md" → "CLAUDE"
135
+ const dotIdx = safeQuery.lastIndexOf('.');
136
+ const strippedLikePattern = dotIdx > 0 && dotIdx < safeQuery.length - 1
137
+ ? `%${safeQuery.slice(0, dotIdx).replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_')}%`
138
+ : null;
139
+ let likeSql = `
140
+ SELECT n.id, n.name, n.norm_label, n.file_path, n.label,
141
+ n.start_line, n.end_line, 0 AS rank
142
+ FROM nodes n
143
+ WHERE (n.name LIKE ? ESCAPE '\\' OR n.norm_label LIKE ? ESCAPE '\\' OR n.file_path LIKE ? ESCAPE '\\'`;
144
+ const likeParams = [likePattern, likePattern, likePattern];
145
+ if (strippedLikePattern) {
146
+ likeSql += ` OR n.name LIKE ? ESCAPE '\\'`;
147
+ likeParams.push(strippedLikePattern);
148
+ }
149
+ likeSql += ')';
150
+ if (label) {
151
+ likeSql += ' AND n.label = ?';
152
+ likeParams.push(label);
153
+ }
154
+ likeSql += ' LIMIT ?';
155
+ likeParams.push(limit);
156
+ const likeRows = db.prepare(likeSql).all(...likeParams);
157
+ // Merge: MATCH results first, append LIKE results not already present
158
+ const seenIds = new Set(matchRows.map((r) => r.id));
159
+ for (const r of likeRows) {
160
+ if (!seenIds.has(r.id)) {
161
+ matchRows.push(r);
162
+ seenIds.add(r.id);
163
+ }
164
+ }
165
+ }
166
+ return matchRows.slice(0, limit).map((r) => ({
167
+ id: r.id,
168
+ name: r.name,
169
+ normLabel: r.norm_label,
170
+ filePath: r.file_path ?? null,
171
+ label: r.label,
172
+ rank: r.rank,
173
+ startLine: r.start_line ?? null,
174
+ endLine: r.end_line ?? null,
175
+ }));
176
+ }
177
+ //# sourceMappingURL=fts-store-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts-store-search.js","sourceRoot":"","sources":["../../../src/storage/fts-store-search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Extract code-relevant search terms from a natural-language query.
3
+ * Strips stopwords, splits camelCase/PascalCase/snake_case, and strips
4
+ * file extensions (so "CLAUDE.md" also tries "CLAUDE").
5
+ * Returns an empty array when the query is already a bare identifier.
6
+ */
7
+ export declare function extractSearchTerms(query: string): string[];
8
+ //# sourceMappingURL=fts-store-terms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts-store-terms.d.ts","sourceRoot":"","sources":["../../../src/storage/fts-store-terms.ts"],"names":[],"mappings":"AAkIA;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAuC1D"}
@@ -0,0 +1,174 @@
1
+ // ── Intent-to-identifier extraction ──────────────────────────────────────────
2
+ // File-size sweep: split out of fts-store.ts.
3
+ const STOPWORDS = new Set([
4
+ 'a',
5
+ 'an',
6
+ 'the',
7
+ 'is',
8
+ 'are',
9
+ 'was',
10
+ 'were',
11
+ 'be',
12
+ 'been',
13
+ 'being',
14
+ 'have',
15
+ 'has',
16
+ 'had',
17
+ 'do',
18
+ 'does',
19
+ 'did',
20
+ 'will',
21
+ 'would',
22
+ 'shall',
23
+ 'should',
24
+ 'may',
25
+ 'might',
26
+ 'must',
27
+ 'can',
28
+ 'could',
29
+ 'am',
30
+ 'it',
31
+ 'its',
32
+ 'i',
33
+ 'me',
34
+ 'my',
35
+ 'we',
36
+ 'our',
37
+ 'you',
38
+ 'your',
39
+ 'he',
40
+ 'she',
41
+ 'they',
42
+ 'them',
43
+ 'their',
44
+ 'this',
45
+ 'that',
46
+ 'these',
47
+ 'those',
48
+ 'of',
49
+ 'in',
50
+ 'to',
51
+ 'for',
52
+ 'with',
53
+ 'on',
54
+ 'at',
55
+ 'from',
56
+ 'by',
57
+ 'about',
58
+ 'as',
59
+ 'into',
60
+ 'through',
61
+ 'during',
62
+ 'before',
63
+ 'after',
64
+ 'above',
65
+ 'below',
66
+ 'between',
67
+ 'out',
68
+ 'off',
69
+ 'over',
70
+ 'under',
71
+ 'again',
72
+ 'then',
73
+ 'once',
74
+ 'here',
75
+ 'there',
76
+ 'when',
77
+ 'where',
78
+ 'why',
79
+ 'how',
80
+ 'all',
81
+ 'both',
82
+ 'each',
83
+ 'every',
84
+ 'few',
85
+ 'more',
86
+ 'most',
87
+ 'other',
88
+ 'some',
89
+ 'such',
90
+ 'no',
91
+ 'nor',
92
+ 'not',
93
+ 'only',
94
+ 'own',
95
+ 'same',
96
+ 'so',
97
+ 'than',
98
+ 'too',
99
+ 'very',
100
+ 'just',
101
+ 'because',
102
+ 'but',
103
+ 'and',
104
+ 'or',
105
+ 'if',
106
+ 'while',
107
+ 'what',
108
+ 'which',
109
+ 'who',
110
+ 'whom',
111
+ 'up',
112
+ 'down',
113
+ 'get',
114
+ 'set',
115
+ 'add',
116
+ 'use',
117
+ 'find',
118
+ 'show',
119
+ 'make',
120
+ 'let',
121
+ 'want',
122
+ 'need',
123
+ 'like',
124
+ 'look',
125
+ 'also',
126
+ 'new',
127
+ 'old',
128
+ ]);
129
+ /**
130
+ * Extract code-relevant search terms from a natural-language query.
131
+ * Strips stopwords, splits camelCase/PascalCase/snake_case, and strips
132
+ * file extensions (so "CLAUDE.md" also tries "CLAUDE").
133
+ * Returns an empty array when the query is already a bare identifier.
134
+ */
135
+ export function extractSearchTerms(query) {
136
+ const words = query.split(/[\s,;:!?()[\]{}'"]+/).filter(Boolean);
137
+ // If query is a single token, it's already a bare identifier query — nothing to extract.
138
+ // (We used to also bail out for short, stopword-free multi-token queries under the
139
+ // assumption they were "already an identifier query", but that misfires for queries
140
+ // like "ExtensionBridge keepalive reconnect" — three distinct identifier-ish tokens
141
+ // that FTS5 MATCH ANDs together and fails to find as a single row. Since this function
142
+ // only runs as a fallback after the raw MATCH already returned zero rows, there's no
143
+ // extra cost to always extracting terms for multi-token queries.)
144
+ if (words.length <= 1)
145
+ return [];
146
+ const terms = new Set();
147
+ for (const word of words) {
148
+ const lower = word.toLowerCase();
149
+ if (STOPWORDS.has(lower))
150
+ continue;
151
+ if (word.length < 3)
152
+ continue;
153
+ // Strip file extensions: "CLAUDE.md" → also add "CLAUDE"
154
+ const dotIdx = word.lastIndexOf('.');
155
+ if (dotIdx > 0 && dotIdx < word.length - 1) {
156
+ terms.add(word.slice(0, dotIdx));
157
+ terms.add(word);
158
+ }
159
+ // Split camelCase/PascalCase: "ExtensionBridge" → "Extension", "Bridge"
160
+ const camelParts = word
161
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
162
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
163
+ .split(/[\s_-]+/)
164
+ .filter((p) => p.length >= 3 && !STOPWORDS.has(p.toLowerCase()));
165
+ if (camelParts.length > 1) {
166
+ for (const part of camelParts)
167
+ terms.add(part);
168
+ }
169
+ // Keep the original word
170
+ terms.add(word);
171
+ }
172
+ return Array.from(terms);
173
+ }
174
+ //# sourceMappingURL=fts-store-terms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts-store-terms.js","sourceRoot":"","sources":["../../../src/storage/fts-store-terms.ts"],"names":[],"mappings":"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"}
@@ -1,50 +1,16 @@
1
- import type Database from 'better-sqlite3';
2
1
  /**
3
- * Extract code-relevant search terms from a natural-language query.
4
- * Strips stopwords, splits camelCase/PascalCase/snake_case, and strips
5
- * file extensions (so "CLAUDE.md" also tries "CLAUDE").
6
- * Returns an empty array when the query is already a bare identifier.
7
- */
8
- export declare function extractSearchTerms(query: string): string[];
9
- export interface FtsResult {
10
- id: string;
11
- name: string;
12
- normLabel: string;
13
- filePath: string | null;
14
- label: string;
15
- rank: number;
16
- /** First line of the symbol in its source file (1-based, null if unknown). */
17
- startLine: number | null;
18
- /** Last line of the symbol in its source file (1-based, null if unknown). */
19
- endLine: number | null;
20
- }
21
- /**
22
- * THE single conversion from FTS5's `rank` to monograph's score convention.
23
- *
24
- * SCORE CONVENTION: every score that leaves the search layer is
25
- * **higher-is-better and non-negative**. SQLite FTS5's `rank` is the opposite
26
- * (negative, and *more* negative means a *better* match), so a raw rank must
27
- * never escape this module — forwarding one into a ranker that takes maxima
28
- * lets an unrelated node sitting at 0 outrank a genuine match at -0.656.
29
- *
30
- * Maps |rank| through x/(1+x) into (0, 1): monotonically increasing, so
31
- * relative BM25 order is preserved, and bounded, so it composes with the
32
- * additive fuzzy/node-type bonuses without one term swamping the others.
33
- */
34
- export declare function relevanceFromFtsRank(rank: number): number;
35
- export declare function ftsSearch(db: Database.Database, query: string, limit: number, label?: string): FtsResult[];
36
- export interface HybridSearchResult extends FtsResult {
37
- combinedScore: number;
38
- matchStrategy: 'fts' | 'like' | 'fuzzy';
39
- }
40
- /**
41
- * Hybrid search combining three strategies:
42
- * 1. FTS5 (trigram) BM25 match via `ftsSearch`
43
- * 2. LIKE fallback for short queries (≤3 chars) or when FTS returns 0 results
44
- * 3. In-memory fuzzy character-sequence scoring applied to all candidates
2
+ * FTS5 search, intent-extraction, and hybrid search over the monograph nodes
3
+ * table.
45
4
  *
46
- * Results are deduped by id (highest combinedScore wins), re-ranked, and
47
- * trimmed to `limit`. The existing `ftsSearch` is left unchanged.
5
+ * File-size sweep: split into sibling modules — stopwords + intent-to-
6
+ * identifier extraction in fts-store-terms.ts, the FTS5/LIKE search path in
7
+ * fts-store-search.ts, and hybrid (FTS + LIKE + fuzzy) search in
8
+ * fts-store-hybrid.ts. Re-exported here so every existing import path
9
+ * (`./fts-store.js`) keeps working unchanged.
48
10
  */
49
- export declare function hybridSearch(db: Database.Database, query: string, limit: number, label?: string): HybridSearchResult[];
11
+ export type { HybridSearchResult } from './fts-store-hybrid.js';
12
+ export { hybridSearch } from './fts-store-hybrid.js';
13
+ export type { FtsResult } from './fts-store-search.js';
14
+ export { ftsSearch, relevanceFromFtsRank } from './fts-store-search.js';
15
+ export { extractSearchTerms } from './fts-store-terms.js';
50
16
  //# sourceMappingURL=fts-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fts-store.d.ts","sourceRoot":"","sources":["../../../src/storage/fts-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAmI3C;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAuC1D;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzD;AAgBD,wBAAgB,SAAS,CACvB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,EAAE,CA+Jb;AAID,MAAM,WAAW,kBAAmB,SAAQ,SAAS;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CACzC;AAyCD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,kBAAkB,EAAE,CA0EtB"}
1
+ {"version":3,"file":"fts-store.d.ts","sourceRoot":"","sources":["../../../src/storage/fts-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}