@neuledge/context 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.
package/dist/git.js ADDED
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Git operations for cloning repositories.
3
+ */
4
+ // TODO: Future enhancements:
5
+ // - Auto-detect documentation site from README (parse for docs.* or documentation links)
6
+ // - Suggest specific versions when repo has tags (e.g., "context add react --version 18.2.0")
7
+ import { execSync } from "node:child_process";
8
+ import { createHash } from "node:crypto";
9
+ import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import ignore, {} from "ignore";
13
+ /**
14
+ * Generate a content hash for deduplication.
15
+ * Uses first 16 chars of MD5 (sufficient for detecting identical content).
16
+ */
17
+ function contentHash(content) {
18
+ return createHash("md5").update(content).digest("hex").slice(0, 16);
19
+ }
20
+ /**
21
+ * ISO 639-1 language codes (2-letter) commonly used in docs.
22
+ * Used to detect and filter locale directories.
23
+ */
24
+ const LOCALE_CODES = new Set([
25
+ "ar",
26
+ "bg",
27
+ "bn",
28
+ "ca",
29
+ "cs",
30
+ "da",
31
+ "de",
32
+ "el",
33
+ "en",
34
+ "es",
35
+ "et",
36
+ "fa",
37
+ "fi",
38
+ "fr",
39
+ "he",
40
+ "hi",
41
+ "hr",
42
+ "hu",
43
+ "id",
44
+ "it",
45
+ "ja",
46
+ "ko",
47
+ "lt",
48
+ "lv",
49
+ "ms",
50
+ "nl",
51
+ "no",
52
+ "pl",
53
+ "pt",
54
+ "ro",
55
+ "ru",
56
+ "sk",
57
+ "sl",
58
+ "sr",
59
+ "sv",
60
+ "th",
61
+ "tr",
62
+ "uk",
63
+ "vi",
64
+ "zh",
65
+ "zh-cn",
66
+ "zh-tw",
67
+ "zh-hans",
68
+ "zh-hant",
69
+ "pt-br",
70
+ "es-la",
71
+ ]);
72
+ /**
73
+ * Check if a directory name looks like a locale code.
74
+ */
75
+ function isLocaleDir(name) {
76
+ return LOCALE_CODES.has(name.toLowerCase());
77
+ }
78
+ /**
79
+ * Files to ignore during markdown indexing.
80
+ * These are common repo files that aren't useful documentation.
81
+ */
82
+ const IGNORED_FILES = new Set([
83
+ "code_of_conduct.md",
84
+ "contributing.md",
85
+ "changelog.md",
86
+ "history.md",
87
+ "license.md",
88
+ "security.md",
89
+ "pull_request_template.md",
90
+ "issue_template.md",
91
+ "claude.md", // AI assistant configuration
92
+ ]);
93
+ /**
94
+ * Directories to ignore during markdown indexing.
95
+ * Includes test directories, internal docs, and other non-user-facing content.
96
+ */
97
+ const IGNORED_DIRS = new Set([
98
+ // Test directories
99
+ "__tests__",
100
+ "__test__",
101
+ "test",
102
+ "tests",
103
+ "spec",
104
+ "specs",
105
+ "fixtures",
106
+ "__fixtures__",
107
+ "__mocks__",
108
+ // Internal/development directories
109
+ "internal",
110
+ "dev",
111
+ "plans",
112
+ ".plans",
113
+ // Build/generated directories
114
+ "node_modules",
115
+ "dist",
116
+ "build",
117
+ "out",
118
+ ".next",
119
+ ".nuxt",
120
+ // Other non-doc directories
121
+ "examples", // Often contains code samples, not docs
122
+ "benchmarks",
123
+ "benchmark",
124
+ ]);
125
+ /**
126
+ * Check if a string is a git URL (supports various git protocols).
127
+ * Matches: https://, git://, ssh://, git@host:, or .git suffix
128
+ * Excludes: URLs with paths beyond repo root (releases, blob, actions, etc.)
129
+ */
130
+ export function isGitUrl(source) {
131
+ // https://... or http://... ending with .git
132
+ if (/^https?:\/\/.*\.git$/i.test(source))
133
+ return true;
134
+ // git://...
135
+ if (source.startsWith("git://"))
136
+ return true;
137
+ // ssh://...
138
+ if (source.startsWith("ssh://"))
139
+ return true;
140
+ // git@host:user/repo format (SSH shorthand)
141
+ if (/^git@[\w.-]+:[\w./-]+$/.test(source))
142
+ return true;
143
+ // Known git hosting providers - only match repo root or /tree/ paths
144
+ // Exclude: /releases/, /blob/, /raw/, /actions/, /issues/, /pull/, etc.
145
+ const gitHostMatch = source.match(/^https?:\/\/(github|gitlab|bitbucket|codeberg)\.[^/]+\/[\w.-]+\/[\w.-]+(\/tree\/.*)?$/);
146
+ if (gitHostMatch)
147
+ return true;
148
+ return false;
149
+ }
150
+ /**
151
+ * Clone a git repository to a temporary directory.
152
+ */
153
+ export function cloneRepository(url, ref) {
154
+ const tempDir = mkdtempSync(join(tmpdir(), "context-git-"));
155
+ try {
156
+ // Clone with depth 1 for efficiency (shallow clone)
157
+ const cloneArgs = ["clone", "--depth", "1"];
158
+ if (ref) {
159
+ cloneArgs.push("--branch", ref);
160
+ }
161
+ cloneArgs.push(url, tempDir);
162
+ execSync(`git ${cloneArgs.join(" ")}`, {
163
+ stdio: ["pipe", "pipe", "pipe"],
164
+ encoding: "utf-8",
165
+ });
166
+ }
167
+ catch (error) {
168
+ // Clean up on failure
169
+ rmSync(tempDir, { recursive: true, force: true });
170
+ const message = error instanceof Error ? error.message : String(error);
171
+ throw new Error(`Git clone failed: ${message}`);
172
+ }
173
+ return {
174
+ tempDir,
175
+ cleanup: () => rmSync(tempDir, { recursive: true, force: true }),
176
+ };
177
+ }
178
+ /**
179
+ * Extract repository name from a git URL.
180
+ * Handles common docs repo patterns:
181
+ * - org.github.io → org
182
+ * - expressjs.com → express
183
+ * - project-docs → project
184
+ */
185
+ export function extractRepoName(url) {
186
+ // Remove .git suffix if present
187
+ let cleaned = url.replace(/\.git$/, "");
188
+ // Handle SSH shorthand (git@host:user/repo)
189
+ if (cleaned.includes("@") && cleaned.includes(":")) {
190
+ cleaned = cleaned.split(":").pop() ?? cleaned;
191
+ }
192
+ // Get the last path segment (repo name) and org/user
193
+ const segments = cleaned.split("/").filter(Boolean);
194
+ let name = segments.pop() ?? "unknown";
195
+ const org = segments.pop();
196
+ // Handle *.github.io patterns → use org name instead
197
+ if (name.endsWith(".github.io") && org) {
198
+ name = org;
199
+ }
200
+ // Handle domain-style repos (e.g., expressjs.com) → strip TLD and "js" suffix
201
+ else if (/\.(com|org|io|dev|net|site|app)$/i.test(name)) {
202
+ name = name
203
+ .replace(/\.(com|org|io|dev|net|site|app)$/i, "")
204
+ .replace(/js$/i, ""); // expressjs → express (only for domain repos)
205
+ }
206
+ return (name
207
+ .toLowerCase()
208
+ .replace(/\.js$/, "") // express.js → express
209
+ .replace(/-docs?$/, "")
210
+ .replace(/[^a-z0-9-]/g, "-")
211
+ .replace(/-+/g, "-")
212
+ .replace(/^-|-$/g, "") || "unknown");
213
+ }
214
+ /**
215
+ * Parse git URL to extract optional ref from URL path.
216
+ * Supports: https://github.com/owner/repo/tree/branch
217
+ */
218
+ export function parseGitUrl(url) {
219
+ // Handle GitHub/GitLab tree paths
220
+ const treeMatch = url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/tree\/(.+)$/);
221
+ if (treeMatch?.[1] && treeMatch[2]) {
222
+ return { url: treeMatch[1], ref: treeMatch[2] };
223
+ }
224
+ return { url };
225
+ }
226
+ const DOCS_FOLDER_CANDIDATES = ["docs", "documentation", "doc"];
227
+ /**
228
+ * Detect docs folder in a local directory.
229
+ */
230
+ export function detectLocalDocsFolder(dirPath) {
231
+ try {
232
+ const entries = readdirSync(dirPath, { withFileTypes: true });
233
+ const dirs = entries
234
+ .filter((e) => e.isDirectory())
235
+ .map((e) => e.name.toLowerCase());
236
+ for (const candidate of DOCS_FOLDER_CANDIDATES) {
237
+ if (dirs.includes(candidate)) {
238
+ // Return actual case-sensitive name
239
+ const actual = entries.find((e) => e.isDirectory() && e.name.toLowerCase() === candidate);
240
+ return actual?.name ?? null;
241
+ }
242
+ }
243
+ }
244
+ catch {
245
+ // Directory read failed
246
+ }
247
+ return null;
248
+ }
249
+ /**
250
+ * Load .gitignore from a directory if it exists.
251
+ */
252
+ function loadGitignore(basePath) {
253
+ const ig = ignore();
254
+ const gitignorePath = join(basePath, ".gitignore");
255
+ if (existsSync(gitignorePath)) {
256
+ try {
257
+ const content = readFileSync(gitignorePath, "utf-8");
258
+ ig.add(content);
259
+ }
260
+ catch {
261
+ // Ignore read errors
262
+ }
263
+ }
264
+ return ig;
265
+ }
266
+ /**
267
+ * Recursively find all markdown files in a directory.
268
+ * Respects .gitignore rules and skips non-doc files like CODE_OF_CONDUCT.
269
+ * By default, filters out non-English locale directories.
270
+ */
271
+ function findMarkdownFiles(dirPath, ig, basePath = "", options = {}) {
272
+ const files = [];
273
+ const lang = options.lang?.toLowerCase();
274
+ try {
275
+ const entries = readdirSync(dirPath, { withFileTypes: true });
276
+ for (const entry of entries) {
277
+ const fullPath = join(dirPath, entry.name);
278
+ const relativePath = basePath ? join(basePath, entry.name) : entry.name;
279
+ // Skip hidden entries
280
+ if (entry.name.startsWith("."))
281
+ continue;
282
+ // Check gitignore (directories need trailing slash for gitignore matching)
283
+ const pathToCheck = entry.isDirectory()
284
+ ? `${relativePath}/`
285
+ : relativePath;
286
+ if (ig.ignores(pathToCheck))
287
+ continue;
288
+ if (entry.isDirectory()) {
289
+ // Skip test, internal, and other non-doc directories
290
+ if (IGNORED_DIRS.has(entry.name.toLowerCase())) {
291
+ continue;
292
+ }
293
+ // Filter locale directories unless --lang all or specific lang matches
294
+ if (isLocaleDir(entry.name)) {
295
+ const dirName = entry.name.toLowerCase();
296
+ // Include if: all languages, matching lang, or default to English
297
+ if (lang === "all" ||
298
+ lang === dirName ||
299
+ (!lang && dirName === "en")) {
300
+ files.push(...findMarkdownFiles(fullPath, ig, relativePath, options));
301
+ }
302
+ // Skip other locales by default
303
+ }
304
+ else {
305
+ files.push(...findMarkdownFiles(fullPath, ig, relativePath, options));
306
+ }
307
+ }
308
+ else if (entry.isFile()) {
309
+ if (entry.name.endsWith(".md") || entry.name.endsWith(".mdx")) {
310
+ const lowerName = entry.name.toLowerCase();
311
+ // Skip non-doc markdown files
312
+ if (IGNORED_FILES.has(lowerName))
313
+ continue;
314
+ // Skip test fixture files (e.g., component.expect.md, hook.test.md)
315
+ if (lowerName.endsWith(".expect.md") ||
316
+ lowerName.endsWith(".test.md") ||
317
+ lowerName.endsWith(".spec.md")) {
318
+ continue;
319
+ }
320
+ files.push(relativePath);
321
+ }
322
+ }
323
+ }
324
+ }
325
+ catch {
326
+ // Directory read failed
327
+ }
328
+ return files;
329
+ }
330
+ /**
331
+ * Read all markdown files from a local directory.
332
+ * Respects .gitignore from the base path (repo root).
333
+ * By default, filters out non-English locale directories.
334
+ * Deduplicates files by content hash (keeps first occurrence).
335
+ */
336
+ export function readLocalDocsFiles(basePath, options = {}) {
337
+ const { docsPath, lang } = options;
338
+ const searchPath = docsPath ? join(basePath, docsPath) : basePath;
339
+ if (!existsSync(searchPath)) {
340
+ throw new Error(`Directory not found: ${searchPath}`);
341
+ }
342
+ // Load gitignore from repo root
343
+ const ig = loadGitignore(basePath);
344
+ const markdownFiles = findMarkdownFiles(searchPath, ig, "", { lang });
345
+ const files = [];
346
+ const seenHashes = new Set();
347
+ for (const filePath of markdownFiles) {
348
+ try {
349
+ const fullPath = join(searchPath, filePath);
350
+ const content = readFileSync(fullPath, "utf-8");
351
+ // Skip duplicate content (keep first occurrence)
352
+ const hash = contentHash(content);
353
+ if (seenHashes.has(hash)) {
354
+ continue;
355
+ }
356
+ seenHashes.add(hash);
357
+ // Use relative path from docs folder for storage
358
+ const storagePath = docsPath ? join(docsPath, filePath) : filePath;
359
+ files.push({ path: storagePath, content });
360
+ }
361
+ catch {
362
+ // Skip files that can't be read
363
+ }
364
+ }
365
+ return files;
366
+ }
367
+ /**
368
+ * Extract version from ref or return 'latest'.
369
+ */
370
+ export function extractVersion(ref) {
371
+ if (!ref)
372
+ return "latest";
373
+ return ref.startsWith("v") ? ref.slice(1) : ref;
374
+ }
375
+ /**
376
+ * Detect version from a directory by checking:
377
+ * 1. Latest git tag (universal across all languages)
378
+ * 2. Falls back to 'latest'
379
+ */
380
+ export function detectVersion(dirPath) {
381
+ // Try git tag (works for any language/framework)
382
+ try {
383
+ const tag = execSync("git describe --tags --abbrev=0 2>/dev/null", {
384
+ cwd: dirPath,
385
+ encoding: "utf-8",
386
+ stdio: ["pipe", "pipe", "pipe"],
387
+ }).trim();
388
+ if (tag) {
389
+ return tag.startsWith("v") ? tag.slice(1) : tag;
390
+ }
391
+ }
392
+ catch {
393
+ // Not a git repo or no tags
394
+ }
395
+ return "latest";
396
+ }
397
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { type DocSection, type ParsedDoc, parseMarkdown } from "./build.js";
2
+ export { cloneRepository, detectLocalDocsFolder, extractRepoName, extractVersion, type GitCloneResult, isGitUrl, type LocalDocsResult, parseGitUrl, readLocalDocsFiles, } from "./git.js";
3
+ export { type BuildResult, buildPackage, type MarkdownFile, type PackageBuildOptions, } from "./package-builder.js";
4
+ export type { DocSnippet, SearchResult } from "./search.js";
5
+ export { ContextServer } from "./server.js";
6
+ export { type PackageInfo, type PackageMeta, PackageStore, readPackageInfo, } from "./store.js";
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ // Public API
2
+ // Build utilities
3
+ export { parseMarkdown } from "./build.js";
4
+ // Git utilities
5
+ export { cloneRepository, detectLocalDocsFolder, extractRepoName, extractVersion, isGitUrl, parseGitUrl, readLocalDocsFiles, } from "./git.js";
6
+ export { buildPackage, } from "./package-builder.js";
7
+ export { ContextServer } from "./server.js";
8
+ export { PackageStore, readPackageInfo, } from "./store.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AAEb,kBAAkB;AAClB,OAAO,EAAmC,aAAa,EAAE,MAAM,YAAY,CAAC;AAC5E,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,cAAc,EAEd,QAAQ,EAER,WAAW,EACX,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAEL,YAAY,GAGb,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAGL,YAAY,EACZ,eAAe,GAChB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Package builder for creating documentation packages from markdown files.
3
+ */
4
+ export interface PackageBuildOptions {
5
+ name: string;
6
+ version: string;
7
+ description?: string;
8
+ sourceUrl?: string;
9
+ }
10
+ export interface MarkdownFile {
11
+ path: string;
12
+ content: string;
13
+ }
14
+ export interface BuildResult {
15
+ path: string;
16
+ sectionCount: number;
17
+ totalTokens: number;
18
+ }
19
+ /**
20
+ * Build a documentation package from markdown files.
21
+ */
22
+ export declare function buildPackage(outputPath: string, files: MarkdownFile[], options: PackageBuildOptions): BuildResult;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Package builder for creating documentation packages from markdown files.
3
+ */
4
+ import { existsSync, unlinkSync } from "node:fs";
5
+ import Database from "better-sqlite3";
6
+ import { parseMarkdown } from "./build.js";
7
+ /**
8
+ * Build a documentation package from markdown files.
9
+ */
10
+ export function buildPackage(outputPath, files, options) {
11
+ // Remove existing file if present
12
+ if (existsSync(outputPath)) {
13
+ unlinkSync(outputPath);
14
+ }
15
+ const db = new Database(outputPath);
16
+ try {
17
+ // Create schema
18
+ db.exec(`
19
+ CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);
20
+ CREATE TABLE chunks (
21
+ id INTEGER PRIMARY KEY,
22
+ doc_path TEXT NOT NULL,
23
+ doc_title TEXT NOT NULL,
24
+ section_title TEXT NOT NULL,
25
+ content TEXT NOT NULL,
26
+ tokens INTEGER NOT NULL,
27
+ has_code INTEGER DEFAULT 0
28
+ );
29
+ CREATE VIRTUAL TABLE chunks_fts USING fts5(
30
+ doc_title, section_title, content,
31
+ content='chunks', content_rowid='id',
32
+ tokenize='porter unicode61'
33
+ );
34
+ `);
35
+ // Insert metadata
36
+ const insertMeta = db.prepare("INSERT INTO meta (key, value) VALUES (?, ?)");
37
+ insertMeta.run("name", options.name);
38
+ insertMeta.run("version", options.version);
39
+ if (options.description) {
40
+ insertMeta.run("description", options.description);
41
+ }
42
+ if (options.sourceUrl) {
43
+ insertMeta.run("source_url", options.sourceUrl);
44
+ }
45
+ // Parse and insert chunks
46
+ const insertChunk = db.prepare(`
47
+ INSERT INTO chunks (doc_path, doc_title, section_title, content, tokens, has_code)
48
+ VALUES (?, ?, ?, ?, ?, ?)
49
+ `);
50
+ const allSections = [];
51
+ for (const file of files) {
52
+ try {
53
+ const parsed = parseMarkdown(file.content, file.path);
54
+ allSections.push(...parsed.sections);
55
+ }
56
+ catch {
57
+ // Skip files that fail to parse
58
+ }
59
+ }
60
+ // Insert all sections in a transaction
61
+ const insertAll = db.transaction((sections) => {
62
+ for (const section of sections) {
63
+ insertChunk.run(section.docPath, section.docTitle, section.sectionTitle, section.content, section.tokens, section.hasCode ? 1 : 0);
64
+ }
65
+ });
66
+ insertAll(allSections);
67
+ // Rebuild FTS index
68
+ db.exec("INSERT INTO chunks_fts(chunks_fts) VALUES('rebuild')");
69
+ const totalTokens = allSections.reduce((sum, s) => sum + s.tokens, 0);
70
+ return {
71
+ path: outputPath,
72
+ sectionCount: allSections.length,
73
+ totalTokens,
74
+ };
75
+ }
76
+ finally {
77
+ db.close();
78
+ }
79
+ }
80
+ //# sourceMappingURL=package-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-builder.js","sourceRoot":"","sources":["../src/package-builder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAmB,aAAa,EAAE,MAAM,YAAY,CAAC;AAoB5D;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,UAAkB,EAClB,KAAqB,EACrB,OAA4B;IAE5B,kCAAkC;IAClC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,UAAU,CAAC,UAAU,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,gBAAgB;QAChB,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;KAgBP,CAAC,CAAC;QAEH,kBAAkB;QAClB,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAC3B,6CAA6C,CAC9C,CAAC;QACF,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACrC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACxB,UAAU,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,UAAU,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAClD,CAAC;QAED,0BAA0B;QAC1B,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC;;;KAG9B,CAAC,CAAC;QAEH,MAAM,WAAW,GAAiB,EAAE,CAAC;QAErC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtD,WAAW,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,gCAAgC;YAClC,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,MAAM,SAAS,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,QAAsB,EAAE,EAAE;YAC1D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,WAAW,CAAC,GAAG,CACb,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,YAAY,EACpB,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACxB,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,SAAS,CAAC,WAAW,CAAC,CAAC;QAEvB,oBAAoB;QACpB,EAAE,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QAEhE,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEtE,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,WAAW,CAAC,MAAM;YAChC,WAAW;SACZ,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type Database from "better-sqlite3";
2
+ export interface DocSnippet {
3
+ title: string;
4
+ content: string;
5
+ source: string;
6
+ }
7
+ export interface SearchResult {
8
+ library: string;
9
+ version: string;
10
+ results: DocSnippet[];
11
+ }
12
+ export declare function search(db: Database.Database, topic: string): SearchResult;