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.
Files changed (175) hide show
  1. package/README.md +67 -14
  2. package/dist/__tests__/bridge-service.test.js +25 -13
  3. package/dist/__tests__/bridge-service.test.js.map +1 -1
  4. package/dist/__tests__/bridge-session.test.d.ts +2 -0
  5. package/dist/__tests__/bridge-session.test.d.ts.map +1 -0
  6. package/dist/__tests__/bridge-session.test.js +171 -0
  7. package/dist/__tests__/bridge-session.test.js.map +1 -0
  8. package/dist/__tests__/chunker.test.d.ts +2 -0
  9. package/dist/__tests__/chunker.test.d.ts.map +1 -0
  10. package/dist/__tests__/chunker.test.js +201 -0
  11. package/dist/__tests__/chunker.test.js.map +1 -0
  12. package/dist/__tests__/docs-core.test.d.ts +2 -0
  13. package/dist/__tests__/docs-core.test.d.ts.map +1 -0
  14. package/dist/__tests__/docs-core.test.js +137 -0
  15. package/dist/__tests__/docs-core.test.js.map +1 -0
  16. package/dist/__tests__/docs-fetcher.test.d.ts +2 -0
  17. package/dist/__tests__/docs-fetcher.test.d.ts.map +1 -0
  18. package/dist/__tests__/docs-fetcher.test.js +173 -0
  19. package/dist/__tests__/docs-fetcher.test.js.map +1 -0
  20. package/dist/__tests__/helpers.d.ts +8 -0
  21. package/dist/__tests__/helpers.d.ts.map +1 -0
  22. package/dist/__tests__/helpers.js +23 -0
  23. package/dist/__tests__/helpers.js.map +1 -0
  24. package/dist/__tests__/http-routes.test.d.ts +2 -0
  25. package/dist/__tests__/http-routes.test.d.ts.map +1 -0
  26. package/dist/__tests__/http-routes.test.js +233 -0
  27. package/dist/__tests__/http-routes.test.js.map +1 -0
  28. package/dist/__tests__/http-server.test.js +13 -6
  29. package/dist/__tests__/http-server.test.js.map +1 -1
  30. package/dist/__tests__/integration.test.js +9 -4
  31. package/dist/__tests__/integration.test.js.map +1 -1
  32. package/dist/__tests__/semantic-search.test.d.ts +2 -0
  33. package/dist/__tests__/semantic-search.test.d.ts.map +1 -0
  34. package/dist/__tests__/semantic-search.test.js +202 -0
  35. package/dist/__tests__/semantic-search.test.js.map +1 -0
  36. package/dist/__tests__/smoke.test.js +7 -3
  37. package/dist/__tests__/smoke.test.js.map +1 -1
  38. package/dist/__tests__/studio-client.test.d.ts +2 -0
  39. package/dist/__tests__/studio-client.test.d.ts.map +1 -0
  40. package/dist/__tests__/studio-client.test.js +25 -0
  41. package/dist/__tests__/studio-client.test.js.map +1 -0
  42. package/dist/__tests__/tool-nudges.test.d.ts +2 -0
  43. package/dist/__tests__/tool-nudges.test.d.ts.map +1 -0
  44. package/dist/__tests__/tool-nudges.test.js +60 -0
  45. package/dist/__tests__/tool-nudges.test.js.map +1 -0
  46. package/dist/__tests__/tool-registry.test.d.ts +2 -0
  47. package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
  48. package/dist/__tests__/tool-registry.test.js +365 -0
  49. package/dist/__tests__/tool-registry.test.js.map +1 -0
  50. package/dist/__tests__/tools-bridge.test.d.ts +2 -0
  51. package/dist/__tests__/tools-bridge.test.d.ts.map +1 -0
  52. package/dist/__tests__/tools-bridge.test.js +396 -0
  53. package/dist/__tests__/tools-bridge.test.js.map +1 -0
  54. package/dist/__tests__/tools-docs.test.d.ts +2 -0
  55. package/dist/__tests__/tools-docs.test.d.ts.map +1 -0
  56. package/dist/__tests__/tools-docs.test.js +112 -0
  57. package/dist/__tests__/tools-docs.test.js.map +1 -0
  58. package/dist/__tests__/tools-guards.test.d.ts +2 -0
  59. package/dist/__tests__/tools-guards.test.d.ts.map +1 -0
  60. package/dist/__tests__/tools-guards.test.js +131 -0
  61. package/dist/__tests__/tools-guards.test.js.map +1 -0
  62. package/dist/__tests__/tools-runtime.test.d.ts +2 -0
  63. package/dist/__tests__/tools-runtime.test.d.ts.map +1 -0
  64. package/dist/__tests__/tools-runtime.test.js +214 -0
  65. package/dist/__tests__/tools-runtime.test.js.map +1 -0
  66. package/dist/__tests__/tools-visual.test.d.ts +2 -0
  67. package/dist/__tests__/tools-visual.test.d.ts.map +1 -0
  68. package/dist/__tests__/tools-visual.test.js +149 -0
  69. package/dist/__tests__/tools-visual.test.js.map +1 -0
  70. package/dist/bridge-service.d.ts +99 -12
  71. package/dist/bridge-service.d.ts.map +1 -1
  72. package/dist/bridge-service.js +238 -21
  73. package/dist/bridge-service.js.map +1 -1
  74. package/dist/docs/cache.d.ts +50 -0
  75. package/dist/docs/cache.d.ts.map +1 -0
  76. package/dist/docs/cache.js +123 -0
  77. package/dist/docs/cache.js.map +1 -0
  78. package/dist/docs/embeddings/chunker.d.ts +120 -0
  79. package/dist/docs/embeddings/chunker.d.ts.map +1 -0
  80. package/dist/docs/embeddings/chunker.js +395 -0
  81. package/dist/docs/embeddings/chunker.js.map +1 -0
  82. package/dist/docs/embeddings/embedder.d.ts +41 -0
  83. package/dist/docs/embeddings/embedder.d.ts.map +1 -0
  84. package/dist/docs/embeddings/embedder.js +113 -0
  85. package/dist/docs/embeddings/embedder.js.map +1 -0
  86. package/dist/docs/embeddings/index.d.ts +102 -0
  87. package/dist/docs/embeddings/index.d.ts.map +1 -0
  88. package/dist/docs/embeddings/index.js +250 -0
  89. package/dist/docs/embeddings/index.js.map +1 -0
  90. package/dist/docs/embeddings/manager.d.ts +68 -0
  91. package/dist/docs/embeddings/manager.d.ts.map +1 -0
  92. package/dist/docs/embeddings/manager.js +97 -0
  93. package/dist/docs/embeddings/manager.js.map +1 -0
  94. package/dist/docs/fetcher.d.ts +29 -0
  95. package/dist/docs/fetcher.d.ts.map +1 -0
  96. package/dist/docs/fetcher.js +244 -0
  97. package/dist/docs/fetcher.js.map +1 -0
  98. package/dist/docs/reference.d.ts +37 -0
  99. package/dist/docs/reference.d.ts.map +1 -0
  100. package/dist/docs/reference.js +108 -0
  101. package/dist/docs/reference.js.map +1 -0
  102. package/dist/docs/search.d.ts +194 -0
  103. package/dist/docs/search.d.ts.map +1 -0
  104. package/dist/docs/search.js +733 -0
  105. package/dist/docs/search.js.map +1 -0
  106. package/dist/http-server.d.ts.map +1 -1
  107. package/dist/http-server.js +52 -5
  108. package/dist/http-server.js.map +1 -1
  109. package/dist/index.d.ts +8 -9
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +35 -1035
  112. package/dist/index.js.map +1 -1
  113. package/dist/instructions.d.ts +15 -0
  114. package/dist/instructions.d.ts.map +1 -0
  115. package/dist/instructions.js +26 -0
  116. package/dist/instructions.js.map +1 -0
  117. package/dist/tools/defs/attributes.d.ts +6 -0
  118. package/dist/tools/defs/attributes.d.ts.map +1 -0
  119. package/dist/tools/defs/attributes.js +85 -0
  120. package/dist/tools/defs/attributes.js.map +1 -0
  121. package/dist/tools/defs/docs.d.ts +17 -0
  122. package/dist/tools/defs/docs.d.ts.map +1 -0
  123. package/dist/tools/defs/docs.js +151 -0
  124. package/dist/tools/defs/docs.js.map +1 -0
  125. package/dist/tools/defs/execute.d.ts +6 -0
  126. package/dist/tools/defs/execute.d.ts.map +1 -0
  127. package/dist/tools/defs/execute.js +21 -0
  128. package/dist/tools/defs/execute.js.map +1 -0
  129. package/dist/tools/defs/inspection.d.ts +7 -0
  130. package/dist/tools/defs/inspection.d.ts.map +1 -0
  131. package/dist/tools/defs/inspection.js +202 -0
  132. package/dist/tools/defs/inspection.js.map +1 -0
  133. package/dist/tools/defs/objects.d.ts +6 -0
  134. package/dist/tools/defs/objects.d.ts.map +1 -0
  135. package/dist/tools/defs/objects.js +111 -0
  136. package/dist/tools/defs/objects.js.map +1 -0
  137. package/dist/tools/defs/properties.d.ts +6 -0
  138. package/dist/tools/defs/properties.d.ts.map +1 -0
  139. package/dist/tools/defs/properties.js +71 -0
  140. package/dist/tools/defs/properties.js.map +1 -0
  141. package/dist/tools/defs/runtime.d.ts +6 -0
  142. package/dist/tools/defs/runtime.d.ts.map +1 -0
  143. package/dist/tools/defs/runtime.js +145 -0
  144. package/dist/tools/defs/runtime.js.map +1 -0
  145. package/dist/tools/defs/scripts.d.ts +18 -0
  146. package/dist/tools/defs/scripts.d.ts.map +1 -0
  147. package/dist/tools/defs/scripts.js +163 -0
  148. package/dist/tools/defs/scripts.js.map +1 -0
  149. package/dist/tools/defs/tags.d.ts +6 -0
  150. package/dist/tools/defs/tags.d.ts.map +1 -0
  151. package/dist/tools/defs/tags.js +74 -0
  152. package/dist/tools/defs/tags.js.map +1 -0
  153. package/dist/tools/defs/visual.d.ts +7 -0
  154. package/dist/tools/defs/visual.d.ts.map +1 -0
  155. package/dist/tools/defs/visual.js +208 -0
  156. package/dist/tools/defs/visual.js.map +1 -0
  157. package/dist/tools/index.d.ts +101 -25
  158. package/dist/tools/index.d.ts.map +1 -1
  159. package/dist/tools/index.js +580 -63
  160. package/dist/tools/index.js.map +1 -1
  161. package/dist/tools/nudges.d.ts +25 -0
  162. package/dist/tools/nudges.d.ts.map +1 -0
  163. package/dist/tools/nudges.js +34 -0
  164. package/dist/tools/nudges.js.map +1 -0
  165. package/dist/tools/registry.d.ts +20 -0
  166. package/dist/tools/registry.d.ts.map +1 -0
  167. package/dist/tools/registry.js +65 -0
  168. package/dist/tools/registry.js.map +1 -0
  169. package/dist/tools/types.d.ts +24 -0
  170. package/dist/tools/types.d.ts.map +1 -0
  171. package/dist/tools/types.js +2 -0
  172. package/dist/tools/types.js.map +1 -0
  173. package/package.json +7 -6
  174. package/studio-plugin/MCPPlugin.rbxmx +3 -238
  175. 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":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAqC7B,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB;;;;;;;;;;;;GAYG;AACH,IAAI,SAAS,GAAkB,IAAI,CAAC;AACpC,MAAM,UAAU,eAAe;IAC7B,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAChD,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,mEAAmE;IACnE,mEAAmE;IACnE,uEAAuE;IACvE,SAAS,GAAG,eAAe,EAAE,CAAC;IAC9B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1E,MAAM,OAAO,GAAG,sBAAsB,CAAC;IACvC,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC/D,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YACrF,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,+CAA+C;YAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;YACvC,MAAM,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC5E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACvC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAC;QAC3C,IAAI,MAAM,CAAC,aAAa,KAAK,cAAc,EAAE,CAAC;YAC5C,6DAA6D;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACxC,oEAAoE;QACpE,+BAA+B;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAqC;IACrF,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAa,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;IAClE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB;IACnD,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAW;IAChD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,UAAU,IAAI,CAAC,CAAS;QAC3B,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;gBACpB,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;iBAAM,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;gBACtB,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAC9B,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC;gBACrB,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS;gBACX,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -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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"}