@nbtca/docs 0.2.3 → 0.3.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 CHANGED
@@ -1,10 +1,8 @@
1
1
  # @nbtca/docs
2
2
 
3
- Data-only library for the [NBTCA documents repository](https://github.com/nbtca/documents).
4
- Fetches directory listings and raw markdown files from GitHub with built-in TTL caching,
5
- stale-on-error fallback, and rate-limit handling.
6
-
7
- Rendering is the consumer's job (e.g. `@nbtca/prompt`).
3
+ Typed GitHub client for the [NBTCA documents repository](https://github.com/nbtca/documents).
4
+ It lists Markdown documents, reads raw content, caches successful responses, and falls back to
5
+ stale data after transient failures. Rendering remains the consumer's responsibility.
8
6
 
9
7
  ## Install
10
8
 
@@ -17,53 +15,63 @@ npm install @nbtca/docs
17
15
  ```ts
18
16
  import { createDocsClient } from '@nbtca/docs';
19
17
 
20
- const docs = createDocsClient(); // defaults to nbtca/documents@main
18
+ const docs = createDocsClient();
21
19
 
22
- const items = await docs.listDir('tutorial'); // DocItem[]
23
- const all = await docs.listAll(); // all markdown DocItem[]
24
- const md = await docs.getFile('repair/guide.md'); // string (raw markdown)
25
- ```
26
-
27
- Custom target:
28
-
29
- ```ts
30
- const docs = createDocsClient({
31
- owner: 'my-org',
32
- repo: 'my-docs',
33
- branch: 'main',
34
- token: process.env.GITHUB_TOKEN,
35
- });
20
+ const sections = await docs.listDir();
21
+ const documents = await docs.listAll();
22
+ const markdown = await docs.getFile('repair/guide.md');
23
+ const page = await docs.getDocument('repair/index.md');
24
+ const matches = await docs.search('repair', { pathPrefix: 'repair' });
36
25
  ```
37
26
 
38
27
  ## API
39
28
 
40
29
  ### `createDocsClient(options?)`
41
30
 
42
- | Option | Default | Description |
43
- |---|---|---|
44
- | `owner` | `'nbtca'` | GitHub org/user |
45
- | `repo` | `'documents'` | Repository name |
46
- | `branch` | `'main'` | Branch or ref |
47
- | `token` | `GITHUB_TOKEN` env | Auth token (raises rate limit) |
48
- | `cacheTtlMs.dir` | `300000` (5 min) | Directory listing cache TTL |
49
- | `cacheTtlMs.file` | `600000` (10 min) | File content cache TTL |
31
+ | Option | Default | Description |
32
+ | ----------------- | ---------------------------- | ---------------------------- |
33
+ | `owner` | `'nbtca'` | GitHub owner |
34
+ | `repo` | `'documents'` | Repository name |
35
+ | `branch` | `'main'` | Branch name or ref |
36
+ | `token` | `GITHUB_TOKEN` or `GH_TOKEN` | GitHub token |
37
+ | `cacheTtlMs.dir` | `300000` | Directory and tree cache TTL |
38
+ | `cacheTtlMs.file` | `600000` | File cache TTL |
50
39
 
51
40
  ### `docs.listDir(path?)`
52
41
 
53
- Returns `DocItem[]` for the given path (root if omitted).
54
- Filters out hidden files, non-markdown files, and repository metadata.
42
+ Lists directories and Markdown files at a repository-relative path. The root path is used when
43
+ `path` is omitted.
55
44
 
56
45
  ### `docs.getFile(path)`
57
46
 
58
- Returns raw markdown as a string. Falls back to stale cache on network error.
47
+ Returns raw file content.
59
48
 
60
49
  ### `docs.listAll()`
61
50
 
62
- Returns every markdown file in the repository through GitHub's recursive tree API.
51
+ Lists every Markdown file through GitHub's recursive tree API.
52
+
53
+ ### `docs.listSections()`
54
+
55
+ Returns top-level content sections with document counts and optional index paths.
56
+
57
+ ### `docs.getDocument(path)`
58
+
59
+ Returns content with its route, section, title, summary, and semantic component attributes. Component
60
+ metadata covers `PageHero`, `FactStrip`, `LinkCard`, `Split`, `TimelineEntry`, and `Figure` without
61
+ imposing a renderer.
62
+
63
+ ### `docs.search(query, options?)`
64
+
65
+ Searches paths, titles, summaries, Markdown text, and semantic component attributes. Results are
66
+ ranked and include excerpts. Use `pathPrefix` to scope a search and `limit` to cap results.
67
+
68
+ ### `docs.clear()`
69
+
70
+ Clears all cached values and in-flight request bookkeeping.
63
71
 
64
72
  ### `DocsFetchError`
65
73
 
66
- Thrown when a fetch fails with no stale cache available. Has `.path` and `.status` fields.
74
+ Thrown when a request fails without usable stale data. Exposes `path` and HTTP `status`.
67
75
 
68
76
  ## License
69
77
 
package/dist/cache.d.ts CHANGED
@@ -9,4 +9,3 @@ export declare class TtlCache<T> {
9
9
  clear(): void;
10
10
  private evictOldest;
11
11
  }
12
- //# sourceMappingURL=cache.d.ts.map
package/dist/cache.js CHANGED
@@ -31,4 +31,3 @@ export class TtlCache {
31
31
  this.map.delete(key);
32
32
  }
33
33
  }
34
- //# sourceMappingURL=cache.js.map
package/dist/client.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  import type { DocsClient, DocsClientOptions } from './types.js';
2
2
  export declare function createDocsClient(options?: DocsClientOptions): DocsClient;
3
- //# sourceMappingURL=client.d.ts.map
package/dist/client.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { TtlCache } from './cache.js';
2
+ import { parseDoc, searchDoc } from './content.js';
2
3
  import { DocsFetchError } from './types.js';
3
4
  const DEFAULTS = {
4
5
  owner: 'nbtca',
@@ -7,15 +8,40 @@ const DEFAULTS = {
7
8
  dirTtlMs: 5 * 60 * 1000,
8
9
  fileTtlMs: 10 * 60 * 1000,
9
10
  };
10
- const SKIP = new Set(['.github', '.husky', '.vitepress', '.vscode', 'node_modules',
11
- 'assets', 'public', 'scripts', 'utils', 'package.json', 'pnpm-lock.yaml',
12
- 'tsconfig.json', 'eslint.config.mjs', '.nvmrc', '.gitignore',
13
- '.markdownlint-cli2.jsonc', 'CONTRIBUTING.md', 'CONTEXT.md']);
11
+ const SKIP = new Set([
12
+ '.github',
13
+ '.husky',
14
+ '.vitepress',
15
+ '.vscode',
16
+ 'node_modules',
17
+ 'assets',
18
+ 'public',
19
+ 'scripts',
20
+ 'utils',
21
+ 'package.json',
22
+ 'pnpm-lock.yaml',
23
+ 'tsconfig.json',
24
+ 'eslint.config.mjs',
25
+ '.nvmrc',
26
+ '.gitignore',
27
+ '.markdownlint-cli2.jsonc',
28
+ 'CONTRIBUTING.md',
29
+ 'CONTEXT.md',
30
+ 'README.md',
31
+ 'docs',
32
+ ]);
33
+ const SEARCH_CONCURRENCY = 6;
34
+ const SEARCH_RESULT_LIMIT = 20;
14
35
  function filterAndSort(raw) {
15
36
  return raw
16
- .filter(i => !i.name.startsWith('.') && !SKIP.has(i.name) &&
17
- (i.type === 'dir' || (i.type === 'file' && i.name.endsWith('.md'))))
18
- .map(i => ({ name: i.name, path: i.path, type: (i.type === 'dir' ? 'dir' : 'file') }))
37
+ .filter((item) => !item.name.startsWith('.') &&
38
+ !SKIP.has(item.name) &&
39
+ (item.type === 'dir' || (item.type === 'file' && item.name.endsWith('.md'))))
40
+ .map((item) => ({
41
+ name: item.name,
42
+ path: item.path,
43
+ type: item.type === 'dir' ? 'dir' : 'file',
44
+ }))
19
45
  .sort((a, b) => {
20
46
  if (a.type !== b.type)
21
47
  return a.type === 'dir' ? -1 : 1;
@@ -24,54 +50,108 @@ function filterAndSort(raw) {
24
50
  }
25
51
  function filterTree(items) {
26
52
  return items
27
- .filter(i => {
28
- const parts = i.path.split('/');
29
- if (parts.some(p => p.startsWith('.') || SKIP.has(p)))
53
+ .filter((item) => {
54
+ const parts = item.path.split('/');
55
+ if (parts.some((part) => part.startsWith('.') || SKIP.has(part)))
30
56
  return false;
31
- // Only return .md files; directories are navigated via listDir
32
- return i.type === 'blob' && i.path.endsWith('.md');
57
+ return item.type === 'blob' && item.path.endsWith('.md');
33
58
  })
34
- .map(i => ({
35
- name: i.path.split('/').pop(),
36
- path: i.path,
59
+ .map((item) => ({
60
+ name: item.path.slice(item.path.lastIndexOf('/') + 1),
61
+ path: item.path,
37
62
  type: 'file',
38
63
  }))
39
64
  .sort((a, b) => a.path.localeCompare(b.path));
40
65
  }
41
66
  function copyItems(items) {
42
- return items.map(item => ({ ...item }));
67
+ return items.map((item) => ({ ...item }));
68
+ }
69
+ function sectionsFromItems(items) {
70
+ const sections = new Map();
71
+ for (const item of items) {
72
+ const separator = item.path.indexOf('/');
73
+ if (separator < 1)
74
+ continue;
75
+ const path = item.path.slice(0, separator);
76
+ const current = sections.get(path) ?? { count: 0, path };
77
+ current.count += 1;
78
+ if (item.path === `${path}/index.md`)
79
+ current.indexPath = item.path;
80
+ sections.set(path, current);
81
+ }
82
+ return [...sections.values()]
83
+ .map((section) => ({ ...section }))
84
+ .sort((left, right) => left.path.localeCompare(right.path));
85
+ }
86
+ function searchLimit(value) {
87
+ const limit = value ?? SEARCH_RESULT_LIMIT;
88
+ if (!Number.isSafeInteger(limit) || limit < 0) {
89
+ throw new RangeError('limit must be a non-negative safe integer');
90
+ }
91
+ return limit;
92
+ }
93
+ async function mapConcurrent(values, concurrency, map) {
94
+ const results = new Array(values.length);
95
+ let nextIndex = 0;
96
+ async function worker() {
97
+ for (;;) {
98
+ const index = nextIndex;
99
+ nextIndex += 1;
100
+ if (index >= values.length)
101
+ return;
102
+ const value = values[index];
103
+ if (value !== undefined)
104
+ results[index] = await map(value);
105
+ }
106
+ }
107
+ const workers = Math.min(concurrency, values.length);
108
+ await Promise.all(Array.from({ length: workers }, worker));
109
+ return results;
43
110
  }
44
111
  function encodePath(path) {
45
- return path.split('/').map(encodeURIComponent).join('/');
112
+ return path
113
+ .split('/')
114
+ .map((segment) => encodeURIComponent(segment))
115
+ .join('/');
46
116
  }
47
117
  function assertRepositoryPath(path, allowEmpty) {
48
118
  if (allowEmpty && path === '')
49
119
  return;
50
120
  const parts = path.split('/');
51
- if (path === ''
52
- || path.startsWith('/')
53
- || path.endsWith('/')
54
- || path.includes('\\')
55
- || parts.some(part => part === '' || part === '.' || part === '..')) {
121
+ if (path === '' ||
122
+ path.startsWith('/') ||
123
+ path.endsWith('/') ||
124
+ path.includes('\\') ||
125
+ parts.some((part) => part === '' || part === '.' || part === '..')) {
56
126
  throw new TypeError('path must be a normalized repository-relative path');
57
127
  }
58
128
  }
129
+ function hasControlCharacter(value) {
130
+ for (const character of value) {
131
+ const code = character.charCodeAt(0);
132
+ if (code <= 0x1f || code === 0x7f)
133
+ return true;
134
+ }
135
+ return false;
136
+ }
59
137
  function assertRepositoryCoordinate(value, name) {
60
- if (value === ''
61
- || value !== value.trim()
62
- || value === '.'
63
- || value === '..'
64
- || /[\/\\\u0000-\u001f\u007f]/.test(value)) {
138
+ if (value === '' ||
139
+ value !== value.trim() ||
140
+ value === '.' ||
141
+ value === '..' ||
142
+ value.includes('/') ||
143
+ value.includes('\\') ||
144
+ hasControlCharacter(value)) {
65
145
  throw new TypeError(`${name} must be a valid GitHub repository coordinate`);
66
146
  }
67
147
  }
68
148
  function assertBranchRef(value) {
69
149
  const parts = value.split('/');
70
- if (value === ''
71
- || value !== value.trim()
72
- || value.includes('\\')
73
- || /[\u0000-\u001f\u007f]/.test(value)
74
- || parts.some(part => part === '' || part === '.' || part === '..')) {
150
+ if (value === '' ||
151
+ value !== value.trim() ||
152
+ value.includes('\\') ||
153
+ hasControlCharacter(value) ||
154
+ parts.some((part) => part === '' || part === '.' || part === '..')) {
75
155
  throw new TypeError('branch must be a valid Git ref');
76
156
  }
77
157
  }
@@ -85,16 +165,56 @@ function cacheTtl(value, fallback, name) {
85
165
  function isTransientStatus(status) {
86
166
  return status === 408 || status === 429 || status >= 500;
87
167
  }
168
+ function isSecondaryRateLimitMessage(value) {
169
+ if (!isRecord(value) || typeof value.message !== 'string')
170
+ return false;
171
+ const message = value.message.toLowerCase().replace(/\s+/g, ' ').trim();
172
+ return (/\b(?:exceeded|hit|triggered) (?:a |the )?secondary rate limit\b/.test(message) ||
173
+ /\bsecondary rate limit (?:was |has been )?(?:exceeded|hit|triggered)\b/.test(message));
174
+ }
175
+ async function isTransientResponse(response) {
176
+ if (isTransientStatus(response.status))
177
+ return true;
178
+ if (response.status !== 403)
179
+ return false;
180
+ if (response.headers.get('x-ratelimit-remaining') === '0' ||
181
+ response.headers.get('retry-after') !== null) {
182
+ return true;
183
+ }
184
+ try {
185
+ return isSecondaryRateLimitMessage(await response.clone().json());
186
+ }
187
+ catch {
188
+ return false;
189
+ }
190
+ }
191
+ function reject(error) {
192
+ return Promise.reject(error instanceof Error ? error : new Error('Operation failed with a non-error value'));
193
+ }
194
+ function cancelUnusedResponseBody(response) {
195
+ try {
196
+ if (!response || response.bodyUsed)
197
+ return;
198
+ const body = response.body;
199
+ if (!body)
200
+ return;
201
+ void body.cancel().catch(() => undefined);
202
+ }
203
+ catch {
204
+ // Cleanup must not override the request result.
205
+ }
206
+ }
88
207
  function isRecord(value) {
89
208
  return typeof value === 'object' && value !== null;
90
209
  }
91
210
  function isGitHubItem(value) {
92
- return isRecord(value) && typeof value.name === 'string' &&
93
- typeof value.path === 'string' && typeof value.type === 'string';
211
+ return (isRecord(value) &&
212
+ typeof value.name === 'string' &&
213
+ typeof value.path === 'string' &&
214
+ typeof value.type === 'string');
94
215
  }
95
216
  function isGitHubTreeItem(value) {
96
- return isRecord(value) && typeof value.path === 'string' &&
97
- typeof value.type === 'string';
217
+ return isRecord(value) && typeof value.path === 'string' && typeof value.type === 'string';
98
218
  }
99
219
  function parseContentsResponse(value) {
100
220
  if (!Array.isArray(value) || !value.every(isGitHubItem)) {
@@ -103,8 +223,10 @@ function parseContentsResponse(value) {
103
223
  return value;
104
224
  }
105
225
  function parseTreeResponse(value) {
106
- if (!isRecord(value) || typeof value.truncated !== 'boolean' ||
107
- !Array.isArray(value.tree) || !value.tree.every(isGitHubTreeItem)) {
226
+ if (!isRecord(value) ||
227
+ typeof value.truncated !== 'boolean' ||
228
+ !Array.isArray(value.tree) ||
229
+ !value.tree.every(isGitHubTreeItem)) {
108
230
  throw new TypeError('Invalid GitHub tree response');
109
231
  }
110
232
  return { tree: value.tree, truncated: value.truncated };
@@ -116,45 +238,51 @@ export function createDocsClient(options = {}) {
116
238
  assertRepositoryCoordinate(owner, 'owner');
117
239
  assertRepositoryCoordinate(repo, 'repo');
118
240
  assertBranchRef(branch);
119
- const token = options.token ?? (typeof process !== 'undefined'
120
- ? (process.env['GITHUB_TOKEN'] ?? process.env['GH_TOKEN'])
121
- : undefined);
241
+ const token = options.token ??
242
+ (typeof process !== 'undefined'
243
+ ? (process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN)
244
+ : undefined);
122
245
  const apiRepoUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;
123
246
  const rawRepoUrl = `https://raw.githubusercontent.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;
124
247
  const encodedBranch = encodeURIComponent(branch);
125
248
  const dirTtlMs = cacheTtl(options.cacheTtlMs?.dir, DEFAULTS.dirTtlMs, 'cacheTtlMs.dir');
126
249
  const fileTtlMs = cacheTtl(options.cacheTtlMs?.file, DEFAULTS.fileTtlMs, 'cacheTtlMs.file');
127
250
  const dirCache = new TtlCache(dirTtlMs, 30);
128
- const fileCache = new TtlCache(fileTtlMs, 50);
251
+ const fileCache = new TtlCache(fileTtlMs, 200);
129
252
  const treeCache = new TtlCache(dirTtlMs, 1);
130
253
  const dirRequests = new Map();
131
254
  const fileRequests = new Map();
132
255
  const treeRequests = new Map();
133
256
  let cacheGeneration = 0;
134
257
  function headers() {
135
- const h = { 'Accept': 'application/vnd.github.v3+json' };
258
+ const requestHeaders = {
259
+ Accept: 'application/vnd.github.v3+json',
260
+ };
136
261
  if (token)
137
- h['Authorization'] = `Bearer ${token}`;
138
- return h;
262
+ requestHeaders.Authorization = `Bearer ${token}`;
263
+ return requestHeaders;
139
264
  }
140
265
  async function withResponse(url, timeoutMs, consume) {
141
266
  const ctrl = new AbortController();
142
- const timer = setTimeout(() => ctrl.abort(), timeoutMs);
267
+ const timer = setTimeout(() => {
268
+ ctrl.abort();
269
+ }, timeoutMs);
270
+ let response;
143
271
  try {
144
- const response = await fetch(url, { signal: ctrl.signal, headers: headers() });
272
+ response = await fetch(url, { signal: ctrl.signal, headers: headers() });
145
273
  return await consume(response);
146
274
  }
147
275
  finally {
148
276
  clearTimeout(timer);
277
+ // Not awaited: a custom transport's cancel may never settle.
278
+ cancelUnusedResponseBody(response);
149
279
  }
150
280
  }
151
- function recoverFailure(cache, key, path, error, copy = value => value) {
281
+ function recoverFailure(cache, key, path, error, copy = (value) => value) {
152
282
  const stale = cache.getStale(key);
153
283
  if (stale !== undefined)
154
284
  return copy(stale);
155
- const message = error instanceof Error && error.name === 'AbortError'
156
- ? 'Request timed out'
157
- : String(error);
285
+ const message = error instanceof Error && error.name === 'AbortError' ? 'Request timed out' : String(error);
158
286
  throw new DocsFetchError(path, null, message);
159
287
  }
160
288
  function shareRequest(requests, key, load) {
@@ -170,27 +298,27 @@ export function createDocsClient(options = {}) {
170
298
  requests.set(key, request);
171
299
  return request;
172
300
  }
173
- async function loadDir(path, key, generation) {
301
+ async function loadDir(path, generation) {
174
302
  const url = `${apiRepoUrl}/contents/${encodePath(path)}?ref=${encodedBranch}`;
175
303
  try {
176
304
  return await withResponse(url, 10000, async (response) => {
177
305
  if (!response.ok) {
178
- const stale = dirCache.getStale(key);
179
- if (isTransientStatus(response.status) && stale !== undefined)
306
+ const stale = dirCache.getStale(path);
307
+ if (stale !== undefined && (await isTransientResponse(response)))
180
308
  return copyItems(stale);
181
- throw new DocsFetchError(path, response.status, `HTTP ${response.status}`);
309
+ throw new DocsFetchError(path, response.status, `HTTP ${String(response.status)}`);
182
310
  }
183
311
  const data = parseContentsResponse(await response.json());
184
312
  const items = filterAndSort(data);
185
313
  if (generation === cacheGeneration)
186
- dirCache.set(key, copyItems(items));
314
+ dirCache.set(path, copyItems(items));
187
315
  return items;
188
316
  });
189
317
  }
190
318
  catch (error) {
191
319
  if (error instanceof DocsFetchError)
192
320
  throw error;
193
- return recoverFailure(dirCache, key, path, error, copyItems);
321
+ return recoverFailure(dirCache, path, path, error, copyItems);
194
322
  }
195
323
  }
196
324
  function listDir(path = '') {
@@ -198,12 +326,12 @@ export function createDocsClient(options = {}) {
198
326
  assertRepositoryPath(path, true);
199
327
  }
200
328
  catch (error) {
201
- return Promise.reject(error);
329
+ return reject(error);
202
330
  }
203
331
  const hit = dirCache.get(path);
204
332
  if (hit)
205
333
  return Promise.resolve(copyItems(hit));
206
- return shareRequest(dirRequests, path, () => loadDir(path, path, cacheGeneration));
334
+ return shareRequest(dirRequests, path, () => loadDir(path, cacheGeneration)).then(copyItems);
207
335
  }
208
336
  async function loadAll(generation) {
209
337
  const key = '__tree__';
@@ -212,9 +340,9 @@ export function createDocsClient(options = {}) {
212
340
  return await withResponse(url, 20000, async (response) => {
213
341
  if (!response.ok) {
214
342
  const stale = treeCache.getStale(key);
215
- if (isTransientStatus(response.status) && stale !== undefined)
343
+ if (stale !== undefined && (await isTransientResponse(response)))
216
344
  return copyItems(stale);
217
- throw new DocsFetchError('', response.status, `HTTP ${response.status}`);
345
+ throw new DocsFetchError('', response.status, `HTTP ${String(response.status)}`);
218
346
  }
219
347
  const data = parseTreeResponse(await response.json());
220
348
  if (data.truncated) {
@@ -240,7 +368,10 @@ export function createDocsClient(options = {}) {
240
368
  const hit = treeCache.get(key);
241
369
  if (hit)
242
370
  return Promise.resolve(copyItems(hit));
243
- return shareRequest(treeRequests, key, () => loadAll(cacheGeneration));
371
+ return shareRequest(treeRequests, key, () => loadAll(cacheGeneration)).then(copyItems);
372
+ }
373
+ async function listSections() {
374
+ return sectionsFromItems(await listAll());
244
375
  }
245
376
  async function loadFile(path, generation) {
246
377
  const url = `${rawRepoUrl}/${encodedBranch}/${encodePath(path)}`;
@@ -248,9 +379,9 @@ export function createDocsClient(options = {}) {
248
379
  return await withResponse(url, 15000, async (response) => {
249
380
  if (!response.ok) {
250
381
  const stale = fileCache.getStale(path);
251
- if (isTransientStatus(response.status) && stale !== undefined)
382
+ if (stale !== undefined && (await isTransientResponse(response)))
252
383
  return stale;
253
- throw new DocsFetchError(path, response.status, `HTTP ${response.status}`);
384
+ throw new DocsFetchError(path, response.status, `HTTP ${String(response.status)}`);
254
385
  }
255
386
  const content = await response.text();
256
387
  if (generation === cacheGeneration)
@@ -269,13 +400,55 @@ export function createDocsClient(options = {}) {
269
400
  assertRepositoryPath(path, false);
270
401
  }
271
402
  catch (error) {
272
- return Promise.reject(error);
403
+ return reject(error);
273
404
  }
274
405
  const hit = fileCache.get(path);
275
406
  if (hit !== undefined)
276
407
  return Promise.resolve(hit);
277
408
  return shareRequest(fileRequests, path, () => loadFile(path, cacheGeneration));
278
409
  }
410
+ async function getDocument(path) {
411
+ if (!path.toLowerCase().endsWith('.md')) {
412
+ throw new TypeError('path must point to a Markdown document');
413
+ }
414
+ return parseDoc(path, await getFile(path));
415
+ }
416
+ async function search(query, options = {}) {
417
+ const normalizedQuery = query.trim();
418
+ if (!normalizedQuery)
419
+ throw new TypeError('query must not be empty');
420
+ const limit = searchLimit(options.limit);
421
+ const pathPrefix = options.pathPrefix ?? '';
422
+ assertRepositoryPath(pathPrefix, true);
423
+ if (limit === 0)
424
+ return [];
425
+ const all = await listAll();
426
+ const candidates = pathPrefix
427
+ ? all.filter((item) => item.path.startsWith(`${pathPrefix}/`))
428
+ : all;
429
+ let loaded = 0;
430
+ let firstFailure;
431
+ const matches = await mapConcurrent(candidates, SEARCH_CONCURRENCY, async (item) => {
432
+ try {
433
+ const document = await getDocument(item.path);
434
+ loaded += 1;
435
+ return searchDoc(document, normalizedQuery);
436
+ }
437
+ catch (error) {
438
+ if (error instanceof DocsFetchError) {
439
+ firstFailure ?? (firstFailure = error);
440
+ return null;
441
+ }
442
+ throw error;
443
+ }
444
+ });
445
+ if (loaded === 0 && firstFailure)
446
+ throw firstFailure;
447
+ return matches
448
+ .filter((result) => result !== null)
449
+ .sort((left, right) => right.score - left.score || left.path.localeCompare(right.path))
450
+ .slice(0, limit);
451
+ }
279
452
  function clear() {
280
453
  cacheGeneration += 1;
281
454
  dirCache.clear();
@@ -285,6 +458,5 @@ export function createDocsClient(options = {}) {
285
458
  fileRequests.clear();
286
459
  treeRequests.clear();
287
460
  }
288
- return { listDir, listAll, getFile, clear };
461
+ return { listDir, listAll, listSections, getFile, getDocument, search, clear };
289
462
  }
290
- //# sourceMappingURL=client.js.map
@@ -0,0 +1,3 @@
1
+ import type { DocPage, DocsSearchResult } from './types.js';
2
+ export declare function parseDoc(path: string, content: string): DocPage;
3
+ export declare function searchDoc(page: DocPage, query: string): DocsSearchResult | null;
@@ -0,0 +1,515 @@
1
+ const SUMMARY_LENGTH = 160;
2
+ const EXCERPT_LENGTH = 180;
3
+ const GRAPHEME_SEGMENTER = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
4
+ const LIST_ITEM = /^( {0,3}(?:[-+*]|\d{1,9}[.)]))([ \t]+)/;
5
+ const NORMALIZATION_CHUNK = 512;
6
+ // NFKC never joins these characters to what precedes them, so chunks split before them.
7
+ const NORMALIZATION_BOUNDARY = /[ -~\u4e00-\u9fff]/g;
8
+ const COMPONENT_ATTRIBUTES = {
9
+ Band: ['alt', 'source'],
10
+ Figure: ['alt', 'caption', 'date', 'source'],
11
+ LinkCard: ['title', 'desc', 'alt'],
12
+ PageHero: ['title', 'lede', 'alt', 'source'],
13
+ Split: ['heading', 'alt'],
14
+ TimelineEntry: ['year', 'title'],
15
+ };
16
+ function splitFrontmatter(content) {
17
+ const source = content.startsWith('\uFEFF') ? content.slice(1) : content;
18
+ const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(source);
19
+ if (!match)
20
+ return { body: source, frontmatter: '' };
21
+ return {
22
+ body: source.slice(match[0].length),
23
+ frontmatter: match[1] ?? '',
24
+ };
25
+ }
26
+ function decodeScalar(value) {
27
+ const trimmed = value.trim();
28
+ if (!trimmed || trimmed === '|' || trimmed === '>' || trimmed === '~' || trimmed === 'null') {
29
+ return undefined;
30
+ }
31
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
32
+ try {
33
+ const parsed = JSON.parse(trimmed);
34
+ return typeof parsed === 'string' ? parsed : undefined;
35
+ }
36
+ catch {
37
+ return trimmed.slice(1, -1);
38
+ }
39
+ }
40
+ if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
41
+ return trimmed.slice(1, -1).replace(/''/g, "'");
42
+ }
43
+ return trimmed;
44
+ }
45
+ function frontmatterValue(frontmatter, key) {
46
+ for (const line of frontmatter.split(/\r?\n/)) {
47
+ if (/^[ \t]/.test(line))
48
+ continue;
49
+ const separator = line.indexOf(':');
50
+ if (separator < 0 || line.slice(0, separator).trim() !== key)
51
+ continue;
52
+ return decodeScalar(line.slice(separator + 1));
53
+ }
54
+ return undefined;
55
+ }
56
+ function cleanInline(value) {
57
+ return value
58
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
59
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
60
+ .replace(/`([^`]+)`/g, '$1')
61
+ .replace(/<[^>]+>/g, '')
62
+ .replace(/[*_~]+/g, '')
63
+ .replace(/\s+/g, ' ')
64
+ .trim();
65
+ }
66
+ function quoteContainer(line) {
67
+ let depth = 0;
68
+ let rest = line;
69
+ for (;;) {
70
+ const prefix = /^ {0,3}>[ \t]?/.exec(rest)?.[0];
71
+ if (!prefix)
72
+ return { depth, rest };
73
+ depth += 1;
74
+ rest = rest.slice(prefix.length);
75
+ }
76
+ }
77
+ function transitionFence(line, current) {
78
+ const quote = quoteContainer(line);
79
+ let candidate = quote.rest;
80
+ let listIndent = 0;
81
+ if (current) {
82
+ if (quote.depth < current.quoteDepth)
83
+ return transitionFence(line, undefined);
84
+ if (quote.depth !== current.quoteDepth)
85
+ return { delimiter: false, fence: current };
86
+ if (current.listIndent > 0) {
87
+ const indentation = /^ */.exec(candidate)?.[0].length ?? 0;
88
+ if (candidate.trim() !== '' && indentation < current.listIndent) {
89
+ return transitionFence(line, undefined);
90
+ }
91
+ candidate = candidate.slice(Math.min(indentation, current.listIndent));
92
+ }
93
+ }
94
+ else {
95
+ listIndent = LIST_ITEM.exec(candidate)?.[0].length ?? 0;
96
+ candidate = candidate.slice(listIndent);
97
+ }
98
+ const match = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(candidate);
99
+ const sequence = match?.[1];
100
+ if (!sequence)
101
+ return { delimiter: false, fence: current };
102
+ const marker = sequence.startsWith('`') ? '`' : '~';
103
+ const suffix = match[2] ?? '';
104
+ if (!current) {
105
+ if (marker === '`' && suffix.includes('`')) {
106
+ return { delimiter: false, fence: undefined };
107
+ }
108
+ return {
109
+ delimiter: true,
110
+ fence: { length: sequence.length, listIndent, marker, quoteDepth: quote.depth },
111
+ };
112
+ }
113
+ if (marker === current.marker && sequence.length >= current.length && /^[ \t]*$/.test(suffix)) {
114
+ return { delimiter: true, fence: undefined };
115
+ }
116
+ return { delimiter: false, fence: current };
117
+ }
118
+ function indentWidth(line) {
119
+ let width = 0;
120
+ for (const character of line) {
121
+ if (character === ' ')
122
+ width += 1;
123
+ else if (character === '\t')
124
+ width += 4 - (width % 4);
125
+ else
126
+ break;
127
+ }
128
+ return width;
129
+ }
130
+ function proseLines(body) {
131
+ let fence;
132
+ let afterBreak = true;
133
+ let inCode = false;
134
+ let listIndent = 0;
135
+ return body.split(/\r?\n/).map((line) => {
136
+ const transition = transitionFence(line, fence);
137
+ fence = transition.fence;
138
+ if (transition.delimiter || fence) {
139
+ afterBreak = true;
140
+ inCode = false;
141
+ return undefined;
142
+ }
143
+ const candidate = quoteContainer(line).rest;
144
+ if (candidate.trim() === '') {
145
+ afterBreak = true;
146
+ return line;
147
+ }
148
+ const width = indentWidth(candidate);
149
+ if (afterBreak && width < listIndent)
150
+ listIndent = 0;
151
+ inCode = width - listIndent >= 4 && (afterBreak || inCode);
152
+ afterBreak = false;
153
+ if (inCode)
154
+ return undefined;
155
+ const item = LIST_ITEM.exec(candidate);
156
+ if (!item)
157
+ return line;
158
+ const marker = item[1]?.length ?? 0;
159
+ const padding = item[2] ?? '';
160
+ const rest = candidate.slice(item[0].length);
161
+ if (padding.includes('\t') || padding.length >= 5 || /^(?: {4}|\t)/.test(rest)) {
162
+ listIndent = marker + 1;
163
+ inCode = true;
164
+ return undefined;
165
+ }
166
+ listIndent = marker + padding.length;
167
+ return line;
168
+ });
169
+ }
170
+ function extractTitle(body) {
171
+ for (const line of proseLines(body)) {
172
+ if (line === undefined)
173
+ continue;
174
+ const match = /^#\s+(.+?)\s*$/.exec(line);
175
+ if (match?.[1])
176
+ return cleanInline(match[1].replace(/\s+#+\s*$/, ''));
177
+ }
178
+ return undefined;
179
+ }
180
+ function truncate(value, length) {
181
+ let end = 0;
182
+ let codePoints = 0;
183
+ for (const { index, segment } of GRAPHEME_SEGMENTER.segment(value)) {
184
+ codePoints += Array.from(segment).length;
185
+ if (codePoints > length)
186
+ return `${value.slice(0, end).trimEnd()}…`;
187
+ end = index + segment.length;
188
+ }
189
+ return value;
190
+ }
191
+ function extractSummary(body) {
192
+ const paragraphs = [];
193
+ let current = [];
194
+ let inContainer = false;
195
+ let inTag = false;
196
+ let hiddenTag;
197
+ const finishParagraph = () => {
198
+ if (current.length > 0)
199
+ paragraphs.push(current.join(' '));
200
+ current = [];
201
+ };
202
+ for (const rawLine of proseLines(body)) {
203
+ if (rawLine === undefined) {
204
+ finishParagraph();
205
+ continue;
206
+ }
207
+ const line = rawLine.trim();
208
+ if (hiddenTag) {
209
+ if (line.toLowerCase().includes(`</${hiddenTag}>`))
210
+ hiddenTag = undefined;
211
+ continue;
212
+ }
213
+ const hiddenStart = /^<(script|style)(?:\s|>)/i.exec(line)?.[1]?.toLowerCase();
214
+ if (hiddenStart === 'script' || hiddenStart === 'style') {
215
+ hiddenTag = line.toLowerCase().includes(`</${hiddenStart}>`) ? undefined : hiddenStart;
216
+ finishParagraph();
217
+ continue;
218
+ }
219
+ if (line.startsWith(':::')) {
220
+ inContainer = !inContainer;
221
+ finishParagraph();
222
+ continue;
223
+ }
224
+ if (inContainer)
225
+ continue;
226
+ if (inTag) {
227
+ if (line.endsWith('>'))
228
+ inTag = false;
229
+ continue;
230
+ }
231
+ if (line.startsWith('<')) {
232
+ if (!line.endsWith('>'))
233
+ inTag = true;
234
+ finishParagraph();
235
+ continue;
236
+ }
237
+ if (line === '') {
238
+ finishParagraph();
239
+ if (paragraphs.length > 0)
240
+ break;
241
+ continue;
242
+ }
243
+ if (/^(?:#{1,6}\s|>|\||[-*+]\s|\d+[.)]\s|(?:-{3,}|\*{3,}|_{3,})$)/.test(line)) {
244
+ finishParagraph();
245
+ continue;
246
+ }
247
+ current.push(line);
248
+ }
249
+ finishParagraph();
250
+ return truncate(cleanInline(paragraphs[0] ?? ''), SUMMARY_LENGTH);
251
+ }
252
+ function markdownText(body) {
253
+ return cleanInline(body
254
+ .replace(/<!--[\s\S]*?-->/g, ' ')
255
+ .replace(/<(?:script|style)(?:\s[^>]*)?>[\s\S]*?<\/(?:script|style)>/gi, ' ')
256
+ .replace(/<[A-Z][A-Za-z\d]*(?:\s[^>]*)?\s*\/\s*>/g, ' ')
257
+ .replace(/<\/?[A-Z][A-Za-z\d]*(?:\s[^>]*)?>/g, ' ')
258
+ .replace(/^---\s*$/gm, ' ')
259
+ .replace(/^:::[^\n]*$/gm, ' ')
260
+ .replace(/^\s*(?:#{1,6}|>|[-*+]|\d+[.)])\s+/gm, ''));
261
+ }
262
+ function parseAttributes(source) {
263
+ const attributes = {};
264
+ const pattern = /([:@A-Za-z_][:@\w.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
265
+ for (const match of source.matchAll(pattern)) {
266
+ const name = match[1];
267
+ if (!name)
268
+ continue;
269
+ attributes[name] = match[2] ?? match[3] ?? match[4] ?? true;
270
+ }
271
+ return attributes;
272
+ }
273
+ function componentSource(body) {
274
+ return proseLines(body)
275
+ .filter((line) => line !== undefined)
276
+ .join('\n')
277
+ .replace(/<!--[\s\S]*?-->/g, ' ');
278
+ }
279
+ function extractComponents(body) {
280
+ const components = [];
281
+ const pattern = /<([A-Z][A-Za-z\d]*)\b((?:[^>"']|"[^"]*"|'[^']*')*)\/?\s*>/g;
282
+ for (const match of componentSource(body).matchAll(pattern)) {
283
+ const name = match[1];
284
+ if (!name)
285
+ continue;
286
+ components.push({ attributes: parseAttributes(match[2] ?? ''), name });
287
+ }
288
+ return components;
289
+ }
290
+ function componentText(components) {
291
+ const values = [];
292
+ for (const component of components) {
293
+ if (component.name === 'FactStrip') {
294
+ const facts = component.attributes[':facts'];
295
+ if (typeof facts === 'string') {
296
+ for (const match of facts.matchAll(/\b(?:label|value)\s*:\s*(['"])(.*?)\1/gs)) {
297
+ if (match[2])
298
+ values.push(match[2].replace(/\\(['"\\])/g, '$1'));
299
+ }
300
+ }
301
+ }
302
+ for (const name of COMPONENT_ATTRIBUTES[component.name] ?? []) {
303
+ const value = component.attributes[name];
304
+ if (typeof value === 'string')
305
+ values.push(value);
306
+ }
307
+ }
308
+ return values.join(' ');
309
+ }
310
+ function routeFromPath(path) {
311
+ const withoutExtension = path.replace(/\.md$/i, '');
312
+ if (withoutExtension === 'index')
313
+ return '/';
314
+ if (withoutExtension.endsWith('/index'))
315
+ return `/${withoutExtension.slice(0, -5)}`;
316
+ return `/${withoutExtension}`;
317
+ }
318
+ function fallbackTitle(path) {
319
+ const name = path.slice(path.lastIndexOf('/') + 1);
320
+ return name.replace(/\.md$/i, '');
321
+ }
322
+ export function parseDoc(path, content) {
323
+ const { body, frontmatter } = splitFrontmatter(content);
324
+ const components = extractComponents(body);
325
+ const hero = components.find((component) => component.name === 'PageHero');
326
+ const heroTitle = hero?.attributes.title;
327
+ const heroSummary = hero?.attributes.lede;
328
+ const name = path.slice(path.lastIndexOf('/') + 1);
329
+ const title = cleanInline(extractTitle(body) ??
330
+ frontmatterValue(frontmatter, 'title') ??
331
+ (typeof heroTitle === 'string' ? heroTitle : ''));
332
+ const summary = cleanInline(frontmatterValue(frontmatter, 'summary') ??
333
+ (typeof heroSummary === 'string' ? heroSummary : extractSummary(body)));
334
+ return {
335
+ components,
336
+ content,
337
+ name,
338
+ path,
339
+ route: routeFromPath(path),
340
+ section: path.includes('/') ? (path.split('/')[0] ?? null) : null,
341
+ summary: truncate(summary, SUMMARY_LENGTH),
342
+ title: title || fallbackTitle(path),
343
+ };
344
+ }
345
+ function normalize(value) {
346
+ // Lowercasing keeps final sigma (ς) where a search for Σ yields σ.
347
+ return value
348
+ .normalize('NFKC')
349
+ .toLowerCase()
350
+ .replace(/\u03c2/g, '\u03c3');
351
+ }
352
+ function normalizeChunks(text) {
353
+ const offsets = [];
354
+ const sources = [];
355
+ const parts = [];
356
+ let length = 0;
357
+ for (let start = 0; start < text.length;) {
358
+ NORMALIZATION_BOUNDARY.lastIndex = start + NORMALIZATION_CHUNK;
359
+ const end = NORMALIZATION_BOUNDARY.exec(text)?.index ?? text.length;
360
+ const part = normalize(text.slice(start, end));
361
+ offsets.push(length);
362
+ sources.push(start);
363
+ parts.push(part);
364
+ length += part.length;
365
+ start = end;
366
+ }
367
+ return { offsets, sources, value: parts.join('') };
368
+ }
369
+ function lastIndexAtMost(count, target, valueAt) {
370
+ let low = 0;
371
+ let high = count - 1;
372
+ while (low < high) {
373
+ const middle = Math.ceil((low + high) / 2);
374
+ if (valueAt(middle) <= target)
375
+ low = middle;
376
+ else
377
+ high = middle - 1;
378
+ }
379
+ return low;
380
+ }
381
+ function graphemeAt(graphemes, position) {
382
+ const part = graphemes.containing(position);
383
+ if (!part)
384
+ return { codePoints: 0, end: position, start: position };
385
+ return {
386
+ codePoints: Array.from(part.segment).length,
387
+ end: part.index + part.segment.length,
388
+ start: part.index,
389
+ };
390
+ }
391
+ function normalizedLength(text, normalized, position) {
392
+ const { offsets, sources } = normalized;
393
+ const chunk = lastIndexAtMost(sources.length, position, (index) => sources[index] ?? 0);
394
+ const start = sources[chunk] ?? 0;
395
+ return (offsets[chunk] ?? 0) + normalize(text.slice(start, position)).length;
396
+ }
397
+ function sourceBoundary(text, normalized, graphemes, offset) {
398
+ const { offsets, sources } = normalized;
399
+ const chunk = lastIndexAtMost(offsets.length, offset, (index) => offsets[index] ?? 0);
400
+ const chunkEnd = sources[chunk + 1] ?? text.length;
401
+ const boundaries = [graphemeAt(graphemes, sources[chunk] ?? 0).start];
402
+ for (let position = boundaries[0] ?? 0; position < chunkEnd;) {
403
+ position = graphemeAt(graphemes, position).end;
404
+ boundaries.push(position);
405
+ }
406
+ const lengths = new Map();
407
+ const lengthAt = (index) => {
408
+ const position = boundaries[index] ?? 0;
409
+ const length = lengths.get(position) ?? normalizedLength(text, normalized, position);
410
+ lengths.set(position, length);
411
+ return length;
412
+ };
413
+ const index = lastIndexAtMost(boundaries.length, offset, lengthAt);
414
+ return { length: lengthAt(index), position: boundaries[index] ?? 0 };
415
+ }
416
+ function countMatches(value, term) {
417
+ let count = 0;
418
+ let offset = 0;
419
+ while (offset < value.length) {
420
+ const index = value.indexOf(term, offset);
421
+ if (index < 0)
422
+ break;
423
+ count += 1;
424
+ offset = index + Math.max(term.length, 1);
425
+ }
426
+ return count;
427
+ }
428
+ function excerpt(text, query, terms) {
429
+ if (!text)
430
+ return '';
431
+ const normalized = normalizeChunks(text);
432
+ const exactIndex = normalized.value.indexOf(query);
433
+ let matchIndex = exactIndex;
434
+ let matchLength = query.length;
435
+ if (matchIndex < 0) {
436
+ matchIndex = Number.POSITIVE_INFINITY;
437
+ for (const term of terms) {
438
+ const index = normalized.value.indexOf(term);
439
+ if (index >= 0 && index < matchIndex) {
440
+ matchIndex = index;
441
+ matchLength = term.length;
442
+ }
443
+ }
444
+ }
445
+ if (!Number.isFinite(matchIndex))
446
+ return truncate(text, EXCERPT_LENGTH);
447
+ const graphemes = GRAPHEME_SEGMENTER.segment(text);
448
+ const matchStart = sourceBoundary(text, normalized, graphemes, matchIndex).position;
449
+ const endOffset = Math.min(normalized.value.length, matchIndex + matchLength);
450
+ const floor = sourceBoundary(text, normalized, graphemes, endOffset);
451
+ const matchEnd = floor.length < endOffset ? graphemeAt(graphemes, floor.position).end : floor.position;
452
+ const matchCodePoints = Array.from(text.slice(matchStart, matchEnd)).length;
453
+ const contextLimit = Math.min(Math.floor(EXCERPT_LENGTH / 3), Math.max(0, EXCERPT_LENGTH - matchCodePoints));
454
+ let start = matchStart;
455
+ let contextCodePoints = 0;
456
+ while (start > 0) {
457
+ const previous = graphemeAt(graphemes, start - 1);
458
+ if (contextCodePoints + previous.codePoints > contextLimit)
459
+ break;
460
+ contextCodePoints += previous.codePoints;
461
+ start = previous.start;
462
+ }
463
+ let end = start;
464
+ let selectedCodePoints = 0;
465
+ while (end < text.length) {
466
+ const next = graphemeAt(graphemes, end);
467
+ if (end > start && selectedCodePoints + next.codePoints > EXCERPT_LENGTH)
468
+ break;
469
+ selectedCodePoints += next.codePoints;
470
+ end = next.end;
471
+ }
472
+ const prefix = start > 0 ? '…' : '';
473
+ const suffix = end < text.length ? '…' : '';
474
+ return `${prefix}${text.slice(start, end).trim()}${suffix}`;
475
+ }
476
+ export function searchDoc(page, query) {
477
+ const normalizedQuery = normalize(query.trim());
478
+ if (!normalizedQuery)
479
+ throw new TypeError('query must not be empty');
480
+ const terms = normalizedQuery.split(/\s+/).filter(Boolean);
481
+ const componentValue = componentText(page.components);
482
+ const bodyValue = markdownText(splitFrontmatter(page.content).body);
483
+ const textValue = `${componentValue} ${bodyValue}`.trim();
484
+ const title = normalize(page.title);
485
+ const summary = normalize(page.summary);
486
+ const path = normalize(page.path.replace(/[-_/]+/g, ' '));
487
+ const components = normalize(componentValue);
488
+ const text = normalize(bodyValue);
489
+ const combined = `${title}\n${summary}\n${path}\n${components}\n${text}`;
490
+ if (!terms.every((term) => combined.includes(term)))
491
+ return null;
492
+ let score = title === normalizedQuery ? 240 : 0;
493
+ score += countMatches(title, normalizedQuery) * 80;
494
+ score += countMatches(summary, normalizedQuery) * 40;
495
+ score += countMatches(components, normalizedQuery) * 60;
496
+ score += countMatches(path, normalizedQuery) * 24;
497
+ score += Math.min(countMatches(text, normalizedQuery), 5) * 10;
498
+ for (const term of terms) {
499
+ score += countMatches(title, term) * 24;
500
+ score += countMatches(summary, term) * 12;
501
+ score += countMatches(components, term) * 18;
502
+ score += countMatches(path, term) * 8;
503
+ score += Math.min(countMatches(text, term), 5) * 3;
504
+ }
505
+ return {
506
+ excerpt: excerpt(textValue, normalizedQuery, terms),
507
+ name: page.name,
508
+ path: page.path,
509
+ route: page.route,
510
+ score,
511
+ section: page.section,
512
+ summary: page.summary,
513
+ title: page.title,
514
+ };
515
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { createDocsClient } from './client.js';
2
- export type { DocItem, DocsClient, DocsClientOptions } from './types.js';
2
+ export { parseDoc } from './content.js';
3
+ export type { DocComponent, DocItem, DocPage, DocSection, DocsClient, DocsClientOptions, DocsSearchOptions, DocsSearchResult, } from './types.js';
3
4
  export { DocsFetchError } from './types.js';
4
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { createDocsClient } from './client.js';
2
+ export { parseDoc } from './content.js';
2
3
  export { DocsFetchError } from './types.js';
3
- //# sourceMappingURL=index.js.map
package/dist/types.d.ts CHANGED
@@ -3,6 +3,39 @@ export interface DocItem {
3
3
  path: string;
4
4
  type: 'file' | 'dir';
5
5
  }
6
+ export interface DocComponent {
7
+ attributes: Readonly<Record<string, string | true>>;
8
+ name: string;
9
+ }
10
+ export interface DocPage {
11
+ components: DocComponent[];
12
+ content: string;
13
+ name: string;
14
+ path: string;
15
+ route: string;
16
+ section: string | null;
17
+ summary: string;
18
+ title: string;
19
+ }
20
+ export interface DocSection {
21
+ count: number;
22
+ indexPath?: string;
23
+ path: string;
24
+ }
25
+ export interface DocsSearchOptions {
26
+ limit?: number;
27
+ pathPrefix?: string;
28
+ }
29
+ export interface DocsSearchResult {
30
+ excerpt: string;
31
+ name: string;
32
+ path: string;
33
+ route: string;
34
+ score: number;
35
+ section: string | null;
36
+ summary: string;
37
+ title: string;
38
+ }
6
39
  export interface DocsClientOptions {
7
40
  owner?: string;
8
41
  repo?: string;
@@ -16,7 +49,10 @@ export interface DocsClientOptions {
16
49
  export interface DocsClient {
17
50
  listDir(path?: string): Promise<DocItem[]>;
18
51
  listAll(): Promise<DocItem[]>;
52
+ listSections(): Promise<DocSection[]>;
19
53
  getFile(path: string): Promise<string>;
54
+ getDocument(path: string): Promise<DocPage>;
55
+ search(query: string, options?: DocsSearchOptions): Promise<DocsSearchResult[]>;
20
56
  clear(): void;
21
57
  }
22
58
  export declare class DocsFetchError extends Error {
@@ -24,4 +60,3 @@ export declare class DocsFetchError extends Error {
24
60
  readonly status: number | null;
25
61
  constructor(path: string, status: number | null, message: string);
26
62
  }
27
- //# sourceMappingURL=types.d.ts.map
package/dist/types.js CHANGED
@@ -6,4 +6,3 @@ export class DocsFetchError extends Error {
6
6
  this.name = 'DocsFetchError';
7
7
  }
8
8
  }
9
- //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nbtca/docs",
3
- "version": "0.2.3",
4
- "description": "Data-only library for the NBTCA documents repository",
3
+ "version": "0.3.1",
4
+ "description": "GitHub-backed document client for NBTCA",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -18,11 +18,16 @@
18
18
  "scripts": {
19
19
  "clean": "node --input-type=module --eval \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true })\"",
20
20
  "prebuild": "npm run clean",
21
- "build": "tsc",
21
+ "build": "tsc -p tsconfig.build.json",
22
+ "format": "prettier --write .",
23
+ "format:check": "prettier --check .",
24
+ "lint": "eslint . --max-warnings 0",
25
+ "typecheck": "tsc --noEmit",
22
26
  "test": "vitest run",
23
27
  "precheck:package": "npm run build",
24
28
  "check:package": "node scripts/check-package.mjs",
25
- "check": "npm test && npm run check:package && npm audit --audit-level=moderate",
29
+ "audit": "npm audit --audit-level=moderate",
30
+ "check": "npm run format:check && npm run lint && npm run typecheck && npm test && npm run check:package && npm run audit",
26
31
  "prepublishOnly": "npm run check"
27
32
  },
28
33
  "keywords": [
@@ -46,8 +51,15 @@
46
51
  "node": ">=20.12.0"
47
52
  },
48
53
  "devDependencies": {
49
- "@types/node": "^26.0.0",
50
- "typescript": "^5.4.0",
51
- "vitest": "^3.2.7"
54
+ "@eslint/js": "^9.39.5",
55
+ "@types/node": "20.12.12",
56
+ "eslint": "^9.39.5",
57
+ "eslint-config-prettier": "^10.1.8",
58
+ "globals": "^17.9.0",
59
+ "prettier": "^3.9.6",
60
+ "typescript": "^5.9.3",
61
+ "typescript-eslint": "^8.67.0",
62
+ "vite": "6.4.3",
63
+ "vitest": "^4.1.11"
52
64
  }
53
65
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAKA,qBAAa,QAAQ,CAAC,CAAC;IAInB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA+B;gBAGhC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAW;IAGvC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAO/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAIpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAKhC,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,WAAW;CAMpB"}
package/dist/cache.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,QAAQ;IAGnB,YACmB,KAAa,EACb,UAAkB,EAAE;QADpB,UAAK,GAAL,KAAK,CAAQ;QACb,YAAO,GAAP,OAAO,CAAa;QAJtB,QAAG,GAAG,IAAI,GAAG,EAAoB,CAAC;IAKhD,CAAC;IAEJ,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC;QACrD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAQ;QACvB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACjE,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,WAAW,EAAE,CAAC;IACvD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAEO,WAAW;QACjB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;aACnC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;aAC/C,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,MAAM;YAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnD,CAAC;CACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAsIzE,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU,CAqL5E"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE;QACX,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3C,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,qBAAa,cAAe,SAAQ,KAAK;aAErB,IAAI,EAAE,MAAM;aACZ,MAAM,EAAE,MAAM,GAAG,IAAI;gBADrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,IAAI,EACrC,OAAO,EAAE,MAAM;CAKlB"}
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAwBA,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YACkB,IAAY,EACZ,MAAqB,EACrC,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAAe;QAIrC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF"}