rbxstudio-mcp 2.3.2 → 2.4.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/README.md +67 -14
- package/dist/__tests__/bridge-service.test.js +25 -13
- package/dist/__tests__/bridge-service.test.js.map +1 -1
- package/dist/__tests__/bridge-session.test.d.ts +2 -0
- package/dist/__tests__/bridge-session.test.d.ts.map +1 -0
- package/dist/__tests__/bridge-session.test.js +171 -0
- package/dist/__tests__/bridge-session.test.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 +201 -0
- package/dist/__tests__/chunker.test.js.map +1 -0
- package/dist/__tests__/docs-core.test.d.ts +2 -0
- package/dist/__tests__/docs-core.test.d.ts.map +1 -0
- package/dist/__tests__/docs-core.test.js +137 -0
- package/dist/__tests__/docs-core.test.js.map +1 -0
- package/dist/__tests__/docs-fetcher.test.d.ts +2 -0
- package/dist/__tests__/docs-fetcher.test.d.ts.map +1 -0
- package/dist/__tests__/docs-fetcher.test.js +173 -0
- package/dist/__tests__/docs-fetcher.test.js.map +1 -0
- package/dist/__tests__/helpers.d.ts +8 -0
- package/dist/__tests__/helpers.d.ts.map +1 -0
- package/dist/__tests__/helpers.js +23 -0
- package/dist/__tests__/helpers.js.map +1 -0
- package/dist/__tests__/http-routes.test.d.ts +2 -0
- package/dist/__tests__/http-routes.test.d.ts.map +1 -0
- package/dist/__tests__/http-routes.test.js +233 -0
- package/dist/__tests__/http-routes.test.js.map +1 -0
- package/dist/__tests__/http-server.test.js +13 -6
- package/dist/__tests__/http-server.test.js.map +1 -1
- package/dist/__tests__/integration.test.js +9 -4
- package/dist/__tests__/integration.test.js.map +1 -1
- package/dist/__tests__/semantic-search.test.d.ts +2 -0
- package/dist/__tests__/semantic-search.test.d.ts.map +1 -0
- package/dist/__tests__/semantic-search.test.js +202 -0
- package/dist/__tests__/semantic-search.test.js.map +1 -0
- package/dist/__tests__/smoke.test.js +7 -3
- package/dist/__tests__/smoke.test.js.map +1 -1
- package/dist/__tests__/studio-client.test.d.ts +2 -0
- package/dist/__tests__/studio-client.test.d.ts.map +1 -0
- package/dist/__tests__/studio-client.test.js +25 -0
- package/dist/__tests__/studio-client.test.js.map +1 -0
- package/dist/__tests__/tool-nudges.test.d.ts +2 -0
- package/dist/__tests__/tool-nudges.test.d.ts.map +1 -0
- package/dist/__tests__/tool-nudges.test.js +60 -0
- package/dist/__tests__/tool-nudges.test.js.map +1 -0
- package/dist/__tests__/tool-registry.test.d.ts +2 -0
- package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
- package/dist/__tests__/tool-registry.test.js +365 -0
- package/dist/__tests__/tool-registry.test.js.map +1 -0
- package/dist/__tests__/tools-bridge.test.d.ts +2 -0
- package/dist/__tests__/tools-bridge.test.d.ts.map +1 -0
- package/dist/__tests__/tools-bridge.test.js +396 -0
- package/dist/__tests__/tools-bridge.test.js.map +1 -0
- package/dist/__tests__/tools-docs.test.d.ts +2 -0
- package/dist/__tests__/tools-docs.test.d.ts.map +1 -0
- package/dist/__tests__/tools-docs.test.js +112 -0
- package/dist/__tests__/tools-docs.test.js.map +1 -0
- package/dist/__tests__/tools-guards.test.d.ts +2 -0
- package/dist/__tests__/tools-guards.test.d.ts.map +1 -0
- package/dist/__tests__/tools-guards.test.js +131 -0
- package/dist/__tests__/tools-guards.test.js.map +1 -0
- package/dist/__tests__/tools-runtime.test.d.ts +2 -0
- package/dist/__tests__/tools-runtime.test.d.ts.map +1 -0
- package/dist/__tests__/tools-runtime.test.js +214 -0
- package/dist/__tests__/tools-runtime.test.js.map +1 -0
- package/dist/__tests__/tools-visual.test.d.ts +2 -0
- package/dist/__tests__/tools-visual.test.d.ts.map +1 -0
- package/dist/__tests__/tools-visual.test.js +149 -0
- package/dist/__tests__/tools-visual.test.js.map +1 -0
- package/dist/bridge-service.d.ts +99 -12
- package/dist/bridge-service.d.ts.map +1 -1
- package/dist/bridge-service.js +238 -21
- package/dist/bridge-service.js.map +1 -1
- package/dist/docs/cache.d.ts +50 -0
- package/dist/docs/cache.d.ts.map +1 -0
- package/dist/docs/cache.js +123 -0
- package/dist/docs/cache.js.map +1 -0
- package/dist/docs/embeddings/chunker.d.ts +120 -0
- package/dist/docs/embeddings/chunker.d.ts.map +1 -0
- package/dist/docs/embeddings/chunker.js +395 -0
- package/dist/docs/embeddings/chunker.js.map +1 -0
- package/dist/docs/embeddings/embedder.d.ts +41 -0
- package/dist/docs/embeddings/embedder.d.ts.map +1 -0
- package/dist/docs/embeddings/embedder.js +113 -0
- package/dist/docs/embeddings/embedder.js.map +1 -0
- package/dist/docs/embeddings/index.d.ts +102 -0
- package/dist/docs/embeddings/index.d.ts.map +1 -0
- package/dist/docs/embeddings/index.js +250 -0
- package/dist/docs/embeddings/index.js.map +1 -0
- package/dist/docs/embeddings/manager.d.ts +68 -0
- package/dist/docs/embeddings/manager.d.ts.map +1 -0
- package/dist/docs/embeddings/manager.js +97 -0
- package/dist/docs/embeddings/manager.js.map +1 -0
- package/dist/docs/fetcher.d.ts +29 -0
- package/dist/docs/fetcher.d.ts.map +1 -0
- package/dist/docs/fetcher.js +244 -0
- package/dist/docs/fetcher.js.map +1 -0
- package/dist/docs/reference.d.ts +37 -0
- package/dist/docs/reference.d.ts.map +1 -0
- package/dist/docs/reference.js +108 -0
- package/dist/docs/reference.js.map +1 -0
- package/dist/docs/search.d.ts +194 -0
- package/dist/docs/search.d.ts.map +1 -0
- package/dist/docs/search.js +733 -0
- package/dist/docs/search.js.map +1 -0
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +52 -5
- package/dist/http-server.js.map +1 -1
- package/dist/index.d.ts +8 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -1035
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +15 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +26 -0
- package/dist/instructions.js.map +1 -0
- package/dist/tools/defs/attributes.d.ts +6 -0
- package/dist/tools/defs/attributes.d.ts.map +1 -0
- package/dist/tools/defs/attributes.js +85 -0
- package/dist/tools/defs/attributes.js.map +1 -0
- package/dist/tools/defs/docs.d.ts +17 -0
- package/dist/tools/defs/docs.d.ts.map +1 -0
- package/dist/tools/defs/docs.js +151 -0
- package/dist/tools/defs/docs.js.map +1 -0
- package/dist/tools/defs/execute.d.ts +6 -0
- package/dist/tools/defs/execute.d.ts.map +1 -0
- package/dist/tools/defs/execute.js +21 -0
- package/dist/tools/defs/execute.js.map +1 -0
- package/dist/tools/defs/inspection.d.ts +7 -0
- package/dist/tools/defs/inspection.d.ts.map +1 -0
- package/dist/tools/defs/inspection.js +202 -0
- package/dist/tools/defs/inspection.js.map +1 -0
- package/dist/tools/defs/objects.d.ts +6 -0
- package/dist/tools/defs/objects.d.ts.map +1 -0
- package/dist/tools/defs/objects.js +111 -0
- package/dist/tools/defs/objects.js.map +1 -0
- package/dist/tools/defs/properties.d.ts +6 -0
- package/dist/tools/defs/properties.d.ts.map +1 -0
- package/dist/tools/defs/properties.js +71 -0
- package/dist/tools/defs/properties.js.map +1 -0
- package/dist/tools/defs/runtime.d.ts +6 -0
- package/dist/tools/defs/runtime.d.ts.map +1 -0
- package/dist/tools/defs/runtime.js +145 -0
- package/dist/tools/defs/runtime.js.map +1 -0
- package/dist/tools/defs/scripts.d.ts +18 -0
- package/dist/tools/defs/scripts.d.ts.map +1 -0
- package/dist/tools/defs/scripts.js +163 -0
- package/dist/tools/defs/scripts.js.map +1 -0
- package/dist/tools/defs/tags.d.ts +6 -0
- package/dist/tools/defs/tags.d.ts.map +1 -0
- package/dist/tools/defs/tags.js +74 -0
- package/dist/tools/defs/tags.js.map +1 -0
- package/dist/tools/defs/visual.d.ts +7 -0
- package/dist/tools/defs/visual.d.ts.map +1 -0
- package/dist/tools/defs/visual.js +208 -0
- package/dist/tools/defs/visual.js.map +1 -0
- package/dist/tools/index.d.ts +101 -25
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +580 -63
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/nudges.d.ts +25 -0
- package/dist/tools/nudges.d.ts.map +1 -0
- package/dist/tools/nudges.js +34 -0
- package/dist/tools/nudges.js.map +1 -0
- package/dist/tools/registry.d.ts +20 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +65 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +24 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/package.json +7 -6
- package/studio-plugin/MCPPlugin.rbxmx +3 -238
- package/studio-plugin/plugin.luau +2041 -365
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
const SCHEMA_VERSION = 1;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the docs cache directory. Honors `RBXSTUDIO_DOCS_DIR` first,
|
|
6
|
+
* otherwise falls back to OS-conventional cache paths via `env-paths`.
|
|
7
|
+
*
|
|
8
|
+
* Linux → ~/.cache/rbxstudio-mcp-nodejs/docs
|
|
9
|
+
* macOS → ~/Library/Caches/rbxstudio-mcp-nodejs/docs
|
|
10
|
+
* Windows → %LOCALAPPDATA%\rbxstudio-mcp-nodejs\Cache\docs
|
|
11
|
+
*
|
|
12
|
+
* Note: `env-paths` is pure ESM and Jest's CJS sandbox can't load it at
|
|
13
|
+
* the top level of a TS module that may be imported by tests. Loading
|
|
14
|
+
* it on first call sidesteps that without affecting production
|
|
15
|
+
* behavior — runs at most once per process thanks to the cache below.
|
|
16
|
+
*/
|
|
17
|
+
let cachedDir = null;
|
|
18
|
+
export function resolveCacheDir() {
|
|
19
|
+
if (cachedDir)
|
|
20
|
+
return cachedDir;
|
|
21
|
+
const override = process.env.RBXSTUDIO_DOCS_DIR;
|
|
22
|
+
if (override && override.trim().length > 0) {
|
|
23
|
+
cachedDir = path.resolve(override);
|
|
24
|
+
return cachedDir;
|
|
25
|
+
}
|
|
26
|
+
// Inline the env-paths logic so we don't need a runtime ESM import
|
|
27
|
+
// (which would force the surrounding module into top-level await).
|
|
28
|
+
// env-paths v3 returns OS-conventional cache paths; we replicate them.
|
|
29
|
+
cachedDir = defaultCacheDir();
|
|
30
|
+
return cachedDir;
|
|
31
|
+
}
|
|
32
|
+
function defaultCacheDir() {
|
|
33
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? process.cwd();
|
|
34
|
+
const appName = 'rbxstudio-mcp-nodejs';
|
|
35
|
+
switch (process.platform) {
|
|
36
|
+
case 'darwin':
|
|
37
|
+
return path.join(home, 'Library', 'Caches', appName, 'docs');
|
|
38
|
+
case 'win32': {
|
|
39
|
+
const localAppData = process.env.LOCALAPPDATA ?? path.join(home, 'AppData', 'Local');
|
|
40
|
+
return path.join(localAppData, appName, 'Cache', 'docs');
|
|
41
|
+
}
|
|
42
|
+
default: {
|
|
43
|
+
// Linux / *nix: respect XDG_CACHE_HOME if set.
|
|
44
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
45
|
+
const base = xdg && xdg.trim().length > 0 ? xdg : path.join(home, '.cache');
|
|
46
|
+
return path.join(base, appName, 'docs');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function metaPath(cacheDir) {
|
|
51
|
+
return path.join(cacheDir, 'meta.json');
|
|
52
|
+
}
|
|
53
|
+
export function contentRoot(cacheDir) {
|
|
54
|
+
return path.join(cacheDir, 'content');
|
|
55
|
+
}
|
|
56
|
+
export async function readMeta(cacheDir) {
|
|
57
|
+
try {
|
|
58
|
+
const raw = await fs.readFile(metaPath(cacheDir), 'utf8');
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
if (parsed.schemaVersion !== SCHEMA_VERSION) {
|
|
61
|
+
// Old/incompatible cache — treat as empty so we re-download.
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return parsed;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err?.code === 'ENOENT')
|
|
68
|
+
return null;
|
|
69
|
+
// Any other error (corrupt JSON, EACCES, …) → behave like no cache,
|
|
70
|
+
// upstream code will re-fetch.
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function writeMeta(cacheDir, meta) {
|
|
75
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
76
|
+
const full = { ...meta, schemaVersion: SCHEMA_VERSION };
|
|
77
|
+
await fs.writeFile(metaPath(cacheDir), JSON.stringify(full, null, 2), 'utf8');
|
|
78
|
+
}
|
|
79
|
+
export async function ensureCacheDir(cacheDir) {
|
|
80
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Wipe the extracted content tree (but keep the cacheDir itself, so users
|
|
84
|
+
* can `tail -F` it if they want to).
|
|
85
|
+
*/
|
|
86
|
+
export async function clearContent(cacheDir) {
|
|
87
|
+
const dir = contentRoot(cacheDir);
|
|
88
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Best-effort byte-count of the content tree. Used for status reporting
|
|
92
|
+
* only; not safety-critical, so errors are swallowed.
|
|
93
|
+
*/
|
|
94
|
+
export async function approxSizeOnDisk(dir) {
|
|
95
|
+
let total = 0;
|
|
96
|
+
async function walk(d) {
|
|
97
|
+
let entries;
|
|
98
|
+
try {
|
|
99
|
+
entries = await fs.readdir(d, { withFileTypes: true });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const e of entries) {
|
|
105
|
+
const p = path.join(d, e.name);
|
|
106
|
+
if (e.isDirectory()) {
|
|
107
|
+
await walk(p);
|
|
108
|
+
}
|
|
109
|
+
else if (e.isFile()) {
|
|
110
|
+
try {
|
|
111
|
+
const stat = await fs.stat(p);
|
|
112
|
+
total += stat.size;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// ignore
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
await walk(dir);
|
|
121
|
+
return total;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/docs/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunker for the Roblox creator-docs mirror.
|
|
3
|
+
*
|
|
4
|
+
* Goal: turn the on-disk md/yaml files into bite-sized, semantically
|
|
5
|
+
* coherent passages suitable for sentence-embedding. We index the
|
|
6
|
+
* resulting chunks once (per docs SHA) and rerank keyword hits against
|
|
7
|
+
* them at query time.
|
|
8
|
+
*
|
|
9
|
+
* Strategy per file type:
|
|
10
|
+
*
|
|
11
|
+
* .md → split on top-level (`#`) and second-level (`##`) ATX
|
|
12
|
+
* headings. Each chunk = heading + body until the next
|
|
13
|
+
* heading of equal/higher level. Long chunks are further
|
|
14
|
+
* split on `###` and then on paragraph boundaries until they
|
|
15
|
+
* fit MAX_CHUNK_CHARS.
|
|
16
|
+
*
|
|
17
|
+
* .yaml → the Roblox API schema is a single top-level document with
|
|
18
|
+
* a `properties:` / `methods:` / `events:` list of members.
|
|
19
|
+
* Each member is its own chunk (name + description + tags).
|
|
20
|
+
* Top-level fields (summary, description, code_samples) get
|
|
21
|
+
* one preamble chunk so a query like "Motor6D" — which
|
|
22
|
+
* matches the class-level summary, not any specific member —
|
|
23
|
+
* still has something to rank against.
|
|
24
|
+
*
|
|
25
|
+
* Each chunk records its source path and the line range it covers so
|
|
26
|
+
* downstream code can deep-link back to the original file (matches the
|
|
27
|
+
* shape `searchDocs` already returns: { path, line, ... }).
|
|
28
|
+
*
|
|
29
|
+
* Why ranges and not single lines? An embedding represents a passage,
|
|
30
|
+
* not a single line. When a chunk wins on cosine score we want to point
|
|
31
|
+
* the model at the whole passage, not just the heading.
|
|
32
|
+
*/
|
|
33
|
+
export interface Chunk {
|
|
34
|
+
/** Doc path relative to the content root. Same shape as SearchHit.path. */
|
|
35
|
+
path: string;
|
|
36
|
+
/** 1-indexed inclusive start line in the source file. */
|
|
37
|
+
startLine: number;
|
|
38
|
+
/** 1-indexed inclusive end line in the source file. */
|
|
39
|
+
endLine: number;
|
|
40
|
+
/**
|
|
41
|
+
* The text we feed to the embedder. Includes the heading / member
|
|
42
|
+
* name as a prefix so the model knows what the passage is about
|
|
43
|
+
* (sentence-transformers do better with explicit context).
|
|
44
|
+
*/
|
|
45
|
+
text: string;
|
|
46
|
+
/**
|
|
47
|
+
* Short label for debugging / UI: the heading, member name, or
|
|
48
|
+
* "<preamble>" for the file-level chunk.
|
|
49
|
+
*/
|
|
50
|
+
label: string;
|
|
51
|
+
/**
|
|
52
|
+
* Categorical hint used by the hybrid scorer to bias certain queries.
|
|
53
|
+
* Mirrors the reference categories in `reference.ts`.
|
|
54
|
+
*/
|
|
55
|
+
kind: 'md-section' | 'yaml-preamble' | 'yaml-member' | 'yaml-misc';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Split markdown into heading-bounded sections.
|
|
59
|
+
*
|
|
60
|
+
* Edge cases handled:
|
|
61
|
+
* - Code fences (```...```) — heading-looking lines inside fences
|
|
62
|
+
* are ignored. (Without this we'd split on a `# comment` inside a
|
|
63
|
+
* Lua snippet, which would be wrong.)
|
|
64
|
+
* - Documents with no headings at all → one big chunk per
|
|
65
|
+
* MAX_CHUNK_CHARS slice.
|
|
66
|
+
* - Front-matter (`---\n...\n---` at the top) — treated as a
|
|
67
|
+
* preamble chunk.
|
|
68
|
+
*/
|
|
69
|
+
declare function chunkMarkdown(relPath: string, raw: string): Chunk[];
|
|
70
|
+
/**
|
|
71
|
+
* Split a too-long chunk along blank-line boundaries. Each output
|
|
72
|
+
* piece carries the line range from its slice of the original.
|
|
73
|
+
*
|
|
74
|
+
* Lines are recomputed: each piece's line range is computed by walking
|
|
75
|
+
* the text and counting newlines from the parent's startLine.
|
|
76
|
+
*/
|
|
77
|
+
declare function splitOversize(text: string, parentStart: number, parentEnd: number): {
|
|
78
|
+
text: string;
|
|
79
|
+
startLine: number;
|
|
80
|
+
endLine: number;
|
|
81
|
+
}[];
|
|
82
|
+
/**
|
|
83
|
+
* Lightweight YAML splitter for the Roblox creator-docs API schema.
|
|
84
|
+
*
|
|
85
|
+
* The schema is regular enough that we don't need a full YAML parser
|
|
86
|
+
* to chunk it sensibly. We look for top-level list keys
|
|
87
|
+
* (`properties:`, `methods:`, `events:`, `callbacks:`, `items:`) and
|
|
88
|
+
* split each list entry (a `- name: Foo` block) into its own chunk.
|
|
89
|
+
*
|
|
90
|
+
* Everything else at the top of the file (`name:`, `type:`, `summary:`,
|
|
91
|
+
* `description:`, `code_samples:`) becomes a single preamble chunk.
|
|
92
|
+
*
|
|
93
|
+
* If the YAML doesn't match the expected shape (e.g. a non-reference
|
|
94
|
+
* doc that happens to be yaml), we fall back to one giant
|
|
95
|
+
* chunk-per-MAX-chars slice.
|
|
96
|
+
*/
|
|
97
|
+
declare function chunkYaml(relPath: string, raw: string): Chunk[];
|
|
98
|
+
/**
|
|
99
|
+
* Pull a top-level `key: value` out of a YAML block. Returns the bare
|
|
100
|
+
* value (quotes stripped) or null if not found. Only looks for keys
|
|
101
|
+
* at column 0 or one level of dash-indent — sufficient for our schema.
|
|
102
|
+
*/
|
|
103
|
+
declare function extractYamlField(block: string, key: string): string | null;
|
|
104
|
+
/**
|
|
105
|
+
* Walk the docs content tree and yield chunks for every chunkable
|
|
106
|
+
* file. Walks lazily so we can stream chunks into the embedder
|
|
107
|
+
* without holding the full set in RAM (though at ~3k chunks × ~500
|
|
108
|
+
* chars ≈ 1.5MB, RAM isn't actually a problem).
|
|
109
|
+
*/
|
|
110
|
+
export declare function walkChunks(cacheDir: string): AsyncGenerator<Chunk>;
|
|
111
|
+
/** Eager version of walkChunks — convenient for tests / index builds. */
|
|
112
|
+
export declare function chunkAll(cacheDir: string): Promise<Chunk[]>;
|
|
113
|
+
export declare const __test__: {
|
|
114
|
+
chunkMarkdown: typeof chunkMarkdown;
|
|
115
|
+
chunkYaml: typeof chunkYaml;
|
|
116
|
+
splitOversize: typeof splitOversize;
|
|
117
|
+
extractYamlField: typeof extractYamlField;
|
|
118
|
+
};
|
|
119
|
+
export {};
|
|
120
|
+
//# sourceMappingURL=chunker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../../../src/docs/embeddings/chunker.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,WAAW,KAAK;IACpB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,EAAE,YAAY,GAAG,eAAe,GAAG,aAAa,GAAG,WAAW,CAAC;CACpE;AAuBD;;;;;;;;;;;GAWG;AACH,iBAAS,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,CA4F5D;AAED;;;;;;GAMG;AACH,iBAAS,aAAa,CACpB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAoDxD;AAgBD;;;;;;;;;;;;;;GAcG;AACH,iBAAS,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,CA8FxD;AAsBD;;;;GAIG;AACH,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAenE;AASD;;;;;GAKG;AACH,wBAAuB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CA+BzE;AAED,yEAAyE;AACzE,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAIjE;AAGD,eAAO,MAAM,QAAQ;;;;;CAAgE,CAAC"}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { contentRoot } from '../cache.js';
|
|
4
|
+
/**
|
|
5
|
+
* Hard cap on chunk size in characters. all-MiniLM-L6-v2 truncates at
|
|
6
|
+
* 256 wordpieces (~1000 chars of English). Going much higher just gets
|
|
7
|
+
* truncated and wastes embedding compute. We aim a bit under so the
|
|
8
|
+
* heading prefix + body fits cleanly.
|
|
9
|
+
*
|
|
10
|
+
* Lower bound — chunks smaller than MIN_CHUNK_CHARS get merged with
|
|
11
|
+
* the next one so we don't emit dozens of useless "## Examples" stubs.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_CHUNK_CHARS = 900;
|
|
14
|
+
const MIN_CHUNK_CHARS = 40;
|
|
15
|
+
/**
|
|
16
|
+
* File extensions the chunker handles. Anything else is silently
|
|
17
|
+
* skipped (binaries, images, etc. shouldn't be in the filtered cache
|
|
18
|
+
* but we don't rely on that).
|
|
19
|
+
*/
|
|
20
|
+
const CHUNKABLE_EXTENSIONS = new Set(['.md', '.yaml', '.yml']);
|
|
21
|
+
// ---------- Markdown ----------
|
|
22
|
+
/**
|
|
23
|
+
* Split markdown into heading-bounded sections.
|
|
24
|
+
*
|
|
25
|
+
* Edge cases handled:
|
|
26
|
+
* - Code fences (```...```) — heading-looking lines inside fences
|
|
27
|
+
* are ignored. (Without this we'd split on a `# comment` inside a
|
|
28
|
+
* Lua snippet, which would be wrong.)
|
|
29
|
+
* - Documents with no headings at all → one big chunk per
|
|
30
|
+
* MAX_CHUNK_CHARS slice.
|
|
31
|
+
* - Front-matter (`---\n...\n---` at the top) — treated as a
|
|
32
|
+
* preamble chunk.
|
|
33
|
+
*/
|
|
34
|
+
function chunkMarkdown(relPath, raw) {
|
|
35
|
+
const lines = raw.split(/\r?\n/);
|
|
36
|
+
const sections = [];
|
|
37
|
+
let inFence = false;
|
|
38
|
+
let lastHeadingIdx = -1;
|
|
39
|
+
let lastHeading = '';
|
|
40
|
+
let lastLevel = 0;
|
|
41
|
+
for (let i = 0; i < lines.length; i++) {
|
|
42
|
+
const line = lines[i];
|
|
43
|
+
// Toggle on triple-backtick fences (and tilde, less common).
|
|
44
|
+
if (/^\s*(```|~~~)/.test(line)) {
|
|
45
|
+
inFence = !inFence;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (inFence)
|
|
49
|
+
continue;
|
|
50
|
+
// ATX heading: 1–6 `#` chars at line start, then space, then text.
|
|
51
|
+
const m = /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
|
|
52
|
+
if (!m)
|
|
53
|
+
continue;
|
|
54
|
+
if (lastHeadingIdx >= 0) {
|
|
55
|
+
sections.push({
|
|
56
|
+
start: lastHeadingIdx,
|
|
57
|
+
end: i - 1,
|
|
58
|
+
heading: lastHeading,
|
|
59
|
+
level: lastLevel,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (i > 0) {
|
|
63
|
+
// Content before any heading → preamble.
|
|
64
|
+
sections.push({
|
|
65
|
+
start: 0,
|
|
66
|
+
end: i - 1,
|
|
67
|
+
heading: '<preamble>',
|
|
68
|
+
level: 0,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
lastHeadingIdx = i;
|
|
72
|
+
lastHeading = m[2];
|
|
73
|
+
lastLevel = m[1].length;
|
|
74
|
+
}
|
|
75
|
+
// Close out the trailing section (or whole file if no headings).
|
|
76
|
+
if (lastHeadingIdx >= 0) {
|
|
77
|
+
sections.push({
|
|
78
|
+
start: lastHeadingIdx,
|
|
79
|
+
end: lines.length - 1,
|
|
80
|
+
heading: lastHeading,
|
|
81
|
+
level: lastLevel,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else if (sections.length === 0) {
|
|
85
|
+
sections.push({
|
|
86
|
+
start: 0,
|
|
87
|
+
end: lines.length - 1,
|
|
88
|
+
heading: '<preamble>',
|
|
89
|
+
level: 0,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const chunks = [];
|
|
93
|
+
for (const sec of sections) {
|
|
94
|
+
const body = lines.slice(sec.start, sec.end + 1).join('\n').trim();
|
|
95
|
+
if (body.length === 0)
|
|
96
|
+
continue;
|
|
97
|
+
// Heading already lives in `body[0]` for non-preamble sections —
|
|
98
|
+
// don't double-add it. For preamble we prepend a label so the
|
|
99
|
+
// embedder has *some* context.
|
|
100
|
+
const text = sec.heading === '<preamble>'
|
|
101
|
+
? `${pathToLabel(relPath)}\n${body}`
|
|
102
|
+
: body;
|
|
103
|
+
// Split oversize sections on blank lines until each piece fits.
|
|
104
|
+
const pieces = splitOversize(text, sec.start, sec.end);
|
|
105
|
+
for (const p of pieces) {
|
|
106
|
+
// Skip pure-stub chunks ("## Examples" with no body underneath).
|
|
107
|
+
if (p.text.replace(/^#+\s.*$/m, '').trim().length < MIN_CHUNK_CHARS) {
|
|
108
|
+
// ...unless the heading itself is informative enough to keep
|
|
109
|
+
// (rare; usually it's not, so we skip).
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
chunks.push({
|
|
113
|
+
path: relPath,
|
|
114
|
+
startLine: p.startLine,
|
|
115
|
+
endLine: p.endLine,
|
|
116
|
+
text: p.text,
|
|
117
|
+
label: sec.heading === '<preamble>' ? '<preamble>' : sec.heading,
|
|
118
|
+
kind: 'md-section',
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return chunks;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Split a too-long chunk along blank-line boundaries. Each output
|
|
126
|
+
* piece carries the line range from its slice of the original.
|
|
127
|
+
*
|
|
128
|
+
* Lines are recomputed: each piece's line range is computed by walking
|
|
129
|
+
* the text and counting newlines from the parent's startLine.
|
|
130
|
+
*/
|
|
131
|
+
function splitOversize(text, parentStart, parentEnd) {
|
|
132
|
+
if (text.length <= MAX_CHUNK_CHARS) {
|
|
133
|
+
return [
|
|
134
|
+
{ text, startLine: parentStart + 1, endLine: parentEnd + 1 },
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
// Split on blank lines, then greedily merge into MAX_CHUNK_CHARS bins.
|
|
138
|
+
const paragraphs = text.split(/\n\s*\n/);
|
|
139
|
+
const out = [];
|
|
140
|
+
let buf = '';
|
|
141
|
+
let bufLines = 0;
|
|
142
|
+
let cursor = parentStart;
|
|
143
|
+
let bufStart = parentStart;
|
|
144
|
+
const flush = () => {
|
|
145
|
+
if (!buf.trim())
|
|
146
|
+
return;
|
|
147
|
+
out.push({
|
|
148
|
+
text: buf.trim(),
|
|
149
|
+
startLine: bufStart + 1,
|
|
150
|
+
endLine: bufStart + Math.max(1, bufLines),
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
for (const p of paragraphs) {
|
|
154
|
+
const plines = p.split(/\r?\n/).length;
|
|
155
|
+
// +2 for the blank line we just split on.
|
|
156
|
+
const need = (buf ? 2 : 0) + p.length;
|
|
157
|
+
if (buf && buf.length + need > MAX_CHUNK_CHARS) {
|
|
158
|
+
flush();
|
|
159
|
+
buf = p;
|
|
160
|
+
bufStart = cursor;
|
|
161
|
+
bufLines = plines;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
if (buf) {
|
|
165
|
+
buf += '\n\n' + p;
|
|
166
|
+
bufLines += plines + 1; // +1 for blank line
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
buf = p;
|
|
170
|
+
bufStart = cursor;
|
|
171
|
+
bufLines = plines;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
cursor += plines + 1; // +1 for blank line consumed by split
|
|
175
|
+
}
|
|
176
|
+
flush();
|
|
177
|
+
// Don't run past parentEnd (the blank-line accounting is approximate).
|
|
178
|
+
for (const piece of out) {
|
|
179
|
+
if (piece.endLine > parentEnd + 1)
|
|
180
|
+
piece.endLine = parentEnd + 1;
|
|
181
|
+
if (piece.startLine > parentEnd + 1)
|
|
182
|
+
piece.startLine = parentEnd + 1;
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Best-effort filename → display label, used for the preamble chunk
|
|
188
|
+
* of an unheaded markdown file.
|
|
189
|
+
* "en-us/animation/using.md" → "animation/using"
|
|
190
|
+
*/
|
|
191
|
+
function pathToLabel(relPath) {
|
|
192
|
+
return relPath
|
|
193
|
+
.replace(/^en-us\//, '')
|
|
194
|
+
.replace(/\.(md|ya?ml)$/i, '')
|
|
195
|
+
.replace(/\\/g, '/');
|
|
196
|
+
}
|
|
197
|
+
// ---------- YAML ----------
|
|
198
|
+
/**
|
|
199
|
+
* Lightweight YAML splitter for the Roblox creator-docs API schema.
|
|
200
|
+
*
|
|
201
|
+
* The schema is regular enough that we don't need a full YAML parser
|
|
202
|
+
* to chunk it sensibly. We look for top-level list keys
|
|
203
|
+
* (`properties:`, `methods:`, `events:`, `callbacks:`, `items:`) and
|
|
204
|
+
* split each list entry (a `- name: Foo` block) into its own chunk.
|
|
205
|
+
*
|
|
206
|
+
* Everything else at the top of the file (`name:`, `type:`, `summary:`,
|
|
207
|
+
* `description:`, `code_samples:`) becomes a single preamble chunk.
|
|
208
|
+
*
|
|
209
|
+
* If the YAML doesn't match the expected shape (e.g. a non-reference
|
|
210
|
+
* doc that happens to be yaml), we fall back to one giant
|
|
211
|
+
* chunk-per-MAX-chars slice.
|
|
212
|
+
*/
|
|
213
|
+
function chunkYaml(relPath, raw) {
|
|
214
|
+
const lines = raw.split(/\r?\n/);
|
|
215
|
+
// Detect schema-like docs: must have at least one of the known
|
|
216
|
+
// top-level list keys at column 0.
|
|
217
|
+
const listKeyRe = /^(properties|methods|events|callbacks|items):\s*$/;
|
|
218
|
+
const hasSchema = lines.some((l) => listKeyRe.test(l));
|
|
219
|
+
if (!hasSchema) {
|
|
220
|
+
return chunkYamlFallback(relPath, raw);
|
|
221
|
+
}
|
|
222
|
+
const chunks = [];
|
|
223
|
+
// Preamble: everything from line 0 up to the first known list key.
|
|
224
|
+
let firstListLine = lines.length;
|
|
225
|
+
for (let i = 0; i < lines.length; i++) {
|
|
226
|
+
if (listKeyRe.test(lines[i])) {
|
|
227
|
+
firstListLine = i;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const preamble = lines.slice(0, firstListLine).join('\n').trim();
|
|
232
|
+
if (preamble.length >= MIN_CHUNK_CHARS) {
|
|
233
|
+
const label = extractYamlField(preamble, 'name') ?? pathToLabel(relPath);
|
|
234
|
+
chunks.push({
|
|
235
|
+
path: relPath,
|
|
236
|
+
startLine: 1,
|
|
237
|
+
endLine: firstListLine,
|
|
238
|
+
text: trimTo(`${label}\n${preamble}`, MAX_CHUNK_CHARS),
|
|
239
|
+
label,
|
|
240
|
+
kind: 'yaml-preamble',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
// Walk list bodies. State: we're "inside" a list when the current
|
|
244
|
+
// line is `properties:` etc. and continues until we hit another
|
|
245
|
+
// top-level key (`^\S`) or EOF.
|
|
246
|
+
let i = firstListLine;
|
|
247
|
+
while (i < lines.length) {
|
|
248
|
+
const listMatch = listKeyRe.exec(lines[i]);
|
|
249
|
+
if (!listMatch) {
|
|
250
|
+
i++;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const listKey = listMatch[1];
|
|
254
|
+
const listStart = i;
|
|
255
|
+
i++;
|
|
256
|
+
// Find end of this list section.
|
|
257
|
+
let listEnd = lines.length;
|
|
258
|
+
for (let j = i; j < lines.length; j++) {
|
|
259
|
+
if (/^\S/.test(lines[j]) && !/^\s*-/.test(lines[j])) {
|
|
260
|
+
listEnd = j;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Split into entries. Each entry starts with ` - name:` or
|
|
265
|
+
// ` - ` at the indent established by the first entry. We just
|
|
266
|
+
// detect `^\s*-\s` as the entry boundary.
|
|
267
|
+
const entryStarts = [];
|
|
268
|
+
for (let j = i; j < listEnd; j++) {
|
|
269
|
+
if (/^\s*-\s/.test(lines[j]))
|
|
270
|
+
entryStarts.push(j);
|
|
271
|
+
}
|
|
272
|
+
entryStarts.push(listEnd); // sentinel
|
|
273
|
+
for (let e = 0; e < entryStarts.length - 1; e++) {
|
|
274
|
+
const eStart = entryStarts[e];
|
|
275
|
+
const eEnd = entryStarts[e + 1] - 1;
|
|
276
|
+
const body = lines.slice(eStart, eEnd + 1).join('\n').trim();
|
|
277
|
+
if (body.length < MIN_CHUNK_CHARS)
|
|
278
|
+
continue;
|
|
279
|
+
const memberName = extractYamlField(body, 'name') ?? `${listKey}[${e}]`;
|
|
280
|
+
const preambleLabel = extractYamlField(preamble, 'name') ?? pathToLabel(relPath);
|
|
281
|
+
// Give the embedder both the parent name and the member name —
|
|
282
|
+
// "Motor6D · C0" rather than just "C0".
|
|
283
|
+
const text = trimTo(`${preambleLabel} · ${memberName} (${listKey})\n${body}`, MAX_CHUNK_CHARS);
|
|
284
|
+
chunks.push({
|
|
285
|
+
path: relPath,
|
|
286
|
+
startLine: eStart + 1,
|
|
287
|
+
endLine: eEnd + 1,
|
|
288
|
+
text,
|
|
289
|
+
label: `${preambleLabel}.${memberName}`,
|
|
290
|
+
kind: 'yaml-member',
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
i = listEnd;
|
|
294
|
+
}
|
|
295
|
+
return chunks;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Non-schema YAML: just slice the file into MAX-chars chunks on
|
|
299
|
+
* blank-line boundaries. Best-effort line accounting.
|
|
300
|
+
*/
|
|
301
|
+
function chunkYamlFallback(relPath, raw) {
|
|
302
|
+
const lines = raw.split(/\r?\n/);
|
|
303
|
+
const total = lines.length;
|
|
304
|
+
const piece = splitOversize(raw, 0, total - 1);
|
|
305
|
+
return piece
|
|
306
|
+
.filter((p) => p.text.length >= MIN_CHUNK_CHARS)
|
|
307
|
+
.map((p) => ({
|
|
308
|
+
path: relPath,
|
|
309
|
+
startLine: p.startLine,
|
|
310
|
+
endLine: p.endLine,
|
|
311
|
+
text: trimTo(`${pathToLabel(relPath)}\n${p.text}`, MAX_CHUNK_CHARS),
|
|
312
|
+
label: pathToLabel(relPath),
|
|
313
|
+
kind: 'yaml-misc',
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Pull a top-level `key: value` out of a YAML block. Returns the bare
|
|
318
|
+
* value (quotes stripped) or null if not found. Only looks for keys
|
|
319
|
+
* at column 0 or one level of dash-indent — sufficient for our schema.
|
|
320
|
+
*/
|
|
321
|
+
function extractYamlField(block, key) {
|
|
322
|
+
// Top-level: `name: value`
|
|
323
|
+
// Or first-line-of-entry: `- name: value`
|
|
324
|
+
const re = new RegExp(`^\\s*-?\\s*${key}:\\s*(.+?)\\s*$`, 'm');
|
|
325
|
+
const m = re.exec(block);
|
|
326
|
+
if (!m)
|
|
327
|
+
return null;
|
|
328
|
+
let v = m[1].trim();
|
|
329
|
+
// Strip surrounding quotes.
|
|
330
|
+
if ((v.startsWith('"') && v.endsWith('"')) ||
|
|
331
|
+
(v.startsWith("'") && v.endsWith("'"))) {
|
|
332
|
+
v = v.slice(1, -1);
|
|
333
|
+
}
|
|
334
|
+
return v.length > 0 ? v : null;
|
|
335
|
+
}
|
|
336
|
+
function trimTo(s, n) {
|
|
337
|
+
if (s.length <= n)
|
|
338
|
+
return s;
|
|
339
|
+
return s.slice(0, n - 1) + '…';
|
|
340
|
+
}
|
|
341
|
+
// ---------- Public API ----------
|
|
342
|
+
/**
|
|
343
|
+
* Walk the docs content tree and yield chunks for every chunkable
|
|
344
|
+
* file. Walks lazily so we can stream chunks into the embedder
|
|
345
|
+
* without holding the full set in RAM (though at ~3k chunks × ~500
|
|
346
|
+
* chars ≈ 1.5MB, RAM isn't actually a problem).
|
|
347
|
+
*/
|
|
348
|
+
export async function* walkChunks(cacheDir) {
|
|
349
|
+
const root = contentRoot(cacheDir);
|
|
350
|
+
const stack = [root];
|
|
351
|
+
while (stack.length > 0) {
|
|
352
|
+
const dir = stack.pop();
|
|
353
|
+
let entries;
|
|
354
|
+
try {
|
|
355
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
for (const e of entries) {
|
|
361
|
+
const p = path.join(dir, e.name);
|
|
362
|
+
if (e.isDirectory()) {
|
|
363
|
+
stack.push(p);
|
|
364
|
+
}
|
|
365
|
+
else if (e.isFile()) {
|
|
366
|
+
const ext = path.extname(e.name).toLowerCase();
|
|
367
|
+
if (!CHUNKABLE_EXTENSIONS.has(ext))
|
|
368
|
+
continue;
|
|
369
|
+
const rel = path.relative(root, p);
|
|
370
|
+
let raw;
|
|
371
|
+
try {
|
|
372
|
+
raw = await fs.readFile(p, 'utf8');
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (raw.indexOf('\0') !== -1)
|
|
378
|
+
continue;
|
|
379
|
+
const chunks = ext === '.md' ? chunkMarkdown(rel, raw) : chunkYaml(rel, raw);
|
|
380
|
+
for (const c of chunks)
|
|
381
|
+
yield c;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/** Eager version of walkChunks — convenient for tests / index builds. */
|
|
387
|
+
export async function chunkAll(cacheDir) {
|
|
388
|
+
const out = [];
|
|
389
|
+
for await (const c of walkChunks(cacheDir))
|
|
390
|
+
out.push(c);
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
// Export internals for tests.
|
|
394
|
+
export const __test__ = { chunkMarkdown, chunkYaml, splitOversize, extractYamlField };
|
|
395
|
+
//# sourceMappingURL=chunker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker.js","sourceRoot":"","sources":["../../../src/docs/embeddings/chunker.ts"],"names":[],"mappings":"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"}
|