abb-opencode-local-rag 0.1.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.
Files changed (233) hide show
  1. package/LICENSE +21 -0
  2. package/README.de.md +416 -0
  3. package/README.es.md +416 -0
  4. package/README.fr.md +416 -0
  5. package/README.md +491 -0
  6. package/README.pt-BR.md +416 -0
  7. package/README.zh-CN.md +416 -0
  8. package/dist/bin/install-skills.d.ts +20 -0
  9. package/dist/bin/install-skills.d.ts.map +1 -0
  10. package/dist/bin/install-skills.js +195 -0
  11. package/dist/bin/install-skills.js.map +1 -0
  12. package/dist/chunker/index.d.ts +24 -0
  13. package/dist/chunker/index.d.ts.map +1 -0
  14. package/dist/chunker/index.js +2 -0
  15. package/dist/chunker/index.js.map +1 -0
  16. package/dist/chunker/semantic-chunker.d.ts +97 -0
  17. package/dist/chunker/semantic-chunker.d.ts.map +1 -0
  18. package/dist/chunker/semantic-chunker.js +294 -0
  19. package/dist/chunker/semantic-chunker.js.map +1 -0
  20. package/dist/chunker/sentence-splitter.d.ts +28 -0
  21. package/dist/chunker/sentence-splitter.d.ts.map +1 -0
  22. package/dist/chunker/sentence-splitter.js +219 -0
  23. package/dist/chunker/sentence-splitter.js.map +1 -0
  24. package/dist/cli/common.d.ts +65 -0
  25. package/dist/cli/common.d.ts.map +1 -0
  26. package/dist/cli/common.js +138 -0
  27. package/dist/cli/common.js.map +1 -0
  28. package/dist/cli/delete.d.ts +8 -0
  29. package/dist/cli/delete.d.ts.map +1 -0
  30. package/dist/cli/delete.js +173 -0
  31. package/dist/cli/delete.js.map +1 -0
  32. package/dist/cli/file-collection.d.ts +2 -0
  33. package/dist/cli/file-collection.d.ts.map +1 -0
  34. package/dist/cli/file-collection.js +53 -0
  35. package/dist/cli/file-collection.js.map +1 -0
  36. package/dist/cli/ingest.d.ts +100 -0
  37. package/dist/cli/ingest.d.ts.map +1 -0
  38. package/dist/cli/ingest.js +363 -0
  39. package/dist/cli/ingest.js.map +1 -0
  40. package/dist/cli/list.d.ts +35 -0
  41. package/dist/cli/list.d.ts.map +1 -0
  42. package/dist/cli/list.js +210 -0
  43. package/dist/cli/list.js.map +1 -0
  44. package/dist/cli/options.d.ts +100 -0
  45. package/dist/cli/options.d.ts.map +1 -0
  46. package/dist/cli/options.js +241 -0
  47. package/dist/cli/options.js.map +1 -0
  48. package/dist/cli/query.d.ts +24 -0
  49. package/dist/cli/query.d.ts.map +1 -0
  50. package/dist/cli/query.js +191 -0
  51. package/dist/cli/query.js.map +1 -0
  52. package/dist/cli/read-neighbors.d.ts +11 -0
  53. package/dist/cli/read-neighbors.d.ts.map +1 -0
  54. package/dist/cli/read-neighbors.js +224 -0
  55. package/dist/cli/read-neighbors.js.map +1 -0
  56. package/dist/cli/status.d.ts +8 -0
  57. package/dist/cli/status.d.ts.map +1 -0
  58. package/dist/cli/status.js +80 -0
  59. package/dist/cli/status.js.map +1 -0
  60. package/dist/cli/sync.d.ts +8 -0
  61. package/dist/cli/sync.d.ts.map +1 -0
  62. package/dist/cli/sync.js +244 -0
  63. package/dist/cli/sync.js.map +1 -0
  64. package/dist/cli-main.d.ts +12 -0
  65. package/dist/cli-main.d.ts.map +1 -0
  66. package/dist/cli-main.js +63 -0
  67. package/dist/cli-main.js.map +1 -0
  68. package/dist/embedder/index.d.ts +85 -0
  69. package/dist/embedder/index.d.ts.map +1 -0
  70. package/dist/embedder/index.js +284 -0
  71. package/dist/embedder/index.js.map +1 -0
  72. package/dist/features/list.d.ts +37 -0
  73. package/dist/features/list.d.ts.map +1 -0
  74. package/dist/features/list.js +40 -0
  75. package/dist/features/list.js.map +1 -0
  76. package/dist/features/sync.d.ts +207 -0
  77. package/dist/features/sync.d.ts.map +1 -0
  78. package/dist/features/sync.js +380 -0
  79. package/dist/features/sync.js.map +1 -0
  80. package/dist/index.d.ts +3 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +53 -0
  83. package/dist/index.js.map +1 -0
  84. package/dist/ingest/compute.d.ts +86 -0
  85. package/dist/ingest/compute.d.ts.map +1 -0
  86. package/dist/ingest/compute.js +177 -0
  87. package/dist/ingest/compute.js.map +1 -0
  88. package/dist/ingest/file.d.ts +27 -0
  89. package/dist/ingest/file.d.ts.map +1 -0
  90. package/dist/ingest/file.js +67 -0
  91. package/dist/ingest/file.js.map +1 -0
  92. package/dist/ingest/visual.d.ts +45 -0
  93. package/dist/ingest/visual.d.ts.map +1 -0
  94. package/dist/ingest/visual.js +234 -0
  95. package/dist/ingest/visual.js.map +1 -0
  96. package/dist/parser/docx-parser.d.ts +12 -0
  97. package/dist/parser/docx-parser.d.ts.map +1 -0
  98. package/dist/parser/docx-parser.js +328 -0
  99. package/dist/parser/docx-parser.js.map +1 -0
  100. package/dist/parser/html-parser.d.ts +18 -0
  101. package/dist/parser/html-parser.d.ts.map +1 -0
  102. package/dist/parser/html-parser.js +102 -0
  103. package/dist/parser/html-parser.js.map +1 -0
  104. package/dist/parser/index.d.ts +214 -0
  105. package/dist/parser/index.d.ts.map +1 -0
  106. package/dist/parser/index.js +454 -0
  107. package/dist/parser/index.js.map +1 -0
  108. package/dist/parser/pdf-extract.d.ts +81 -0
  109. package/dist/parser/pdf-extract.d.ts.map +1 -0
  110. package/dist/parser/pdf-extract.js +112 -0
  111. package/dist/parser/pdf-extract.js.map +1 -0
  112. package/dist/parser/pdf-filter.d.ts +117 -0
  113. package/dist/parser/pdf-filter.d.ts.map +1 -0
  114. package/dist/parser/pdf-filter.js +528 -0
  115. package/dist/parser/pdf-filter.js.map +1 -0
  116. package/dist/parser/title-extractor.d.ts +69 -0
  117. package/dist/parser/title-extractor.d.ts.map +1 -0
  118. package/dist/parser/title-extractor.js +145 -0
  119. package/dist/parser/title-extractor.js.map +1 -0
  120. package/dist/pdf-visual/captioner.d.ts +16 -0
  121. package/dist/pdf-visual/captioner.d.ts.map +1 -0
  122. package/dist/pdf-visual/captioner.js +63 -0
  123. package/dist/pdf-visual/captioner.js.map +1 -0
  124. package/dist/pdf-visual/captioners/fast.d.ts +7 -0
  125. package/dist/pdf-visual/captioners/fast.d.ts.map +1 -0
  126. package/dist/pdf-visual/captioners/fast.js +103 -0
  127. package/dist/pdf-visual/captioners/fast.js.map +1 -0
  128. package/dist/pdf-visual/captioners/quality.d.ts +7 -0
  129. package/dist/pdf-visual/captioners/quality.d.ts.map +1 -0
  130. package/dist/pdf-visual/captioners/quality.js +127 -0
  131. package/dist/pdf-visual/captioners/quality.js.map +1 -0
  132. package/dist/pdf-visual/captioners/shared.d.ts +44 -0
  133. package/dist/pdf-visual/captioners/shared.d.ts.map +1 -0
  134. package/dist/pdf-visual/captioners/shared.js +104 -0
  135. package/dist/pdf-visual/captioners/shared.js.map +1 -0
  136. package/dist/pdf-visual/detector.d.ts +9 -0
  137. package/dist/pdf-visual/detector.d.ts.map +1 -0
  138. package/dist/pdf-visual/detector.js +234 -0
  139. package/dist/pdf-visual/detector.js.map +1 -0
  140. package/dist/pdf-visual/index.d.ts +13 -0
  141. package/dist/pdf-visual/index.d.ts.map +1 -0
  142. package/dist/pdf-visual/index.js +45 -0
  143. package/dist/pdf-visual/index.js.map +1 -0
  144. package/dist/pdf-visual/renderer.d.ts +9 -0
  145. package/dist/pdf-visual/renderer.d.ts.map +1 -0
  146. package/dist/pdf-visual/renderer.js +177 -0
  147. package/dist/pdf-visual/renderer.js.map +1 -0
  148. package/dist/pdf-visual/types.d.ts +62 -0
  149. package/dist/pdf-visual/types.d.ts.map +1 -0
  150. package/dist/pdf-visual/types.js +32 -0
  151. package/dist/pdf-visual/types.js.map +1 -0
  152. package/dist/server/error-utils.d.ts +79 -0
  153. package/dist/server/error-utils.d.ts.map +1 -0
  154. package/dist/server/error-utils.js +148 -0
  155. package/dist/server/error-utils.js.map +1 -0
  156. package/dist/server/index.d.ts +258 -0
  157. package/dist/server/index.d.ts.map +1 -0
  158. package/dist/server/index.js +1104 -0
  159. package/dist/server/index.js.map +1 -0
  160. package/dist/server/list-scanner.d.ts +52 -0
  161. package/dist/server/list-scanner.d.ts.map +1 -0
  162. package/dist/server/list-scanner.js +72 -0
  163. package/dist/server/list-scanner.js.map +1 -0
  164. package/dist/server/tool-definitions.d.ts +8 -0
  165. package/dist/server/tool-definitions.d.ts.map +1 -0
  166. package/dist/server/tool-definitions.js +181 -0
  167. package/dist/server/tool-definitions.js.map +1 -0
  168. package/dist/server/tool-input.d.ts +37 -0
  169. package/dist/server/tool-input.d.ts.map +1 -0
  170. package/dist/server/tool-input.js +216 -0
  171. package/dist/server/tool-input.js.map +1 -0
  172. package/dist/server/types.d.ts +331 -0
  173. package/dist/server/types.d.ts.map +1 -0
  174. package/dist/server/types.js +3 -0
  175. package/dist/server/types.js.map +1 -0
  176. package/dist/server-main.d.ts +46 -0
  177. package/dist/server-main.d.ts.map +1 -0
  178. package/dist/server-main.js +242 -0
  179. package/dist/server-main.js.map +1 -0
  180. package/dist/utils/base-dirs.d.ts +212 -0
  181. package/dist/utils/base-dirs.d.ts.map +1 -0
  182. package/dist/utils/base-dirs.js +422 -0
  183. package/dist/utils/base-dirs.js.map +1 -0
  184. package/dist/utils/errors.d.ts +24 -0
  185. package/dist/utils/errors.d.ts.map +1 -0
  186. package/dist/utils/errors.js +53 -0
  187. package/dist/utils/errors.js.map +1 -0
  188. package/dist/utils/limits.d.ts +26 -0
  189. package/dist/utils/limits.d.ts.map +1 -0
  190. package/dist/utils/limits.js +28 -0
  191. package/dist/utils/limits.js.map +1 -0
  192. package/dist/utils/list-sources.d.ts +47 -0
  193. package/dist/utils/list-sources.d.ts.map +1 -0
  194. package/dist/utils/list-sources.js +50 -0
  195. package/dist/utils/list-sources.js.map +1 -0
  196. package/dist/utils/raw-data-utils.d.ts +131 -0
  197. package/dist/utils/raw-data-utils.d.ts.map +1 -0
  198. package/dist/utils/raw-data-utils.js +255 -0
  199. package/dist/utils/raw-data-utils.js.map +1 -0
  200. package/dist/utils/scan.d.ts +126 -0
  201. package/dist/utils/scan.d.ts.map +1 -0
  202. package/dist/utils/scan.js +221 -0
  203. package/dist/utils/scan.js.map +1 -0
  204. package/dist/utils/scope-match.d.ts +43 -0
  205. package/dist/utils/scope-match.d.ts.map +1 -0
  206. package/dist/utils/scope-match.js +87 -0
  207. package/dist/utils/scope-match.js.map +1 -0
  208. package/dist/utils/sensitive-path.d.ts +23 -0
  209. package/dist/utils/sensitive-path.d.ts.map +1 -0
  210. package/dist/utils/sensitive-path.js +91 -0
  211. package/dist/utils/sensitive-path.js.map +1 -0
  212. package/dist/utils/sync-path-key.d.ts +20 -0
  213. package/dist/utils/sync-path-key.d.ts.map +1 -0
  214. package/dist/utils/sync-path-key.js +33 -0
  215. package/dist/utils/sync-path-key.js.map +1 -0
  216. package/dist/vectordb/index.d.ts +168 -0
  217. package/dist/vectordb/index.d.ts.map +1 -0
  218. package/dist/vectordb/index.js +619 -0
  219. package/dist/vectordb/index.js.map +1 -0
  220. package/dist/vectordb/search-filters.d.ts +39 -0
  221. package/dist/vectordb/search-filters.d.ts.map +1 -0
  222. package/dist/vectordb/search-filters.js +136 -0
  223. package/dist/vectordb/search-filters.js.map +1 -0
  224. package/dist/vectordb/types.d.ts +196 -0
  225. package/dist/vectordb/types.d.ts.map +1 -0
  226. package/dist/vectordb/types.js +224 -0
  227. package/dist/vectordb/types.js.map +1 -0
  228. package/package.json +105 -0
  229. package/skills/mcp-local-rag/SKILL.md +308 -0
  230. package/skills/mcp-local-rag/references/cli-reference.md +175 -0
  231. package/skills/mcp-local-rag/references/html-ingestion.md +78 -0
  232. package/skills/mcp-local-rag/references/query-optimization.md +57 -0
  233. package/skills/mcp-local-rag/references/result-refinement.md +56 -0
@@ -0,0 +1,422 @@
1
+ // Shared base-dirs module.
2
+ //
3
+ // Provides one internal representation of the effective document roots used
4
+ // by both the CLI (`ingest`, `list`, ...) and the MCP server entry point
5
+ // (`server-main.ts`), plus the pure helpers needed to derive it from raw
6
+ // configuration inputs (env vars, CLI flags).
7
+ //
8
+ // Scope: this file ships only pure helpers and the types so every consumer
9
+ // can adopt the same realpath/prefix-safety semantics without duplicating
10
+ // the trailing-separator pattern.
11
+ import { realpath, stat } from 'node:fs/promises';
12
+ import { homedir } from 'node:os';
13
+ import { resolve, sep } from 'node:path';
14
+ import { AppError } from './errors.js';
15
+ /**
16
+ * Configuration error raised by parsers and the realpath helper. Modeled as
17
+ * a dedicated subclass so consumers can distinguish configuration problems
18
+ * from other I/O errors (e.g. `ValidationError` from `DocumentParser`).
19
+ */
20
+ export class BaseDirsConfigError extends AppError {
21
+ constructor(message, cause) {
22
+ super(message, 'config', 'config', cause);
23
+ this.name = 'BaseDirsConfigError';
24
+ }
25
+ }
26
+ // ============================================
27
+ // Path display helpers
28
+ // ============================================
29
+ /**
30
+ * Render an absolute path for inclusion in user-visible error/warning
31
+ * messages, substituting the current `$HOME` prefix with `~`. The substitution
32
+ * keeps the message useful for debugging while avoiding leaking the operating
33
+ * username when warnings/errors flow out through MCP responses to clients.
34
+ *
35
+ * `$HOME` resolution is read once at call time, so processes that mutate
36
+ * `HOME` between invocations still see the current value (no caching).
37
+ *
38
+ * Exact-match on the home directory itself (`/Users/me` → `~`) and prefix
39
+ * match with a trailing separator (`/Users/me/work` → `~/work`) are both
40
+ * supported; other paths pass through unchanged.
41
+ */
42
+ export function displayPath(path) {
43
+ const home = process.env['HOME'] || homedir();
44
+ if (home.length === 0)
45
+ return path;
46
+ const isWin = process.platform === 'win32';
47
+ const cmp = (s) => (isWin ? s.toLowerCase() : s);
48
+ const homeCmp = cmp(home);
49
+ const pathCmp = cmp(path);
50
+ if (pathCmp === homeCmp)
51
+ return '~';
52
+ if (pathCmp.startsWith(homeCmp + sep) || pathCmp.startsWith(`${homeCmp}/`)) {
53
+ return `~${path.slice(home.length)}`;
54
+ }
55
+ return path;
56
+ }
57
+ // ============================================
58
+ // JSON-array parser for BASE_DIRS
59
+ // ============================================
60
+ /**
61
+ * Parse the `BASE_DIRS` environment variable.
62
+ *
63
+ * Accepts only a JSON array of one or more non-empty, non-whitespace-only
64
+ * strings — e.g. `'["/Users/me/work","/Users/me/specs"]'`. Anything else
65
+ * (delimiter syntax such as `'/a:/b'`, an empty array, an array containing
66
+ * empty strings, non-string elements, JSON scalars, JSON objects, ...)
67
+ * produces a {@link BaseDirsConfigError}.
68
+ *
69
+ * This helper performs only syntactic validation. It does not resolve
70
+ * realpaths or check that the directories exist — that is the job of
71
+ * {@link normalizeRealpath} after the resolver picks a source.
72
+ */
73
+ export function parseBaseDirsEnv(raw) {
74
+ const trimmed = raw.trim();
75
+ if (trimmed.length === 0) {
76
+ return {
77
+ ok: false,
78
+ error: new BaseDirsConfigError('BASE_DIRS must be a JSON array of non-empty path strings (received empty value).'),
79
+ };
80
+ }
81
+ let parsed;
82
+ try {
83
+ parsed = JSON.parse(trimmed);
84
+ }
85
+ catch (error) {
86
+ return {
87
+ ok: false,
88
+ error: new BaseDirsConfigError(`BASE_DIRS must be a JSON array of non-empty path strings. Failed to parse as JSON: ${truncate(raw)}`, error),
89
+ };
90
+ }
91
+ if (!Array.isArray(parsed)) {
92
+ return {
93
+ ok: false,
94
+ error: new BaseDirsConfigError(`BASE_DIRS must be a JSON array (received ${describeJsonShape(parsed)}).`),
95
+ };
96
+ }
97
+ if (parsed.length === 0) {
98
+ return {
99
+ ok: false,
100
+ error: new BaseDirsConfigError('BASE_DIRS must not be an empty array.'),
101
+ };
102
+ }
103
+ const value = [];
104
+ for (let i = 0; i < parsed.length; i++) {
105
+ const item = parsed[i];
106
+ if (typeof item !== 'string') {
107
+ return {
108
+ ok: false,
109
+ error: new BaseDirsConfigError(`BASE_DIRS[${i}] must be a string (received ${describeJsonShape(item)}).`),
110
+ };
111
+ }
112
+ if (item.trim().length === 0) {
113
+ return {
114
+ ok: false,
115
+ error: new BaseDirsConfigError(`BASE_DIRS[${i}] must be a non-empty, non-whitespace path string.`),
116
+ };
117
+ }
118
+ value.push(item);
119
+ }
120
+ return { ok: true, value };
121
+ }
122
+ // ============================================
123
+ // Realpath normalization
124
+ // ============================================
125
+ /**
126
+ * Append a trailing path separator if the input does not already end with
127
+ * one. This is the prefix-safety pattern used throughout the parser
128
+ * (`/foo/bar` must not match `/foo/barista`).
129
+ */
130
+ export function withTrailingSeparator(path) {
131
+ return path.endsWith(sep) ? path : path + sep;
132
+ }
133
+ /**
134
+ * Resolve a directory to its realpath form and append a trailing separator
135
+ * so the result can be used directly as a prefix in security checks.
136
+ *
137
+ * realpath here is the security boundary (see {@link BaseDirsConfig} for the
138
+ * path policy); user-facing surfaces use the resolve()-only `rawBaseDirs`.
139
+ *
140
+ * Throws {@link BaseDirsConfigError} when the directory does not exist or
141
+ * is not a directory — root configuration must point at real directories
142
+ * the process is allowed to read.
143
+ */
144
+ export async function normalizeRealpath(path) {
145
+ let resolved;
146
+ try {
147
+ resolved = await realpath(resolve(path));
148
+ }
149
+ catch (error) {
150
+ throw new BaseDirsConfigError(`Failed to resolve base directory: ${displayPath(path)}. The directory may not exist or is inaccessible.`, error);
151
+ }
152
+ let stats;
153
+ try {
154
+ stats = await stat(resolved);
155
+ }
156
+ catch (error) {
157
+ throw new BaseDirsConfigError(`Failed to stat resolved base directory: ${displayPath(resolved)}.`, error);
158
+ }
159
+ if (!stats.isDirectory()) {
160
+ throw new BaseDirsConfigError(`Base directory is not a directory: ${displayPath(path)} (resolved: ${displayPath(resolved)}).`);
161
+ }
162
+ return withTrailingSeparator(resolved);
163
+ }
164
+ /**
165
+ * Reduce a list of realpath-normalized roots to the effective set.
166
+ *
167
+ * Behavior:
168
+ * - Exact duplicates (`A === B` after realpath normalization) are silently
169
+ * deduplicated. This is treated as user convenience rather than a
170
+ * configuration mistake, so no warning is emitted.
171
+ * - Nested roots (`B` lives under `A` after realpath normalization) are
172
+ * pruned: the parent `A` is kept, the child `B` is dropped, and a
173
+ * `nested-root-pruned` warning describes both paths. This avoids
174
+ * duplicate `list_files` / CLI scan output without widening the security
175
+ * boundary beyond the parent root the user already configured.
176
+ *
177
+ * Input order is preserved for the surviving roots so the first element
178
+ * remains a meaningful legacy `baseDir` (see {@link legacyBaseDir}).
179
+ *
180
+ * All inputs MUST already have a trailing separator (see
181
+ * {@link normalizeRealpath}) — that is what makes the `startsWith`-based
182
+ * nested check safe against sibling-prefix paths like `/foo/barista`.
183
+ */
184
+ export function dedupAndPruneRoots(inputs) {
185
+ // Pass 1: exact dedup, preserving order.
186
+ const deduped = [];
187
+ const seen = new Set();
188
+ for (const root of inputs) {
189
+ if (!seen.has(root)) {
190
+ seen.add(root);
191
+ deduped.push(root);
192
+ }
193
+ }
194
+ // Pass 2: nested-root pruning.
195
+ //
196
+ // A root `child` is pruned when some other root `parent` (parent !== child)
197
+ // is a strict prefix of `child`. Because every input ends with `sep`, the
198
+ // prefix check correctly distinguishes `/foo/bar/` (parent of `/foo/bar/baz/`)
199
+ // from `/foo/barista/` (sibling, not a parent).
200
+ //
201
+ // When a chain like `[grandparent, parent, child]` is provided, both
202
+ // `parent` and `child` are pruned and each emits a warning referencing the
203
+ // closest SURVIVING ancestor (the grandparent). This is the same result the
204
+ // user would have gotten by passing only the grandparent, and avoids the
205
+ // confusing case where a warning points at another path that was itself
206
+ // pruned. Implementation note: this runs in two passes over `deduped`. The
207
+ // pre-pass computes the `survivors` set (candidates with no ancestor in
208
+ // `deduped`); the main pass then resolves each candidate's closest ancestor
209
+ // against `survivors` so the reported parent is always a surviving root.
210
+ // The two `findParent` scans make this O(n^2) in the number of roots, which
211
+ // is harmless at realistic root counts.
212
+ const roots = [];
213
+ const warnings = [];
214
+ // Pre-pass: identify every candidate that has any ancestor in `deduped`
215
+ // (these are the pruned candidates). The candidates that do NOT have any
216
+ // ancestor in `deduped` are the surviving roots.
217
+ const survivors = [];
218
+ for (const candidate of deduped) {
219
+ if (findParent(candidate, deduped) === undefined) {
220
+ survivors.push(candidate);
221
+ }
222
+ }
223
+ for (const candidate of deduped) {
224
+ const survivingAncestor = findParent(candidate, survivors);
225
+ if (survivingAncestor === undefined) {
226
+ // This candidate is itself a surviving root.
227
+ roots.push(candidate);
228
+ continue;
229
+ }
230
+ warnings.push({
231
+ kind: 'nested-root-pruned',
232
+ message: `Nested base directory pruned: ${displayPath(candidate)} is inside ${displayPath(survivingAncestor)}. Keeping ${displayPath(survivingAncestor)} only.`,
233
+ parent: survivingAncestor,
234
+ pruned: candidate,
235
+ });
236
+ }
237
+ return { roots, warnings };
238
+ }
239
+ /**
240
+ * Return the closest ancestor of `candidate` in `all` (excluding `candidate`
241
+ * itself), or `undefined` if no ancestor exists. Closest is measured by
242
+ * prefix length — longer prefix wins so we report the most specific
243
+ * surviving parent.
244
+ */
245
+ function findParent(candidate, all) {
246
+ let best;
247
+ for (const other of all) {
248
+ if (other === candidate)
249
+ continue;
250
+ // `other` ends with `sep` (precondition), so this prefix check is
251
+ // sibling-prefix safe.
252
+ if (candidate.startsWith(other)) {
253
+ if (best === undefined || other.length > best.length) {
254
+ best = other;
255
+ }
256
+ }
257
+ }
258
+ return best;
259
+ }
260
+ /**
261
+ * Resolve effective base directories from CLI / env / cwd inputs.
262
+ *
263
+ * Resolution order (per the multi-base-dirs plan):
264
+ * 1. `cliRoots` (one or more `--base-dir` flags) — when non-empty, replaces
265
+ * env roots. CLI and env are never merged.
266
+ * 2. `envBaseDirs` (JSON array) — when CLI roots are absent.
267
+ * 3. `envBaseDir` (single path) — when CLI and `BASE_DIRS` are absent.
268
+ * 4. `cwd` — when none of the above are set.
269
+ *
270
+ * Warning rules:
271
+ * - `BASE_DIRS > BASE_DIR` precedence warning fires only when CLI roots are
272
+ * absent AND both `BASE_DIRS` and `BASE_DIR` are set. CLI-driven runs do
273
+ * not produce this warning even if both env vars are also set.
274
+ * - Nested-root pruning warnings always fire when applicable, regardless
275
+ * of which source provided the roots.
276
+ *
277
+ * Error rules:
278
+ * - Invalid `BASE_DIRS` (malformed JSON, non-array, empty array, empty
279
+ * string element, ...) returns `{ ok: false, error }`. The resolver does
280
+ * NOT fall back to `BASE_DIR` or `cwd` — callers surface the error per
281
+ * their UI contract (CLI exit code, MCP tool error, `status` diagnostic).
282
+ * - A path that fails realpath resolution (does not exist, not a directory,
283
+ * permission denied) also returns `{ ok: false, error }`. Roots must
284
+ * point at real directories the process is allowed to read.
285
+ *
286
+ * Post-resolution normalization:
287
+ * - Every selected path is realpath-normalized and gets a trailing path
288
+ * separator (see {@link normalizeRealpath}) so it can be used as a prefix
289
+ * in security checks.
290
+ * - Exact duplicates are silently deduplicated.
291
+ * - Nested roots are pruned with a warning (see {@link dedupAndPruneRoots}).
292
+ */
293
+ export async function resolveBaseDirs(input) {
294
+ const selection = selectRoots(input);
295
+ if (!selection.ok) {
296
+ return selection;
297
+ }
298
+ const warnings = [];
299
+ if (selection.precedenceWarning) {
300
+ warnings.push(selection.precedenceWarning);
301
+ }
302
+ // Realpath-normalize each selected root. Failures (missing directory,
303
+ // permission denied, ...) are surfaced as a structured config error.
304
+ //
305
+ // Pair each realpath'd root (security form) with its resolve()-only form so
306
+ // `rawBaseDirs` mirrors the dedup/prune decisions index-for-index. See
307
+ // {@link BaseDirsConfig} for the path policy.
308
+ const normalized = [];
309
+ const realToRaw = new Map();
310
+ for (const root of selection.roots) {
311
+ let real;
312
+ try {
313
+ real = await normalizeRealpath(root);
314
+ }
315
+ catch (error) {
316
+ if (error instanceof BaseDirsConfigError) {
317
+ return { ok: false, error };
318
+ }
319
+ throw error;
320
+ }
321
+ normalized.push(real);
322
+ // First-occurrence-wins so the surviving raw root matches the first
323
+ // configured spelling of a root that realpaths to the same directory.
324
+ if (!realToRaw.has(real)) {
325
+ realToRaw.set(real, withTrailingSeparator(resolve(root)));
326
+ }
327
+ }
328
+ const { roots, warnings: pruningWarnings } = dedupAndPruneRoots(normalized);
329
+ warnings.push(...pruningWarnings);
330
+ // Project the surviving realpath'd roots back to their resolve()-only forms,
331
+ // preserving order so `rawBaseDirs[i]` and `baseDirs[i]` are the same root.
332
+ const rawRoots = roots.map((real) => realToRaw.get(real) ?? real);
333
+ return {
334
+ ok: true,
335
+ config: { baseDirs: roots, rawBaseDirs: rawRoots },
336
+ warnings,
337
+ };
338
+ }
339
+ /**
340
+ * Apply the source-precedence rules to pick which input set of roots to use.
341
+ *
342
+ * Kept as a small helper so the realpath normalization in
343
+ * {@link resolveBaseDirs} stays focused on I/O, not precedence logic. This
344
+ * function performs only string-level parsing and selection — no fs access.
345
+ */
346
+ function selectRoots(input) {
347
+ // 1. CLI roots — when non-empty, replace env entirely (no precedence
348
+ // warning even if env vars are also set, because the user explicitly
349
+ // overrode them via CLI).
350
+ if (input.cliRoots !== undefined && input.cliRoots.length > 0) {
351
+ return { ok: true, roots: input.cliRoots };
352
+ }
353
+ // 2. BASE_DIRS — when CLI absent. Whitespace-only is treated as an
354
+ // invalid value (consistent with parseBaseDirsEnv), not as "unset",
355
+ // so the user notices a malformed env var instead of silently falling
356
+ // through to BASE_DIR.
357
+ if (input.envBaseDirs !== undefined && input.envBaseDirs.length > 0) {
358
+ const parsed = parseBaseDirsEnv(input.envBaseDirs);
359
+ if (!parsed.ok) {
360
+ return { ok: false, error: parsed.error };
361
+ }
362
+ const precedenceWarning = input.envBaseDir !== undefined && input.envBaseDir.trim().length > 0
363
+ ? {
364
+ kind: 'base-dirs-overrides-base-dir',
365
+ message: 'BASE_DIRS is set; BASE_DIR is ignored. Unset BASE_DIR or remove BASE_DIRS to silence this warning.',
366
+ }
367
+ : undefined;
368
+ return precedenceWarning
369
+ ? { ok: true, roots: parsed.value, precedenceWarning }
370
+ : { ok: true, roots: parsed.value };
371
+ }
372
+ // 3. BASE_DIR — when CLI and BASE_DIRS are absent. Whitespace-only is
373
+ // treated as "unset" (a user clearing the value with spaces gets the
374
+ // same behavior as not setting it at all).
375
+ if (input.envBaseDir !== undefined && input.envBaseDir.trim().length > 0) {
376
+ return { ok: true, roots: [input.envBaseDir] };
377
+ }
378
+ // 4. cwd — final fallback.
379
+ return { ok: true, roots: [input.cwd] };
380
+ }
381
+ // ============================================
382
+ // Legacy single-root accessor
383
+ // ============================================
384
+ /**
385
+ * Return the legacy single-root `baseDir` value for a {@link BaseDirsConfig}.
386
+ *
387
+ * Used for backward compatibility with consumers (and response fields) that
388
+ * pre-date the multi-root model. The contract is "first effective root after
389
+ * normalization and nested-root pruning"; callers must build the config via
390
+ * {@link dedupAndPruneRoots} for this to hold.
391
+ */
392
+ export function legacyBaseDir(config) {
393
+ const first = config.baseDirs[0];
394
+ if (first === undefined) {
395
+ throw new BaseDirsConfigError('BaseDirsConfig must contain at least one base directory.');
396
+ }
397
+ return first;
398
+ }
399
+ // ============================================
400
+ // Private helpers
401
+ // ============================================
402
+ /**
403
+ * Describe a JSON value's shape for error messages without dumping its full
404
+ * (possibly large) content.
405
+ */
406
+ function describeJsonShape(value) {
407
+ if (value === null)
408
+ return 'null';
409
+ if (Array.isArray(value))
410
+ return 'array';
411
+ return typeof value;
412
+ }
413
+ /**
414
+ * Truncate user-supplied input so configuration error messages stay readable
415
+ * even when the offending value is large.
416
+ */
417
+ function truncate(input, max = 100) {
418
+ if (input.length <= max)
419
+ return input;
420
+ return `${input.slice(0, max)}...`;
421
+ }
422
+ //# sourceMappingURL=base-dirs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-dirs.js","sourceRoot":"","sources":["../../src/utils/base-dirs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /** Architectural layer that raised the error. */
2
+ export type AppErrorLayer = 'embedder' | 'parser' | 'vectordb' | 'config' | 'pdf-visual';
3
+ /** Nature of the failure, independent of layer. */
4
+ export type AppErrorKind = 'validation' | 'io' | 'config' | 'internal';
5
+ /**
6
+ * Abstract base for every domain error. Stores the `layer`/`kind`
7
+ * discriminants and the originating `cause`, leaving the concrete subclass to
8
+ * own its public constructor signature and its `name`.
9
+ */
10
+ export declare abstract class AppError extends Error {
11
+ readonly layer: AppErrorLayer;
12
+ readonly kind: AppErrorKind;
13
+ readonly cause?: Error;
14
+ protected constructor(message: string, layer: AppErrorLayer, kind: AppErrorKind, cause?: Error);
15
+ }
16
+ /** Type guard: narrows an unknown value to the shared error taxonomy. */
17
+ export declare function isAppError(value: unknown): value is AppError;
18
+ /**
19
+ * Walks the `.cause` chain starting from `error`, returning errors in order
20
+ * `[outer, cause, cause.cause, ...]`. The walk stops at the first link whose
21
+ * `.cause` is not an `Error`. Self-referential chains are guarded against.
22
+ */
23
+ export declare function getCauseChain(error: Error): Error[];
24
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAWA,iDAAiD;AACjD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAA;AAExF,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,IAAI,GAAG,QAAQ,GAAG,UAAU,CAAA;AAEtE;;;;GAIG;AACH,8BAAsB,QAAS,SAAQ,KAAK;IAC1C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAI3B,SAAkB,KAAK,CAAC,EAAE,KAAK,CAAA;IAE/B,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,KAAK,EAO7F;CACF;AAED,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAWnD"}
@@ -0,0 +1,53 @@
1
+ // Shared error taxonomy foundation.
2
+ //
3
+ // `AppError` is the abstract base every domain error class extends. It carries
4
+ // two discriminants — `layer` (which architectural layer raised it) and `kind`
5
+ // (the nature of the failure) — so a single boundary mapper can convert errors
6
+ // to client codes and to logs without each handler re-deriving identity.
7
+ //
8
+ // This module is behavior-preserving on its own: it adds the `layer`/`kind`
9
+ // metadata and centralizes `cause` storage, while the concrete classes keep
10
+ // their existing names, messages, and `cause` semantics.
11
+ /**
12
+ * Abstract base for every domain error. Stores the `layer`/`kind`
13
+ * discriminants and the originating `cause`, leaving the concrete subclass to
14
+ * own its public constructor signature and its `name`.
15
+ */
16
+ export class AppError extends Error {
17
+ layer;
18
+ kind;
19
+ // Declared explicitly so the type is `Error | undefined` (not `unknown`,
20
+ // as on the native `Error.cause`) and assignment honors
21
+ // `exactOptionalPropertyTypes`.
22
+ cause;
23
+ constructor(message, layer, kind, cause) {
24
+ super(message);
25
+ this.layer = layer;
26
+ this.kind = kind;
27
+ if (cause !== undefined) {
28
+ this.cause = cause;
29
+ }
30
+ }
31
+ }
32
+ /** Type guard: narrows an unknown value to the shared error taxonomy. */
33
+ export function isAppError(value) {
34
+ return value instanceof AppError;
35
+ }
36
+ /**
37
+ * Walks the `.cause` chain starting from `error`, returning errors in order
38
+ * `[outer, cause, cause.cause, ...]`. The walk stops at the first link whose
39
+ * `.cause` is not an `Error`. Self-referential chains are guarded against.
40
+ */
41
+ export function getCauseChain(error) {
42
+ const chain = [];
43
+ const seen = new Set();
44
+ let current = error;
45
+ while (current !== undefined && !seen.has(current)) {
46
+ chain.push(current);
47
+ seen.add(current);
48
+ const next = current.cause;
49
+ current = next instanceof Error ? next : undefined;
50
+ }
51
+ return chain;
52
+ }
53
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,yEAAyE;AACzE,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,yDAAyD;AAQzD;;;;GAIG;AACH,MAAM,OAAgB,QAAS,SAAQ,KAAK;IACjC,KAAK,CAAe;IACpB,IAAI,CAAc;IAC3B,yEAAyE;IACzE,wDAAwD;IACxD,gCAAgC;IACd,KAAK,CAAQ;IAE/B,YAAsB,OAAe,EAAE,KAAoB,EAAE,IAAkB,EAAE,KAAa;QAC5F,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QACpB,CAAC;IACH,CAAC;CACF;AAED,yEAAyE;AACzE,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,KAAK,YAAY,QAAQ,CAAA;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAY;IACxC,MAAM,KAAK,GAAY,EAAE,CAAA;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAS,CAAA;IAC7B,IAAI,OAAO,GAAsB,KAAK,CAAA;IACtC,OAAO,OAAO,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACnB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACjB,MAAM,IAAI,GAAY,OAAO,CAAC,KAAK,CAAA;QACnC,OAAO,GAAG,IAAI,YAAY,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;IACpD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Maximum directory recursion depth when scanning a base directory or ingest
3
+ * target. Applied identically by the CLI `ingest`/`list` walkers and the MCP
4
+ * server's `list_files` scan so the boundary is consistent everywhere.
5
+ */
6
+ export declare const MAX_SCAN_DEPTH = 10;
7
+ /**
8
+ * Default maximum file size for ingestion, in bytes (100 MB). Used when neither
9
+ * the CLI `--max-file-size` flag nor the `MAX_FILE_SIZE` env var is provided.
10
+ */
11
+ export declare const DEFAULT_MAX_FILE_SIZE = 104857600;
12
+ /**
13
+ * Hard upper bound (inclusive) for the configurable max file size, in bytes
14
+ * (500 MB). Values above this are rejected by `validateMaxFileSize`.
15
+ */
16
+ export declare const MAX_FILE_SIZE_LIMIT = 524288000;
17
+ /** Inclusive result-count range shared by CLI, MCP validation, and VectorStore. */
18
+ export declare const MIN_QUERY_LIMIT = 1;
19
+ export declare const MAX_QUERY_LIMIT = 20;
20
+ /** Maximum number of adjacent chunks accepted on either side of a target. */
21
+ export declare const MAX_NEIGHBOR_COUNT = 50;
22
+ /** Inclusive upper bound for the configurable minimum chunk length. */
23
+ export declare const MAX_CHUNK_MIN_LENGTH = 10000;
24
+ /** Maximum encoded bytes for one visual attachment returned with a chunk. */
25
+ export declare const MAX_VISUAL_RENDITION_BYTES: number;
26
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/utils/limits.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,cAAc,KAAK,CAAA;AAEhC;;;GAGG;AACH,eAAO,MAAM,qBAAqB,YAAc,CAAA;AAEhD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,YAAc,CAAA;AAE9C,mFAAmF;AACnF,eAAO,MAAM,eAAe,IAAI,CAAA;AAChC,eAAO,MAAM,eAAe,KAAK,CAAA;AAEjC,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC,uEAAuE;AACvE,eAAO,MAAM,oBAAoB,QAAS,CAAA;AAE1C,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,QAAa,CAAA"}
@@ -0,0 +1,28 @@
1
+ // Cross-cutting numeric limits shared across CLI and MCP server entry points.
2
+ // Dependency-free leaf module so any layer can import it without coupling.
3
+ /**
4
+ * Maximum directory recursion depth when scanning a base directory or ingest
5
+ * target. Applied identically by the CLI `ingest`/`list` walkers and the MCP
6
+ * server's `list_files` scan so the boundary is consistent everywhere.
7
+ */
8
+ export const MAX_SCAN_DEPTH = 10;
9
+ /**
10
+ * Default maximum file size for ingestion, in bytes (100 MB). Used when neither
11
+ * the CLI `--max-file-size` flag nor the `MAX_FILE_SIZE` env var is provided.
12
+ */
13
+ export const DEFAULT_MAX_FILE_SIZE = 104_857_600;
14
+ /**
15
+ * Hard upper bound (inclusive) for the configurable max file size, in bytes
16
+ * (500 MB). Values above this are rejected by `validateMaxFileSize`.
17
+ */
18
+ export const MAX_FILE_SIZE_LIMIT = 524_288_000;
19
+ /** Inclusive result-count range shared by CLI, MCP validation, and VectorStore. */
20
+ export const MIN_QUERY_LIMIT = 1;
21
+ export const MAX_QUERY_LIMIT = 20;
22
+ /** Maximum number of adjacent chunks accepted on either side of a target. */
23
+ export const MAX_NEIGHBOR_COUNT = 50;
24
+ /** Inclusive upper bound for the configurable minimum chunk length. */
25
+ export const MAX_CHUNK_MIN_LENGTH = 10_000;
26
+ /** Maximum encoded bytes for one visual attachment returned with a chunk. */
27
+ export const MAX_VISUAL_RENDITION_BYTES = 512 * 1024;
28
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/utils/limits.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2EAA2E;AAE3E;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAA;AAEhC;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAA;AAEhD;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAA;AAE9C,mFAAmF;AACnF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAA;AAChC,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AAEjC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAEpC,uEAAuE;AACvE,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAA;AAE1C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,GAAG,IAAI,CAAA"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * An ingested entry paired with its identity key (`realpathForMatch` of the
3
+ * stored `filePath`). Only the fields the classifier reads are required, so the
4
+ * caller's richer row type is consumed structurally.
5
+ */
6
+ export interface KeyedIngestedEntry {
7
+ entry: {
8
+ filePath: string;
9
+ chunkCount: number;
10
+ timestamp: string;
11
+ };
12
+ key: string;
13
+ }
14
+ /**
15
+ * A classified source: a raw-data entry restored to its original `source`, or a
16
+ * real-file / orphaned entry keyed by `filePath`. Structurally compatible with
17
+ * the `SourceEntry` union both surfaces already return (no type-move refactor).
18
+ */
19
+ export type ClassifiedSource = {
20
+ source: string;
21
+ chunkCount: number;
22
+ timestamp: string;
23
+ } | {
24
+ filePath: string;
25
+ chunkCount: number;
26
+ timestamp: string;
27
+ };
28
+ /**
29
+ * Classify the ingested entries that matched no scanned file into `sources`.
30
+ *
31
+ * Base filter (always): keep only entries whose `key` is absent from
32
+ * `matchedKeys`.
33
+ *
34
+ * When `scope` is present (non-empty): raw-data entries
35
+ * Managed raw-data entries have no filesystem path under a base directory, so
36
+ * they are always emitted regardless of scope; real-file entries are kept only
37
+ * when their stored `filePath` is under scope (`matchesAnyScope`) — a real-file
38
+ * entry outside scope is dropped so it appears in neither `files[]` (already
39
+ * pruned from the scan) nor `sources[]` (no orphan misclassification), while an
40
+ * unmatched real-file entry under scope remains an orphan source.
41
+ *
42
+ * When `scope` is absent (or empty): behavior is unchanged from the pre-extraction
43
+ * inline block — every unmatched entry is emitted, raw-data as `{ source }` when
44
+ * `extractSourceFromPath` yields one, else as `{ filePath }`.
45
+ */
46
+ export declare function classifyIngestedSources(ingestedKeyed: readonly KeyedIngestedEntry[], matchedKeys: ReadonlySet<string>, dbPath: string, scope?: string[]): ClassifiedSource[];
47
+ //# sourceMappingURL=list-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-sources.d.ts","sourceRoot":"","sources":["../../src/utils/list-sources.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,SAAS,kBAAkB,EAAE,EAC5C,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EAAE,GACf,gBAAgB,EAAE,CAoBpB"}