@dynamik-dev/refdocs 0.1.1
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/README.md +109 -0
- package/dist/src/chunker.d.ts +9 -0
- package/dist/src/chunker.d.ts.map +1 -0
- package/dist/src/chunker.js +205 -0
- package/dist/src/chunker.js.map +1 -0
- package/dist/src/config.d.ts +8 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +77 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +124 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/indexer.d.ts +9 -0
- package/dist/src/indexer.d.ts.map +1 -0
- package/dist/src/indexer.js +58 -0
- package/dist/src/indexer.js.map +1 -0
- package/dist/src/search.d.ts +11 -0
- package/dist/src/search.d.ts.map +1 -0
- package/dist/src/search.js +63 -0
- package/dist/src/search.js.map +1 -0
- package/dist/src/types.d.ts +39 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tests/chunker.test.d.ts +2 -0
- package/dist/tests/chunker.test.d.ts.map +1 -0
- package/dist/tests/chunker.test.js +245 -0
- package/dist/tests/chunker.test.js.map +1 -0
- package/dist/tests/cli.test.d.ts +2 -0
- package/dist/tests/cli.test.d.ts.map +1 -0
- package/dist/tests/cli.test.js +166 -0
- package/dist/tests/cli.test.js.map +1 -0
- package/dist/tests/config.test.d.ts +2 -0
- package/dist/tests/config.test.d.ts.map +1 -0
- package/dist/tests/config.test.js +94 -0
- package/dist/tests/config.test.js.map +1 -0
- package/dist/tests/indexer.test.d.ts +2 -0
- package/dist/tests/indexer.test.d.ts.map +1 -0
- package/dist/tests/indexer.test.js +109 -0
- package/dist/tests/indexer.test.js.map +1 -0
- package/dist/tests/search.test.d.ts +2 -0
- package/dist/tests/search.test.d.ts.map +1 -0
- package/dist/tests/search.test.js +160 -0
- package/dist/tests/search.test.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { loadConfig, validateConfig } from "../src/config.js";
|
|
6
|
+
describe("validateConfig", () => {
|
|
7
|
+
it("returns no errors for valid config", () => {
|
|
8
|
+
expect(validateConfig({
|
|
9
|
+
paths: ["docs"],
|
|
10
|
+
index: ".index.json",
|
|
11
|
+
chunkMaxTokens: 800,
|
|
12
|
+
chunkMinTokens: 100,
|
|
13
|
+
boostFields: { title: 2, headings: 1.5, body: 1 },
|
|
14
|
+
})).toEqual([]);
|
|
15
|
+
});
|
|
16
|
+
it("returns no errors for empty object (all optional)", () => {
|
|
17
|
+
expect(validateConfig({})).toEqual([]);
|
|
18
|
+
});
|
|
19
|
+
it("rejects non-object config", () => {
|
|
20
|
+
expect(validateConfig("string")).toEqual(["Config must be a JSON object"]);
|
|
21
|
+
expect(validateConfig(null)).toEqual(["Config must be a JSON object"]);
|
|
22
|
+
expect(validateConfig([])).toEqual(["Config must be a JSON object"]);
|
|
23
|
+
});
|
|
24
|
+
it("rejects invalid paths", () => {
|
|
25
|
+
const errors = validateConfig({ paths: "not-array" });
|
|
26
|
+
expect(errors).toContain('"paths" must be an array of strings');
|
|
27
|
+
});
|
|
28
|
+
it("rejects non-string items in paths", () => {
|
|
29
|
+
const errors = validateConfig({ paths: [123] });
|
|
30
|
+
expect(errors).toContain('"paths" must be an array of strings');
|
|
31
|
+
});
|
|
32
|
+
it("rejects non-string index", () => {
|
|
33
|
+
const errors = validateConfig({ index: 123 });
|
|
34
|
+
expect(errors).toContain('"index" must be a string');
|
|
35
|
+
});
|
|
36
|
+
it("rejects non-positive chunkMaxTokens", () => {
|
|
37
|
+
expect(validateConfig({ chunkMaxTokens: -1 })).toContain('"chunkMaxTokens" must be a positive number');
|
|
38
|
+
expect(validateConfig({ chunkMaxTokens: "abc" })).toContain('"chunkMaxTokens" must be a positive number');
|
|
39
|
+
});
|
|
40
|
+
it("rejects non-positive chunkMinTokens", () => {
|
|
41
|
+
expect(validateConfig({ chunkMinTokens: 0 })).toContain('"chunkMinTokens" must be a positive number');
|
|
42
|
+
});
|
|
43
|
+
it("rejects invalid boostFields", () => {
|
|
44
|
+
expect(validateConfig({ boostFields: "not-object" })).toContain('"boostFields" must be an object');
|
|
45
|
+
});
|
|
46
|
+
it("rejects non-number boost field values", () => {
|
|
47
|
+
const errors = validateConfig({ boostFields: { title: "high" } });
|
|
48
|
+
expect(errors).toContain('"boostFields.title" must be a number');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe("loadConfig", () => {
|
|
52
|
+
let tmpDir;
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
tmpDir = mkdtempSync(join(tmpdir(), "refdocs-test-"));
|
|
55
|
+
});
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
58
|
+
});
|
|
59
|
+
it("returns defaults when no config file exists", () => {
|
|
60
|
+
const { config, configDir } = loadConfig(tmpDir);
|
|
61
|
+
expect(config.paths).toEqual(["ref-docs"]);
|
|
62
|
+
expect(config.index).toBe(".refdocs-index.json");
|
|
63
|
+
expect(config.chunkMaxTokens).toBe(800);
|
|
64
|
+
expect(config.chunkMinTokens).toBe(100);
|
|
65
|
+
expect(configDir).toBe(tmpDir);
|
|
66
|
+
});
|
|
67
|
+
it("loads config from given directory", () => {
|
|
68
|
+
writeFileSync(join(tmpDir, ".refdocs.json"), JSON.stringify({ paths: ["docs"], chunkMaxTokens: 500 }));
|
|
69
|
+
const { config } = loadConfig(tmpDir);
|
|
70
|
+
expect(config.paths).toEqual(["docs"]);
|
|
71
|
+
expect(config.chunkMaxTokens).toBe(500);
|
|
72
|
+
expect(config.chunkMinTokens).toBe(100); // default preserved
|
|
73
|
+
});
|
|
74
|
+
it("walks up directories to find config", () => {
|
|
75
|
+
writeFileSync(join(tmpDir, ".refdocs.json"), JSON.stringify({ paths: ["custom-docs"] }));
|
|
76
|
+
const subDir = join(tmpDir, "sub", "deep");
|
|
77
|
+
mkdirSync(subDir, { recursive: true });
|
|
78
|
+
const { config, configDir } = loadConfig(subDir);
|
|
79
|
+
expect(config.paths).toEqual(["custom-docs"]);
|
|
80
|
+
expect(configDir).toBe(tmpDir);
|
|
81
|
+
});
|
|
82
|
+
it("merges boostFields with defaults", () => {
|
|
83
|
+
writeFileSync(join(tmpDir, ".refdocs.json"), JSON.stringify({ boostFields: { title: 5 } }));
|
|
84
|
+
const { config } = loadConfig(tmpDir);
|
|
85
|
+
expect(config.boostFields.title).toBe(5);
|
|
86
|
+
expect(config.boostFields.headings).toBe(1.5);
|
|
87
|
+
expect(config.boostFields.body).toBe(1);
|
|
88
|
+
});
|
|
89
|
+
it("throws on invalid config", () => {
|
|
90
|
+
writeFileSync(join(tmpDir, ".refdocs.json"), JSON.stringify({ paths: "not-an-array" }));
|
|
91
|
+
expect(() => loadConfig(tmpDir)).toThrow("Invalid .refdocs.json");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../tests/config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.test.d.ts","sourceRoot":"","sources":["../../tests/indexer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, writeFileSync, mkdirSync, rmSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { findMarkdownFiles, buildIndex, loadPersistedIndex } from "../src/indexer.js";
|
|
6
|
+
import { search } from "../src/search.js";
|
|
7
|
+
const testConfig = {
|
|
8
|
+
paths: ["docs"],
|
|
9
|
+
index: ".refdocs-index.json",
|
|
10
|
+
chunkMaxTokens: 800,
|
|
11
|
+
chunkMinTokens: 100,
|
|
12
|
+
boostFields: { title: 2, headings: 1.5, body: 1 },
|
|
13
|
+
};
|
|
14
|
+
describe("findMarkdownFiles", () => {
|
|
15
|
+
let tmpDir;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
tmpDir = mkdtempSync(join(tmpdir(), "refdocs-idx-"));
|
|
18
|
+
mkdirSync(join(tmpDir, "docs"), { recursive: true });
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
it("finds .md files in configured paths", () => {
|
|
24
|
+
writeFileSync(join(tmpDir, "docs", "readme.md"), "# Hello");
|
|
25
|
+
writeFileSync(join(tmpDir, "docs", "guide.md"), "# Guide");
|
|
26
|
+
const files = findMarkdownFiles(["docs"], tmpDir);
|
|
27
|
+
expect(files).toEqual(["docs/guide.md", "docs/readme.md"]);
|
|
28
|
+
});
|
|
29
|
+
it("finds files recursively", () => {
|
|
30
|
+
mkdirSync(join(tmpDir, "docs", "sub"), { recursive: true });
|
|
31
|
+
writeFileSync(join(tmpDir, "docs", "root.md"), "# Root");
|
|
32
|
+
writeFileSync(join(tmpDir, "docs", "sub", "nested.md"), "# Nested");
|
|
33
|
+
const files = findMarkdownFiles(["docs"], tmpDir);
|
|
34
|
+
expect(files).toContain("docs/root.md");
|
|
35
|
+
expect(files).toContain("docs/sub/nested.md");
|
|
36
|
+
});
|
|
37
|
+
it("ignores non-md files", () => {
|
|
38
|
+
writeFileSync(join(tmpDir, "docs", "readme.md"), "# Hello");
|
|
39
|
+
writeFileSync(join(tmpDir, "docs", "notes.txt"), "not markdown");
|
|
40
|
+
writeFileSync(join(tmpDir, "docs", "data.json"), "{}");
|
|
41
|
+
const files = findMarkdownFiles(["docs"], tmpDir);
|
|
42
|
+
expect(files).toEqual(["docs/readme.md"]);
|
|
43
|
+
});
|
|
44
|
+
it("returns empty array for missing directory", () => {
|
|
45
|
+
const files = findMarkdownFiles(["nonexistent"], tmpDir);
|
|
46
|
+
expect(files).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
it("returns sorted list", () => {
|
|
49
|
+
writeFileSync(join(tmpDir, "docs", "z-file.md"), "# Z");
|
|
50
|
+
writeFileSync(join(tmpDir, "docs", "a-file.md"), "# A");
|
|
51
|
+
writeFileSync(join(tmpDir, "docs", "m-file.md"), "# M");
|
|
52
|
+
const files = findMarkdownFiles(["docs"], tmpDir);
|
|
53
|
+
expect(files).toEqual(["docs/a-file.md", "docs/m-file.md", "docs/z-file.md"]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("buildIndex", () => {
|
|
57
|
+
let tmpDir;
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
tmpDir = mkdtempSync(join(tmpdir(), "refdocs-build-"));
|
|
60
|
+
mkdirSync(join(tmpDir, "docs"), { recursive: true });
|
|
61
|
+
});
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
64
|
+
});
|
|
65
|
+
it("builds index and writes to disk", () => {
|
|
66
|
+
writeFileSync(join(tmpDir, "docs", "api.md"), "# API\n\n## Authentication\n\nUse JWT tokens for auth.\n\n## Rate Limiting\n\nDefault is 100 req/min.\n");
|
|
67
|
+
const summary = buildIndex(testConfig, tmpDir);
|
|
68
|
+
expect(summary.filesIndexed).toBe(1);
|
|
69
|
+
expect(summary.chunksCreated).toBeGreaterThanOrEqual(1);
|
|
70
|
+
expect(summary.indexSizeBytes).toBeGreaterThan(0);
|
|
71
|
+
expect(summary.elapsedMs).toBeGreaterThanOrEqual(0);
|
|
72
|
+
expect(existsSync(join(tmpDir, ".refdocs-index.json"))).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
it("indexes multiple files", () => {
|
|
75
|
+
writeFileSync(join(tmpDir, "docs", "a.md"), "# File A\n\nContent A about authentication and tokens.\n");
|
|
76
|
+
writeFileSync(join(tmpDir, "docs", "b.md"), "# File B\n\nContent B about configuration and setup.\n");
|
|
77
|
+
const summary = buildIndex(testConfig, tmpDir);
|
|
78
|
+
expect(summary.filesIndexed).toBe(2);
|
|
79
|
+
});
|
|
80
|
+
it("produces searchable index", () => {
|
|
81
|
+
writeFileSync(join(tmpDir, "docs", "guide.md"), "# Setup Guide\n\n## Database\n\nConfigure PostgreSQL connection string in your .env file.\n\n## Cache\n\nRedis is used for caching session data.\n");
|
|
82
|
+
buildIndex(testConfig, tmpDir);
|
|
83
|
+
const { index } = loadPersistedIndex(join(tmpDir, ".refdocs-index.json"), testConfig);
|
|
84
|
+
const results = search(index, "PostgreSQL", { maxResults: 3 });
|
|
85
|
+
expect(results.length).toBeGreaterThan(0);
|
|
86
|
+
expect(results[0].body).toContain("PostgreSQL");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe("loadPersistedIndex", () => {
|
|
90
|
+
it("throws with actionable error when index does not exist", () => {
|
|
91
|
+
expect(() => loadPersistedIndex("/nonexistent/path.json", testConfig)).toThrow("Index not found. Run `refdocs index` first.");
|
|
92
|
+
});
|
|
93
|
+
it("round-trips build + load successfully", () => {
|
|
94
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "refdocs-rt-"));
|
|
95
|
+
try {
|
|
96
|
+
mkdirSync(join(tmpDir, "docs"), { recursive: true });
|
|
97
|
+
writeFileSync(join(tmpDir, "docs", "test.md"), "# Test Doc\n\nThis is test content about widgets and gadgets.\n");
|
|
98
|
+
buildIndex(testConfig, tmpDir);
|
|
99
|
+
const { index, chunks } = loadPersistedIndex(join(tmpDir, ".refdocs-index.json"), testConfig);
|
|
100
|
+
expect(chunks.length).toBeGreaterThan(0);
|
|
101
|
+
const results = search(index, "widgets", { maxResults: 3 });
|
|
102
|
+
expect(results.length).toBeGreaterThan(0);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=indexer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.test.js","sourceRoot":"","sources":["../../tests/indexer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../tests/search.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { createSearchIndex, indexChunks, serializeIndex, loadIndex, search, } from "../src/search.js";
|
|
3
|
+
const testConfig = {
|
|
4
|
+
paths: ["docs"],
|
|
5
|
+
index: ".refdocs-index.json",
|
|
6
|
+
chunkMaxTokens: 800,
|
|
7
|
+
chunkMinTokens: 100,
|
|
8
|
+
boostFields: { title: 2, headings: 1.5, body: 1 },
|
|
9
|
+
};
|
|
10
|
+
function makeChunk(overrides) {
|
|
11
|
+
return {
|
|
12
|
+
file: "test.md",
|
|
13
|
+
title: "Test",
|
|
14
|
+
headings: "Test",
|
|
15
|
+
body: "test body",
|
|
16
|
+
startLine: 1,
|
|
17
|
+
endLine: 10,
|
|
18
|
+
tokenEstimate: 10,
|
|
19
|
+
...overrides,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const sampleChunks = [
|
|
23
|
+
makeChunk({
|
|
24
|
+
id: "api.md:0",
|
|
25
|
+
file: "api.md",
|
|
26
|
+
title: "Authentication",
|
|
27
|
+
headings: "API > Authentication",
|
|
28
|
+
body: "Use JWT tokens for authentication. Include the token in the Authorization header.",
|
|
29
|
+
startLine: 1,
|
|
30
|
+
endLine: 5,
|
|
31
|
+
}),
|
|
32
|
+
makeChunk({
|
|
33
|
+
id: "api.md:1",
|
|
34
|
+
file: "api.md",
|
|
35
|
+
title: "Rate Limiting",
|
|
36
|
+
headings: "API > Rate Limiting",
|
|
37
|
+
body: "Rate limits are applied per API key. Default limit is 100 requests per minute.",
|
|
38
|
+
startLine: 6,
|
|
39
|
+
endLine: 10,
|
|
40
|
+
}),
|
|
41
|
+
makeChunk({
|
|
42
|
+
id: "guide.md:0",
|
|
43
|
+
file: "guide.md",
|
|
44
|
+
title: "Getting Started",
|
|
45
|
+
headings: "Getting Started",
|
|
46
|
+
body: "Install the package and configure your environment. Run npm install to get started.",
|
|
47
|
+
startLine: 1,
|
|
48
|
+
endLine: 8,
|
|
49
|
+
}),
|
|
50
|
+
makeChunk({
|
|
51
|
+
id: "guide.md:1",
|
|
52
|
+
file: "guide.md",
|
|
53
|
+
title: "Configuration",
|
|
54
|
+
headings: "Getting Started > Configuration",
|
|
55
|
+
body: "Create a .env file with your database URL and API key for authentication.",
|
|
56
|
+
startLine: 9,
|
|
57
|
+
endLine: 15,
|
|
58
|
+
}),
|
|
59
|
+
];
|
|
60
|
+
describe("createSearchIndex and indexChunks", () => {
|
|
61
|
+
it("creates an index and adds chunks without error", () => {
|
|
62
|
+
const index = createSearchIndex(testConfig);
|
|
63
|
+
indexChunks(index, sampleChunks);
|
|
64
|
+
expect(index.documentCount).toBe(sampleChunks.length);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("search", () => {
|
|
68
|
+
it("finds relevant results for a query", () => {
|
|
69
|
+
const index = createSearchIndex(testConfig);
|
|
70
|
+
indexChunks(index, sampleChunks);
|
|
71
|
+
const results = search(index, "authentication", { maxResults: 3 });
|
|
72
|
+
expect(results.length).toBeGreaterThan(0);
|
|
73
|
+
expect(results[0].file).toBe("api.md");
|
|
74
|
+
expect(results[0].body).toContain("JWT tokens");
|
|
75
|
+
});
|
|
76
|
+
it("respects maxResults", () => {
|
|
77
|
+
const index = createSearchIndex(testConfig);
|
|
78
|
+
indexChunks(index, sampleChunks);
|
|
79
|
+
const results = search(index, "api", { maxResults: 1 });
|
|
80
|
+
expect(results).toHaveLength(1);
|
|
81
|
+
});
|
|
82
|
+
it("returns empty array for no matches", () => {
|
|
83
|
+
const index = createSearchIndex(testConfig);
|
|
84
|
+
indexChunks(index, sampleChunks);
|
|
85
|
+
const results = search(index, "xyznonexistent", { maxResults: 3 });
|
|
86
|
+
expect(results).toHaveLength(0);
|
|
87
|
+
});
|
|
88
|
+
it("includes score, file, lines, headings, body in results", () => {
|
|
89
|
+
const index = createSearchIndex(testConfig);
|
|
90
|
+
indexChunks(index, sampleChunks);
|
|
91
|
+
const results = search(index, "rate limiting", { maxResults: 3 });
|
|
92
|
+
const result = results[0];
|
|
93
|
+
expect(result.score).toBeGreaterThan(0);
|
|
94
|
+
expect(result.file).toBe("api.md");
|
|
95
|
+
expect(result.lines).toEqual([6, 10]);
|
|
96
|
+
expect(result.headings).toEqual(["API", "Rate Limiting"]);
|
|
97
|
+
expect(result.body).toContain("100 requests per minute");
|
|
98
|
+
});
|
|
99
|
+
it("boosts title matches over body matches", () => {
|
|
100
|
+
const index = createSearchIndex(testConfig);
|
|
101
|
+
indexChunks(index, sampleChunks);
|
|
102
|
+
// "Authentication" appears in title of api.md:0 and body of guide.md:1
|
|
103
|
+
const results = search(index, "authentication", { maxResults: 4 });
|
|
104
|
+
expect(results[0].file).toBe("api.md");
|
|
105
|
+
expect(results[0].headings).toContain("Authentication");
|
|
106
|
+
});
|
|
107
|
+
it("supports fuzzy matching", () => {
|
|
108
|
+
const index = createSearchIndex(testConfig);
|
|
109
|
+
indexChunks(index, sampleChunks);
|
|
110
|
+
// Misspelling
|
|
111
|
+
const results = search(index, "authenication", { maxResults: 3 });
|
|
112
|
+
expect(results.length).toBeGreaterThan(0);
|
|
113
|
+
});
|
|
114
|
+
it("supports prefix matching", () => {
|
|
115
|
+
const index = createSearchIndex(testConfig);
|
|
116
|
+
indexChunks(index, sampleChunks);
|
|
117
|
+
const results = search(index, "auth", { maxResults: 3 });
|
|
118
|
+
expect(results.length).toBeGreaterThan(0);
|
|
119
|
+
});
|
|
120
|
+
it("filters by file glob", () => {
|
|
121
|
+
const index = createSearchIndex(testConfig);
|
|
122
|
+
indexChunks(index, sampleChunks);
|
|
123
|
+
const results = search(index, "authentication", {
|
|
124
|
+
maxResults: 10,
|
|
125
|
+
fileFilter: "guide.*",
|
|
126
|
+
});
|
|
127
|
+
for (const r of results) {
|
|
128
|
+
expect(r.file).toMatch(/^guide/);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe("serialization", () => {
|
|
133
|
+
it("round-trips through serialize and load", () => {
|
|
134
|
+
const index = createSearchIndex(testConfig);
|
|
135
|
+
indexChunks(index, sampleChunks);
|
|
136
|
+
const json = serializeIndex(index, sampleChunks);
|
|
137
|
+
const { index: loaded, chunks } = loadIndex(json, testConfig);
|
|
138
|
+
expect(chunks).toEqual(sampleChunks);
|
|
139
|
+
const results = search(loaded, "authentication", { maxResults: 3 });
|
|
140
|
+
expect(results.length).toBeGreaterThan(0);
|
|
141
|
+
expect(results[0].body).toContain("JWT");
|
|
142
|
+
});
|
|
143
|
+
it("includes version and createdAt in serialized output", () => {
|
|
144
|
+
const index = createSearchIndex(testConfig);
|
|
145
|
+
indexChunks(index, sampleChunks);
|
|
146
|
+
const json = serializeIndex(index, sampleChunks);
|
|
147
|
+
const parsed = JSON.parse(json);
|
|
148
|
+
expect(parsed.version).toBe(1);
|
|
149
|
+
expect(parsed.createdAt).toBeDefined();
|
|
150
|
+
});
|
|
151
|
+
it("throws on version mismatch", () => {
|
|
152
|
+
const index = createSearchIndex(testConfig);
|
|
153
|
+
indexChunks(index, sampleChunks);
|
|
154
|
+
const json = serializeIndex(index, sampleChunks);
|
|
155
|
+
const parsed = JSON.parse(json);
|
|
156
|
+
parsed.version = 999;
|
|
157
|
+
expect(() => loadIndex(JSON.stringify(parsed), testConfig)).toThrow("Index version mismatch");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
//# sourceMappingURL=search.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.js","sourceRoot":"","sources":["../../tests/search.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dynamik-dev/refdocs",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Local CLI tool that indexes markdown documentation and exposes fast fuzzy search with intelligent chunking",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"refdocs": "dist/src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"build": "bun build --compile src/index.ts --outfile refdocs"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"registry": "https://registry.npmjs.org",
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/dynamik-dev/refdoc-cli.git"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"commander": "^13.1.0",
|
|
28
|
+
"mdast-util-from-markdown": "^2.0.2",
|
|
29
|
+
"minisearch": "^7.1.1",
|
|
30
|
+
"picomatch": "^4.0.2"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.0.0",
|
|
34
|
+
"@types/picomatch": "^3.0.1",
|
|
35
|
+
"tsx": "^4.19.0",
|
|
36
|
+
"typescript": "^5.7.3",
|
|
37
|
+
"vitest": "^3.0.5"
|
|
38
|
+
}
|
|
39
|
+
}
|