@microsoft/rayfin-docs 1.27.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/LICENSE +21 -0
- package/assets/catalog.json +132 -0
- package/dist/_internal/build-index.d.ts +14 -0
- package/dist/_internal/build-index.js +13 -0
- package/dist/catalog-types.d.ts +52 -0
- package/dist/catalog-types.js +21 -0
- package/dist/catalog.d.ts +85 -0
- package/dist/catalog.js +227 -0
- package/dist/discovery.d.ts +110 -0
- package/dist/discovery.js +402 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +422 -0
- package/dist/loader.d.ts +21 -0
- package/dist/loader.js +296 -0
- package/dist/search.d.ts +72 -0
- package/dist/search.js +254 -0
- package/dist/site-cjs/discovery.js +409 -0
- package/dist/site-cjs/package.json +1 -0
- package/dist/site-cjs/types.js +2 -0
- package/dist/types.d.ts +89 -0
- package/dist/types.js +2 -0
- package/package.json +62 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultTrust = void 0;
|
|
4
|
+
exports.hasExplicitDiscoverySource = hasExplicitDiscoverySource;
|
|
5
|
+
exports.discoverRayfinDocsPackages = discoverRayfinDocsPackages;
|
|
6
|
+
exports.validateRayfinDocsManifest = validateRayfinDocsManifest;
|
|
7
|
+
exports.docKindToModule = docKindToModule;
|
|
8
|
+
/**
|
|
9
|
+
* Per-package docs discovery - Phase 2 of `docs-package-architecture`.
|
|
10
|
+
*
|
|
11
|
+
* Walks a project's `node_modules` to find packages declaring the
|
|
12
|
+
* `rayfinDocs` field in their `package.json`. Returns a list of
|
|
13
|
+
* discovered package roots so {@link DocsService} can load each tree's
|
|
14
|
+
* markdown alongside any explicit `assetsRoot`.
|
|
15
|
+
*
|
|
16
|
+
* **Trust model.** Discovery is conservative by default: only
|
|
17
|
+
* `@microsoft/rayfin-*` scoped packages are auto-trusted. Anything else
|
|
18
|
+
* requires the caller to pass an explicit `trust` predicate. This
|
|
19
|
+
* matches the spec-review resolution that broad walking is too trusting
|
|
20
|
+
* (see `openspec/changes/docs-package-architecture/spec-review-findings.md`,
|
|
21
|
+
* finding B3).
|
|
22
|
+
*
|
|
23
|
+
* Discovery is intentionally explicit: callers choose either a fixed
|
|
24
|
+
* candidate list, workspace package roots, or all-installed node_modules
|
|
25
|
+
* walking via `scanInstalledPackages: true`.
|
|
26
|
+
*/
|
|
27
|
+
const fs_1 = require("fs");
|
|
28
|
+
const module_1 = require("module");
|
|
29
|
+
const path_1 = require("path");
|
|
30
|
+
const url_1 = require("url");
|
|
31
|
+
/** Default trust predicate. Auto-trusts the Microsoft Rayfin namespace. */
|
|
32
|
+
const defaultTrust = (packageName) => packageName.startsWith('@microsoft/rayfin-');
|
|
33
|
+
exports.defaultTrust = defaultTrust;
|
|
34
|
+
/**
|
|
35
|
+
* Returns whether callers already selected a discovery source.
|
|
36
|
+
* Wrappers use this to decide whether to inject their own bounded defaults.
|
|
37
|
+
*/
|
|
38
|
+
function hasExplicitDiscoverySource(options) {
|
|
39
|
+
return countDiscoverySources(options) > 0;
|
|
40
|
+
}
|
|
41
|
+
function discoverRayfinDocsPackages(options) {
|
|
42
|
+
const trust = options.trust ?? exports.defaultTrust;
|
|
43
|
+
const fromDir = options.from.startsWith('file:') || options.from.includes('://')
|
|
44
|
+
? (0, path_1.dirname)((0, url_1.fileURLToPath)(options.from))
|
|
45
|
+
: resolveExistingDir(options.from);
|
|
46
|
+
const requireFromBase = (0, module_1.createRequire)((0, path_1.join)(fromDir, 'noop.js'));
|
|
47
|
+
const nodeModulesChain = buildNodeModulesChain(fromDir);
|
|
48
|
+
const discovered = [];
|
|
49
|
+
const notInstalled = [];
|
|
50
|
+
const untrustedSkipped = [];
|
|
51
|
+
const invalidManifest = [];
|
|
52
|
+
const sourceCount = countDiscoverySources(options);
|
|
53
|
+
if (sourceCount === 0) {
|
|
54
|
+
throw new Error('discoverRayfinDocsPackages requires an explicit source: pass `candidates`, `packageRoots`, or `scanInstalledPackages: true`.');
|
|
55
|
+
}
|
|
56
|
+
if (sourceCount > 1) {
|
|
57
|
+
throw new Error('discoverRayfinDocsPackages requires exactly one explicit source: pass only one of `candidates`, `packageRoots`, or `scanInstalledPackages: true`.');
|
|
58
|
+
}
|
|
59
|
+
let packagesToRead;
|
|
60
|
+
if (options.packageRoots !== undefined) {
|
|
61
|
+
packagesToRead = resolvePackageRootJsons(options.packageRoots);
|
|
62
|
+
}
|
|
63
|
+
else if (options.candidates !== undefined) {
|
|
64
|
+
packagesToRead = resolveCandidatePackageJsons(options.candidates, requireFromBase, nodeModulesChain, notInstalled, untrustedSkipped, trust);
|
|
65
|
+
}
|
|
66
|
+
else if (options.scanInstalledPackages) {
|
|
67
|
+
packagesToRead = discoverInstalledPackageJsons(nodeModulesChain);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
throw new Error('Invariant violation: explicit discovery source count did not match selected source.');
|
|
71
|
+
}
|
|
72
|
+
for (const { packageName: candidate, pkgJsonPath } of packagesToRead) {
|
|
73
|
+
let pkgJson;
|
|
74
|
+
try {
|
|
75
|
+
pkgJson = JSON.parse((0, fs_1.readFileSync)(pkgJsonPath, 'utf8'));
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
invalidManifest.push({
|
|
79
|
+
packageName: candidate,
|
|
80
|
+
reason: `unreadable package.json: ${err.message}`,
|
|
81
|
+
});
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const parsedName = pkgJson?.name;
|
|
85
|
+
const packageName = typeof parsedName === 'string' ? parsedName : candidate;
|
|
86
|
+
const hasRayfinDocs = typeof pkgJson === 'object' &&
|
|
87
|
+
pkgJson !== null &&
|
|
88
|
+
'rayfinDocs' in pkgJson;
|
|
89
|
+
if (!hasRayfinDocs) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const parsedVersion = pkgJson?.version;
|
|
93
|
+
if (typeof parsedVersion !== 'string' || parsedVersion.length === 0) {
|
|
94
|
+
invalidManifest.push({
|
|
95
|
+
packageName,
|
|
96
|
+
reason: 'package.json must declare a non-empty string version when rayfinDocs is present',
|
|
97
|
+
});
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (!trust(packageName)) {
|
|
101
|
+
untrustedSkipped.push(packageName);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const validation = validateRayfinDocsManifest(pkgJson, candidate);
|
|
105
|
+
if (!validation.ok) {
|
|
106
|
+
if (validation.kind === 'missing') {
|
|
107
|
+
// Package is installed but doesn't declare rayfinDocs. This is
|
|
108
|
+
// not an error - it's the common case for any package that
|
|
109
|
+
// hasn't migrated yet. Don't surface as invalid.
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
invalidManifest.push({
|
|
113
|
+
packageName: candidate,
|
|
114
|
+
reason: validation.reason,
|
|
115
|
+
});
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const packageRoot = (0, path_1.dirname)(pkgJsonPath);
|
|
119
|
+
discovered.push({
|
|
120
|
+
packageName,
|
|
121
|
+
packageVersion: parsedVersion,
|
|
122
|
+
packageRoot,
|
|
123
|
+
manifest: validation.manifest,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return { discovered, notInstalled, untrustedSkipped, invalidManifest };
|
|
127
|
+
}
|
|
128
|
+
function countDiscoverySources(options) {
|
|
129
|
+
return [
|
|
130
|
+
options.packageRoots !== undefined,
|
|
131
|
+
options.candidates !== undefined,
|
|
132
|
+
options.scanInstalledPackages === true,
|
|
133
|
+
].filter(Boolean).length;
|
|
134
|
+
}
|
|
135
|
+
function resolvePackageRootJsons(packageRoots) {
|
|
136
|
+
return packageRoots.map((packageRoot) => ({
|
|
137
|
+
packageName: packageRoot,
|
|
138
|
+
pkgJsonPath: (0, path_1.join)(resolveExistingDir(packageRoot), 'package.json'),
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
function resolveCandidatePackageJsons(candidates, requireFromBase, nodeModulesChain, notInstalled, untrustedSkipped, trust) {
|
|
142
|
+
const resolved = [];
|
|
143
|
+
for (const candidate of candidates) {
|
|
144
|
+
if (!trust(candidate)) {
|
|
145
|
+
untrustedSkipped.push(candidate);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const pkgJsonPath = resolvePackageJson(requireFromBase, candidate, nodeModulesChain);
|
|
149
|
+
if (!pkgJsonPath) {
|
|
150
|
+
notInstalled.push(candidate);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
resolved.push({ packageName: candidate, pkgJsonPath });
|
|
154
|
+
}
|
|
155
|
+
return resolved;
|
|
156
|
+
}
|
|
157
|
+
function discoverInstalledPackageJsons(nodeModulesChain) {
|
|
158
|
+
const discovered = [];
|
|
159
|
+
const seen = new Set();
|
|
160
|
+
for (const dir of nodeModulesChain) {
|
|
161
|
+
const nodeModules = (0, path_1.join)(dir, 'node_modules');
|
|
162
|
+
if (!(0, fs_1.existsSync)(nodeModules) || !(0, fs_1.statSync)(nodeModules).isDirectory()) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
for (const entry of (0, fs_1.readdirSync)(nodeModules, { withFileTypes: true })) {
|
|
166
|
+
if ((!entry.isDirectory() && !entry.isSymbolicLink()) ||
|
|
167
|
+
entry.name.startsWith('.')) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (entry.name.startsWith('@')) {
|
|
171
|
+
const scopeRoot = (0, path_1.join)(nodeModules, entry.name);
|
|
172
|
+
for (const scoped of (0, fs_1.readdirSync)(scopeRoot, { withFileTypes: true })) {
|
|
173
|
+
if (!scoped.isDirectory() && !scoped.isSymbolicLink())
|
|
174
|
+
continue;
|
|
175
|
+
const packageName = `${entry.name}/${scoped.name}`;
|
|
176
|
+
const pkgJsonPath = (0, path_1.join)(scopeRoot, scoped.name, 'package.json');
|
|
177
|
+
if (!seen.has(packageName) && (0, fs_1.existsSync)(pkgJsonPath)) {
|
|
178
|
+
seen.add(packageName);
|
|
179
|
+
discovered.push({ packageName, pkgJsonPath });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const packageName = entry.name;
|
|
185
|
+
const pkgJsonPath = (0, path_1.join)(nodeModules, entry.name, 'package.json');
|
|
186
|
+
if (!seen.has(packageName) && (0, fs_1.existsSync)(pkgJsonPath)) {
|
|
187
|
+
seen.add(packageName);
|
|
188
|
+
discovered.push({ packageName, pkgJsonPath });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return discovered;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Validate the `rayfinDocs` field on a parsed `package.json`. Returns
|
|
197
|
+
* a discriminated result so callers can distinguish "no manifest"
|
|
198
|
+
* (silently skip) from "broken manifest" (surface as warning).
|
|
199
|
+
*
|
|
200
|
+
* Path validation rejects absolute paths and parent-directory
|
|
201
|
+
* traversal in `dir` - discovered packages must keep their docs
|
|
202
|
+
* inside their own root.
|
|
203
|
+
*/
|
|
204
|
+
function validateRayfinDocsManifest(pkgJson, packageName) {
|
|
205
|
+
if (!pkgJson || typeof pkgJson !== 'object') {
|
|
206
|
+
return {
|
|
207
|
+
ok: false,
|
|
208
|
+
kind: 'invalid',
|
|
209
|
+
reason: 'package.json is not an object',
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const rec = pkgJson;
|
|
213
|
+
const raw = rec['rayfinDocs'];
|
|
214
|
+
if (raw === undefined) {
|
|
215
|
+
return { ok: false, kind: 'missing' };
|
|
216
|
+
}
|
|
217
|
+
if (!raw || typeof raw !== 'object') {
|
|
218
|
+
return {
|
|
219
|
+
ok: false,
|
|
220
|
+
kind: 'invalid',
|
|
221
|
+
reason: '`rayfinDocs` field is not an object',
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const m = raw;
|
|
225
|
+
if (m['version'] !== 1) {
|
|
226
|
+
return {
|
|
227
|
+
ok: false,
|
|
228
|
+
kind: 'invalid',
|
|
229
|
+
reason: `unsupported rayfinDocs.version (got ${JSON.stringify(m['version'])}, expected 1); skipping ${packageName}`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (m['dir'] !== undefined && typeof m['dir'] !== 'string') {
|
|
233
|
+
return {
|
|
234
|
+
ok: false,
|
|
235
|
+
kind: 'invalid',
|
|
236
|
+
reason: '`rayfinDocs.dir` must be a non-empty string',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (m['dir'] === '') {
|
|
240
|
+
return {
|
|
241
|
+
ok: false,
|
|
242
|
+
kind: 'invalid',
|
|
243
|
+
reason: '`rayfinDocs.dir` must be a non-empty string',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const dir = m['dir'] ?? 'assets/docs';
|
|
247
|
+
if ((0, path_1.isAbsolute)(dir)) {
|
|
248
|
+
return {
|
|
249
|
+
ok: false,
|
|
250
|
+
kind: 'invalid',
|
|
251
|
+
reason: '`rayfinDocs.dir` must be a relative path inside the package root',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
// Reject `..` segments anywhere in the relative path. Segment-based
|
|
255
|
+
// check uniformly handles `..`, `./..`, `docs/..`, `a/../b`, and
|
|
256
|
+
// mixed separators.
|
|
257
|
+
const normalized = dir.replace(/\\/g, '/');
|
|
258
|
+
if (normalized.split('/').some((segment) => segment === '..')) {
|
|
259
|
+
return {
|
|
260
|
+
ok: false,
|
|
261
|
+
kind: 'invalid',
|
|
262
|
+
reason: '`rayfinDocs.dir` must not traverse outside the package root (no `..` segments)',
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (typeof m['module'] !== 'string' || m['module'].length === 0) {
|
|
266
|
+
return {
|
|
267
|
+
ok: false,
|
|
268
|
+
kind: 'invalid',
|
|
269
|
+
reason: '`rayfinDocs.module` must be a non-empty string',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (m['kind'] !== 'guide' &&
|
|
273
|
+
m['kind'] !== 'host' &&
|
|
274
|
+
m['kind'] !== 'api-reference') {
|
|
275
|
+
return {
|
|
276
|
+
ok: false,
|
|
277
|
+
kind: 'invalid',
|
|
278
|
+
reason: `\`rayfinDocs.kind\` must be one of "guide" | "host" | "api-reference" (got ${JSON.stringify(m['kind'])}). ` +
|
|
279
|
+
`If you need a new kind, request it in the rayfin-docs repo: ` +
|
|
280
|
+
`https://github.com/microsoft/project-rayfin/issues/new`,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
ok: true,
|
|
285
|
+
manifest: {
|
|
286
|
+
version: 1,
|
|
287
|
+
dir,
|
|
288
|
+
module: m['module'],
|
|
289
|
+
kind: m['kind'],
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Map a `DocKind` to the legacy `DocModule` value used throughout the
|
|
295
|
+
* existing CLI/MCP surfaces. Phase 2 keeps the `DocModule` union for
|
|
296
|
+
* back-compat; `--module ts-sdk` continues to mean "all api-reference
|
|
297
|
+
* docs" for end users.
|
|
298
|
+
*/
|
|
299
|
+
function docKindToModule(kind) {
|
|
300
|
+
switch (kind) {
|
|
301
|
+
case 'guide':
|
|
302
|
+
return 'guide';
|
|
303
|
+
case 'host':
|
|
304
|
+
return 'host';
|
|
305
|
+
case 'api-reference':
|
|
306
|
+
return 'ts-sdk';
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Compute the chain of `node_modules` ancestor directories from
|
|
311
|
+
* `fromDir` upward, capped at 16 levels. Used by the tier-3 fallback
|
|
312
|
+
* in {@link resolvePackageJson} to avoid re-walking the same ancestors
|
|
313
|
+
* once per candidate (`9 candidates × 16 walks = 144 stat calls` in
|
|
314
|
+
* the original implementation; this caches the chain to 16 stats).
|
|
315
|
+
*/
|
|
316
|
+
function buildNodeModulesChain(fromDir) {
|
|
317
|
+
const chain = [];
|
|
318
|
+
let dir = fromDir;
|
|
319
|
+
for (let i = 0; i < 16; i += 1) {
|
|
320
|
+
chain.push(dir);
|
|
321
|
+
const parent = (0, path_1.dirname)(dir);
|
|
322
|
+
if (parent === dir)
|
|
323
|
+
break;
|
|
324
|
+
dir = parent;
|
|
325
|
+
}
|
|
326
|
+
return chain;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Find a package's `package.json` path even when the package's
|
|
330
|
+
* `exports` field doesn't expose it as a subpath OR when the package
|
|
331
|
+
* is ESM-only (`exports."."` is `{ "import": ... }` without a CJS
|
|
332
|
+
* `default`/`require` branch).
|
|
333
|
+
*
|
|
334
|
+
* SDK packages with restricted `exports` typically don't list
|
|
335
|
+
* `./package.json` and so `require.resolve('<pkg>/package.json')`
|
|
336
|
+
* throws `ERR_PACKAGE_PATH_NOT_EXPORTED`. ESM-only packages additionally
|
|
337
|
+
* fail `require.resolve('<pkg>')` (CJS resolution can't see the
|
|
338
|
+
* `import:` branch). We finally fall back to walking the node_modules
|
|
339
|
+
* tree from the resolution base - this matches what every npm/pnpm
|
|
340
|
+
* tool does internally.
|
|
341
|
+
*
|
|
342
|
+
* Returns the absolute path to `package.json`, or `undefined` when the
|
|
343
|
+
* package isn't installed at all.
|
|
344
|
+
*/
|
|
345
|
+
function resolvePackageJson(req, packageName, nodeModulesChain) {
|
|
346
|
+
// Fast path: works when the package omits `exports` or explicitly
|
|
347
|
+
// includes `./package.json` in it.
|
|
348
|
+
try {
|
|
349
|
+
return req.resolve(`${packageName}/package.json`);
|
|
350
|
+
}
|
|
351
|
+
catch (err) {
|
|
352
|
+
const code = err?.code;
|
|
353
|
+
if (code === 'MODULE_NOT_FOUND') {
|
|
354
|
+
// Package isn't installed.
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
// ERR_PACKAGE_PATH_NOT_EXPORTED or similar: keep going.
|
|
358
|
+
}
|
|
359
|
+
// Second fall-back: resolve the main entry, then walk up directories
|
|
360
|
+
// searching for a package.json whose name matches.
|
|
361
|
+
try {
|
|
362
|
+
const entry = req.resolve(packageName);
|
|
363
|
+
let dir = (0, path_1.dirname)(entry);
|
|
364
|
+
for (let i = 0; i < 16; i += 1) {
|
|
365
|
+
const candidate = (0, path_1.join)(dir, 'package.json');
|
|
366
|
+
if ((0, fs_1.existsSync)(candidate)) {
|
|
367
|
+
try {
|
|
368
|
+
const parsed = JSON.parse((0, fs_1.readFileSync)(candidate, 'utf8'));
|
|
369
|
+
if (parsed.name === packageName) {
|
|
370
|
+
return candidate;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// keep walking
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const parent = (0, path_1.dirname)(dir);
|
|
378
|
+
if (parent === dir)
|
|
379
|
+
break;
|
|
380
|
+
dir = parent;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
// ESM-only package or similar: fall through to the node_modules walk.
|
|
385
|
+
}
|
|
386
|
+
// Third fall-back: walk the precomputed `node_modules` ancestor
|
|
387
|
+
// chain looking for `<ancestor>/node_modules/<packageName>/package.json`.
|
|
388
|
+
// This is what npm/pnpm/yarn tooling does internally and works
|
|
389
|
+
// regardless of how the package's `exports` are configured.
|
|
390
|
+
for (const dir of nodeModulesChain) {
|
|
391
|
+
const candidate = (0, path_1.join)(dir, 'node_modules', packageName, 'package.json');
|
|
392
|
+
if ((0, fs_1.existsSync)(candidate)) {
|
|
393
|
+
return candidate;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return undefined;
|
|
397
|
+
}
|
|
398
|
+
/** Internal: ensure `from` exists for `createRequire` when given a dir. */
|
|
399
|
+
function resolveExistingDir(from) {
|
|
400
|
+
const absolute = (0, path_1.resolve)(from);
|
|
401
|
+
if (!(0, fs_1.existsSync)(absolute)) {
|
|
402
|
+
throw new Error(`discoverRayfinDocsPackages: \`from\` path does not exist: ${absolute}`);
|
|
403
|
+
}
|
|
404
|
+
const st = (0, fs_1.statSync)(absolute);
|
|
405
|
+
if (!st.isDirectory()) {
|
|
406
|
+
throw new Error(`discoverRayfinDocsPackages: \`from\` must be a directory: ${absolute}`);
|
|
407
|
+
}
|
|
408
|
+
return absolute;
|
|
409
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "type": "commonjs" }\n
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export type DocModule = 'guide' | 'host' | 'ts-sdk';
|
|
2
|
+
export type DocKind = 'guide' | 'host' | 'api-reference';
|
|
3
|
+
export type DocSearchScope = 'docs' | 'symbols' | 'all';
|
|
4
|
+
/**
|
|
5
|
+
* `rayfinDocs` package.json field declaring where a package's docs live
|
|
6
|
+
* and what kind they are. Read by the discovery walker; the manifest
|
|
7
|
+
* is per-package and points at a single doc tree.
|
|
8
|
+
*/
|
|
9
|
+
export interface RayfinDocsManifest {
|
|
10
|
+
version: 1;
|
|
11
|
+
/** Path to the docs root, relative to package.json's directory. */
|
|
12
|
+
dir: string;
|
|
13
|
+
/** Stable, human-readable module identity. Conventionally the
|
|
14
|
+
* unscoped package name (e.g. `rayfin-core`, `rayfin-guide`,
|
|
15
|
+
* `rayfin-host`). */
|
|
16
|
+
module: string;
|
|
17
|
+
/** Doc kind. Maps to the legacy `DocModule` for back-compat:
|
|
18
|
+
* - `guide` → `module: guide`
|
|
19
|
+
* - `host` → `module: host`
|
|
20
|
+
* - `api-reference` → `module: ts-sdk` */
|
|
21
|
+
kind: DocKind;
|
|
22
|
+
}
|
|
23
|
+
export interface DocSection {
|
|
24
|
+
heading: string;
|
|
25
|
+
level: number;
|
|
26
|
+
content: string;
|
|
27
|
+
symbols: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface DocEntry {
|
|
30
|
+
id: string;
|
|
31
|
+
module: DocModule;
|
|
32
|
+
path: string;
|
|
33
|
+
title: string;
|
|
34
|
+
content: string;
|
|
35
|
+
symbols: string[];
|
|
36
|
+
sections: DocSection[];
|
|
37
|
+
/** When this entry came from a discovered package (Phase 2+), the
|
|
38
|
+
* identifying package metadata. `undefined` for entries loaded from
|
|
39
|
+
* the bundled `assetsRoot` (Phase 1 mode). */
|
|
40
|
+
source?: DocSource;
|
|
41
|
+
}
|
|
42
|
+
export interface DocSource {
|
|
43
|
+
/** Manifest module identity (e.g. `rayfin-core`). */
|
|
44
|
+
module: string;
|
|
45
|
+
/** Doc kind from the manifest. */
|
|
46
|
+
kind: DocKind;
|
|
47
|
+
/** Package name from the manifest's `package.json` (e.g.
|
|
48
|
+
* `@microsoft/rayfin-core`). The package's filesystem path is
|
|
49
|
+
* intentionally NOT exposed in this public type — it leaks the
|
|
50
|
+
* user's home directory into MCP/CLI output. Internal callers that
|
|
51
|
+
* need the absolute path use the `DiscoveredPackage.packageRoot`
|
|
52
|
+
* field on the discovery report instead. */
|
|
53
|
+
packageName: string;
|
|
54
|
+
/** Version from the source package's `package.json`. */
|
|
55
|
+
packageVersion: string;
|
|
56
|
+
}
|
|
57
|
+
export interface DocListItem {
|
|
58
|
+
id: string;
|
|
59
|
+
module: DocModule;
|
|
60
|
+
path: string;
|
|
61
|
+
title: string;
|
|
62
|
+
source?: DocSource;
|
|
63
|
+
}
|
|
64
|
+
export interface DocSearchResult {
|
|
65
|
+
id: string;
|
|
66
|
+
module: DocModule;
|
|
67
|
+
path: string;
|
|
68
|
+
title: string;
|
|
69
|
+
heading?: string;
|
|
70
|
+
symbol?: string;
|
|
71
|
+
kind?: 'doc' | 'symbol';
|
|
72
|
+
snippet: string;
|
|
73
|
+
snippetStart: number;
|
|
74
|
+
snippetEnd: number;
|
|
75
|
+
score: number;
|
|
76
|
+
symbols: string[];
|
|
77
|
+
source?: DocSource;
|
|
78
|
+
}
|
|
79
|
+
export interface DocSectionRef {
|
|
80
|
+
symbol: string;
|
|
81
|
+
entryId: string;
|
|
82
|
+
module: DocModule;
|
|
83
|
+
path: string;
|
|
84
|
+
title: string;
|
|
85
|
+
heading: string;
|
|
86
|
+
content: string;
|
|
87
|
+
source?: DocSource;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@microsoft/rayfin-docs",
|
|
3
|
+
"version": "1.27.0",
|
|
4
|
+
"description": "Rayfin docs indexing and search library — used by the MCP server, CLI, and other doc-facing surfaces",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./_internal/build-index": {
|
|
14
|
+
"types": "./dist/_internal/build-index.d.ts",
|
|
15
|
+
"import": "./dist/_internal/build-index.js"
|
|
16
|
+
},
|
|
17
|
+
"./_internal/site-discovery": {
|
|
18
|
+
"types": "./dist/discovery.d.ts",
|
|
19
|
+
"import": "./dist/discovery.js",
|
|
20
|
+
"require": "./dist/site-cjs/discovery.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/**/*.js",
|
|
26
|
+
"dist/**/*.d.ts",
|
|
27
|
+
"dist/**/*.json",
|
|
28
|
+
"!dist/cjs/**",
|
|
29
|
+
"!dist/**/__tests__/**",
|
|
30
|
+
"assets/catalog.json",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"minisearch": "~7.2.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^24.0.10",
|
|
38
|
+
"eslint": "^9.28.0",
|
|
39
|
+
"prettier": "^3.5.3",
|
|
40
|
+
"rimraf": "~6.0.1",
|
|
41
|
+
"typescript": "^5.8.3",
|
|
42
|
+
"vitest": "^3.2.3",
|
|
43
|
+
"@vitest/coverage-v8": "~3.2.4"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"registry": "https://npm.pkg.github.com",
|
|
47
|
+
"access": "restricted"
|
|
48
|
+
},
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/microsoft/project-rayfin.git",
|
|
52
|
+
"directory": "packages/tools/docs-lib"
|
|
53
|
+
},
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsc && tsc -p tsconfig.site-cjs.json && node scripts/write-site-cjs-package-json.mjs",
|
|
57
|
+
"build:watch": "tsc --watch",
|
|
58
|
+
"clean": "rimraf dist .tsbuildinfo .tsbuildinfo.site-cjs",
|
|
59
|
+
"test": "vitest --run",
|
|
60
|
+
"test:watch": "vitest"
|
|
61
|
+
}
|
|
62
|
+
}
|