@doccov/sdk 0.5.8 → 0.6.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 (3) hide show
  1. package/dist/index.d.ts +489 -485
  2. package/dist/index.js +2766 -1945
  3. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -70,254 +70,260 @@ declare function hasNonAssertionComments(code: string): boolean;
70
70
  */
71
71
  declare function detectExampleAssertionFailures(entry: SpecExport, runtimeResults: Map<number, ExampleRunResult>): SpecDocDrift[];
72
72
  /**
73
- * Markdown/MDX documentation analysis types
73
+ * Project detection types for I/O-agnostic project analysis.
74
+ * Used by both CLI (NodeFileSystem) and API (SandboxFileSystem).
74
75
  */
75
76
  /**
76
- * A code block extracted from a markdown file
77
+ * Minimal filesystem interface for I/O-agnostic detection.
78
+ * Implementations: NodeFileSystem (CLI), SandboxFileSystem (API)
77
79
  */
78
- interface MarkdownCodeBlock {
79
- /** Language tag (ts, typescript, js, javascript, tsx, jsx) */
80
- lang: string;
81
- /** The code content */
82
- code: string;
83
- /** Raw meta string from code fence (e.g., "title=example.ts") */
84
- meta?: string;
85
- /** Starting line number in the markdown file */
86
- lineStart: number;
87
- /** Ending line number in the markdown file */
88
- lineEnd: number;
80
+ interface FileSystem {
81
+ /** Check if a file or directory exists */
82
+ exists(path: string): Promise<boolean>;
83
+ /** Read file contents as string */
84
+ readFile(path: string): Promise<string>;
85
+ /** List directory contents (file/folder names only) */
86
+ readDir(path: string): Promise<string[]>;
87
+ /** Check if path is a directory */
88
+ isDirectory(path: string): Promise<boolean>;
89
89
  }
90
- /**
91
- * A parsed markdown documentation file
92
- */
93
- interface MarkdownDocFile {
94
- /** File path relative to project root */
90
+ /** Supported package managers */
91
+ type PackageManager = "npm" | "yarn" | "pnpm" | "bun";
92
+ /** Package manager detection result with install/run commands */
93
+ interface PackageManagerInfo {
94
+ /** Package manager name */
95
+ name: PackageManager;
96
+ /** Lockfile that was detected (null if none found) */
97
+ lockfile: string | null;
98
+ /** Arguments for install command, e.g. ['install', '--frozen-lockfile'] */
99
+ installArgs: string[];
100
+ /** Prefix for running scripts, e.g. ['npm', 'run'] or ['pnpm'] */
101
+ runPrefix: string[];
102
+ }
103
+ /** Monorepo type based on configuration */
104
+ type MonorepoType = "npm-workspaces" | "pnpm-workspaces" | "lerna" | "none";
105
+ /** Monorepo detection result */
106
+ interface MonorepoInfo {
107
+ /** Whether this is a monorepo */
108
+ isMonorepo: boolean;
109
+ /** Type of monorepo configuration */
110
+ type: MonorepoType;
111
+ /** Workspace patterns from config (e.g. ['packages/*']) */
112
+ patterns: string[];
113
+ /** Resolved workspace packages */
114
+ packages: WorkspacePackage[];
115
+ }
116
+ /** A package within a monorepo workspace */
117
+ interface WorkspacePackage {
118
+ /** Package name from package.json */
119
+ name: string;
120
+ /** Relative path to package directory */
95
121
  path: string;
96
- /** All executable code blocks found */
97
- codeBlocks: MarkdownCodeBlock[];
122
+ /** Whether the package is marked as private */
123
+ private: boolean;
124
+ }
125
+ /** Entry point source - where the entry was detected from */
126
+ type EntryPointSource = "types" | "exports" | "main" | "module" | "fallback";
127
+ /** Entry point detection result */
128
+ interface EntryPointInfo {
129
+ /** Path to entry file (relative to package root) */
130
+ path: string;
131
+ /** Where the entry point was detected from */
132
+ source: EntryPointSource;
133
+ /** Whether this is a .d.ts file (no source available) */
134
+ isDeclarationOnly: boolean;
135
+ }
136
+ /** Build configuration detection result */
137
+ interface BuildInfo {
138
+ /** Build-related script names found (e.g. ['build', 'build:types']) */
139
+ scripts: string[];
140
+ /** Whether any build script was found */
141
+ hasBuildScript: boolean;
142
+ /** Whether TypeScript is configured/installed */
143
+ hasTypeScript: boolean;
144
+ /** Indicators for exotic project types */
145
+ exoticIndicators: {
146
+ /** WASM project (Cargo.toml or wasm-pack scripts) */
147
+ wasm: boolean;
148
+ /** napi-rs native addon project */
149
+ napi: boolean;
150
+ };
151
+ }
152
+ /** Complete project analysis result */
153
+ interface ProjectInfo {
154
+ /** Package manager info */
155
+ packageManager: PackageManagerInfo;
156
+ /** Monorepo info */
157
+ monorepo: MonorepoInfo;
158
+ /** Entry point info */
159
+ entryPoint: EntryPointInfo;
160
+ /** Build info */
161
+ build: BuildInfo;
162
+ }
163
+ /** Options for analyzeProject() */
164
+ interface AnalyzeProjectOptions {
165
+ /** Target package name for monorepos */
166
+ targetPackage?: string;
98
167
  }
99
168
  /**
100
- * A reference to an found in markdown
169
+ * Detect build configuration and exotic project indicators.
170
+ *
171
+ * @param fs - FileSystem implementation
172
+ * @param packagePath - Path to package directory (default: ".")
173
+ * @returns Build info including scripts and exotic indicators
101
174
  */
102
- interface ExportReference {
103
- /** The name of the being referenced */
104
- exportName: string;
105
- /** File path where the reference was found */
106
- file: string;
107
- /** Line number in the file */
108
- line: number;
109
- /** Surrounding code/text for context */
110
- context: string;
111
- /** Whether this reference is inside a code block */
112
- inCodeBlock: boolean;
113
- /** The code block index if inside a code block */
114
- blockIndex?: number;
115
- }
175
+ declare function detectBuildInfo(fs: FileSystem, packagePath?: string): Promise<BuildInfo>;
116
176
  /**
117
- * Change type for an impacted reference
177
+ * Get the primary build script name to run.
178
+ * Prefers 'build' over 'compile' over 'tsc'.
118
179
  */
119
- type DocsChangeType = "signature-changed" | "removed" | "deprecated" | "method-removed" | "method-changed" | "method-deprecated";
180
+ declare function getPrimaryBuildScript(buildInfo: BuildInfo): string | null;
120
181
  /**
121
- * Member-level change type
182
+ * Detect the TypeScript entry point for a package.
183
+ *
184
+ * Priority order:
185
+ * 1. package.json -> types or typings field
186
+ * 2. package.json -> exports["."].types
187
+ * 3. package.json -> main field (resolve to .ts)
188
+ * 4. package.json -> module field (resolve to .ts)
189
+ * 5. Common fallback paths
190
+ *
191
+ * @param fs - FileSystem implementation
192
+ * @param packagePath - Path to package directory (default: ".")
193
+ * @returns Entry point info
194
+ * @throws Error if no entry point can be found
122
195
  */
123
- type MemberChangeType = "added" | "removed" | "signature-changed";
196
+ declare function detectEntryPoint(fs: FileSystem, packagePath?: string): Promise<EntryPointInfo>;
197
+ import { Sandbox } from "@vercel/sandbox";
124
198
  /**
125
- * An impacted reference in a documentation file
199
+ * Node.js filesystem implementation for CLI usage.
200
+ * Wraps Node.js fs module with a base path.
126
201
  */
127
- interface DocsImpactReference {
128
- /** The name that was changed */
129
- exportName: string;
130
- /** Line number in the file */
131
- line: number;
132
- /** Type of change affecting this reference */
133
- changeType: DocsChangeType;
134
- /** Suggested fix (AI-generated or deterministic) */
135
- suggestion?: string;
136
- /** Context around the reference */
137
- context?: string;
138
- /** Member/method name if this is a member-level change */
139
- memberName?: string;
140
- /** Type of member change (added, removed, signature-changed) */
141
- memberChangeType?: MemberChangeType;
142
- /** Suggested replacement for removed/changed members */
143
- replacementSuggestion?: string;
144
- /** True if this is just a class instantiation (new ClassName()) */
145
- isInstantiation?: boolean;
202
+ declare class NodeFileSystem implements FileSystem {
203
+ private basePath;
204
+ constructor(basePath: string);
205
+ private resolve;
206
+ exists(relativePath: string): Promise<boolean>;
207
+ readFile(relativePath: string): Promise<string>;
208
+ readDir(relativePath: string): Promise<string[]>;
209
+ isDirectory(relativePath: string): Promise<boolean>;
146
210
  }
147
211
  /**
148
- * Documentation file impact summary
212
+ * Vercel Sandbox filesystem implementation for API usage.
213
+ * Uses sandbox.runCommand() with shell commands.
149
214
  */
150
- interface DocsImpact {
151
- /** File path */
152
- file: string;
153
- /** All impacted references in this file */
154
- references: DocsImpactReference[];
215
+ declare class SandboxFileSystem implements FileSystem {
216
+ private sandbox;
217
+ constructor(sandbox: Sandbox);
218
+ exists(path: string): Promise<boolean>;
219
+ readFile(path: string): Promise<string>;
220
+ readDir(path: string): Promise<string[]>;
221
+ isDirectory(path: string): Promise<boolean>;
155
222
  }
156
223
  /**
157
- * Complete docs impact analysis result
224
+ * Detect if a project is a monorepo and list its packages.
225
+ *
226
+ * Detection triggers (in order):
227
+ * 1. package.json has workspaces field (npm/yarn)
228
+ * 2. pnpm-workspace.yaml exists
229
+ * 3. lerna.json exists
158
230
  */
159
- interface DocsImpactResult {
160
- /** Files with impacted references */
161
- impactedFiles: DocsImpact[];
162
- /** New exports (from this diff) that have no documentation */
163
- missingDocs: string[];
164
- /** ALL exports from the spec that have no documentation in the scanned files */
165
- allUndocumented: string[];
166
- /** Statistics */
167
- stats: {
168
- /** Total markdown files scanned */
169
- filesScanned: number;
170
- /** Total code blocks found */
171
- codeBlocksFound: number;
172
- /** Total references found */
173
- referencesFound: number;
174
- /** References impacted by changes */
175
- impactedReferences: number;
176
- /** Total exports in the spec */
177
- totalExports: number;
178
- /** Exports found documented in markdown */
179
- documentedExports: number;
180
- };
181
- }
231
+ declare function detectMonorepo(fs: FileSystem): Promise<MonorepoInfo>;
182
232
  /**
183
- * Check if a language tag represents executable code
233
+ * Find a package by name or path in a list of workspace packages.
184
234
  */
185
- declare function isExecutableLang(lang: string | null | undefined): boolean;
235
+ declare function findPackageByName(packages: WorkspacePackage[], nameOrPath: string): WorkspacePackage | undefined;
186
236
  /**
187
- * Parse a markdown file and extract code blocks
237
+ * Format package list for display in error messages.
188
238
  */
189
- declare function parseMarkdownFile(content: string, filePath: string): MarkdownDocFile;
239
+ declare function formatPackageList(packages: WorkspacePackage[], limit?: number): string;
190
240
  /**
191
- * Parse multiple markdown files
241
+ * Detect package manager based on lockfile presence and package.json hints.
242
+ *
243
+ * Resolution order:
244
+ * 1. packageManager field in package.json (e.g., "pnpm@9.0.0")
245
+ * 2. Most recently modified lockfile (when multiple exist)
246
+ * 3. Static priority order: pnpm > bun > yarn > npm
247
+ * 4. Default to npm
192
248
  */
193
- declare function parseMarkdownFiles(files: Array<{
194
- path: string;
195
- content: string;
196
- }>): MarkdownDocFile[];
249
+ declare function detectPackageManager(fs: FileSystem): Promise<PackageManagerInfo>;
197
250
  /**
198
- * Extract import statements from code
199
- * Finds named imports: import { X, Y } from 'pkg'
251
+ * Get install command for a package manager.
252
+ * Returns [command, ...args] array.
200
253
  */
201
- declare function extractImports(code: string): Array<{
202
- name: string;
203
- from: string;
204
- }>;
254
+ declare function getInstallCommand(pm: PackageManagerInfo): string[];
205
255
  /**
206
- * Extract function calls from code
207
- * Finds: functionName( or functionName<
256
+ * Get run command for a package manager script.
257
+ * Returns [command, ...args, scriptName] array.
208
258
  */
209
- declare function extractFunctionCalls(code: string): string[];
259
+ declare function getRunCommand(pm: PackageManagerInfo, script: string): string[];
210
260
  /**
211
- * Find all references to given names in markdown files
261
+ * Safely parse a JSON file, returning null on any error.
212
262
  */
213
- declare function findExportReferences(files: MarkdownDocFile[], exportNames: string[]): ExportReference[];
263
+ declare function safeParseJson<T = Record<string, unknown>>(fs: FileSystem, path: string): Promise<T | null>;
214
264
  /**
215
- * Check if a code block references any of the given names
265
+ * Standard package.json structure for detection purposes.
216
266
  */
217
- declare function blockReferencesExport(block: MarkdownCodeBlock, exportName: string): boolean;
218
- import { SpecDiff } from "@openpkg-ts/spec";
219
- type MemberChangeType2 = "added" | "removed" | "signature-changed";
220
- interface MemberChange {
221
- /** The class this member belongs to */
222
- className: string;
223
- /** The member name (e.g., "evaluateChainhook") */
224
- memberName: string;
225
- /** Kind of member */
226
- memberKind: "method" | "property" | "accessor" | "constructor";
227
- /** Type of change */
228
- changeType: MemberChangeType2;
229
- /** Old signature string (for signature changes) */
230
- oldSignature?: string;
231
- /** New signature string (for signature changes) */
232
- newSignature?: string;
233
- /** Suggested replacement (e.g., "Use replayChainhook instead") */
234
- suggestion?: string;
267
+ interface PackageJson {
268
+ name?: string;
269
+ version?: string;
270
+ private?: boolean;
271
+ main?: string;
272
+ module?: string;
273
+ types?: string;
274
+ typings?: string;
275
+ exports?: PackageExports;
276
+ workspaces?: string[] | {
277
+ packages: string[];
278
+ };
279
+ scripts?: Record<string, string>;
280
+ dependencies?: Record<string, string>;
281
+ devDependencies?: Record<string, string>;
235
282
  }
236
283
  /**
237
- * Analyze docs impact from a spec diff
238
- *
239
- * @param diff - The spec diff result
240
- * @param markdownFiles - Parsed markdown files
241
- * @param newExportNames - All names in the new spec (for missing docs detection)
242
- * @param memberChanges - Optional member-level changes for granular detection
243
- */
244
- declare function analyzeDocsImpact(diff: SpecDiff, markdownFiles: MarkdownDocFile[], newExportNames?: string[], memberChanges?: MemberChange[]): DocsImpactResult;
245
- /**
246
- * Find references to deprecated exports
247
- */
248
- declare function findDeprecatedReferences(markdownFiles: MarkdownDocFile[], deprecatedExports: string[]): ExportReference[];
249
- /**
250
- * Find references to removed exports
251
- */
252
- declare function findRemovedReferences(markdownFiles: MarkdownDocFile[], removedExports: string[]): ExportReference[];
253
- /**
254
- * Check if any docs reference a specific */
255
- declare function hasDocsForExport(markdownFiles: MarkdownDocFile[], exportName: string): boolean;
256
- /**
257
- * Get all exports that have documentation
258
- */
259
- declare function getDocumentedExports(markdownFiles: MarkdownDocFile[], exportNames: string[]): string[];
260
- /**
261
- * Get all exports that lack documentation
262
- */
263
- declare function getUndocumentedExports(markdownFiles: MarkdownDocFile[], exportNames: string[]): string[];
264
- import { CategorizedBreaking, OpenPkg as OpenPkg3, SpecDiff as SpecDiff2 } from "@openpkg-ts/spec";
265
- /**
266
- * Extended spec diff result with docs impact
284
+ * Package.json exports field structure.
267
285
  */
268
- interface SpecDiffWithDocs extends SpecDiff2 {
269
- /** Docs impact analysis (only present if markdown files provided) */
270
- docsImpact?: DocsImpactResult;
271
- /** Member-level changes for classes (methods added/removed/changed) */
272
- memberChanges?: MemberChange[];
273
- /** Breaking changes categorized by severity (high/medium/low) */
274
- categorizedBreaking?: CategorizedBreaking[];
275
- }
286
+ type PackageExports = string | {
287
+ "."?: string | {
288
+ types?: string;
289
+ import?: string;
290
+ require?: string;
291
+ default?: string;
292
+ };
293
+ [key: string]: unknown;
294
+ };
276
295
  /**
277
- * Options for diffSpecWithDocs
296
+ * Read and parse package.json from a directory.
278
297
  */
279
- interface DiffWithDocsOptions {
280
- /** Parsed markdown documentation files */
281
- markdownFiles?: MarkdownDocFile[];
282
- }
298
+ declare function readPackageJson(fs: FileSystem, dir: string): Promise<PackageJson | null>;
283
299
  /**
284
- * Compute spec diff with optional docs impact analysis
300
+ * Analyze a project's structure for scanning.
285
301
  *
286
- * @param oldSpec - Previous version of the spec
287
- * @param newSpec - Current version of the spec
288
- * @param options - Options including markdown files to analyze
289
- * @returns Extended diff result with docs impact
302
+ * This is the main entry point for project detection. It combines all
303
+ * detection functions into a single call that returns complete project info.
290
304
  *
291
- * @example
292
- * ```ts
293
- * import { diffSpecWithDocs, parseMarkdownFiles } from '@doccov/sdk';
305
+ * For monorepos, you must specify the target package via options.targetPackage.
306
+ * If not specified and a monorepo is detected, an error is thrown with the
307
+ * list of available packages.
294
308
  *
295
- * const markdownFiles = parseMarkdownFiles([
296
- * { path: 'docs/guide.md', content: '...' },
297
- * ]);
309
+ * @param fs - FileSystem implementation (NodeFileSystem or SandboxFileSystem)
310
+ * @param options - Options including targetPackage for monorepos
311
+ * @returns Complete project info
312
+ * @throws Error if monorepo detected without targetPackage specified
313
+ * @throws Error if targetPackage not found in monorepo
298
314
  *
299
- * const diff = diffSpecWithDocs(oldSpec, newSpec, { markdownFiles });
315
+ * @example
316
+ * ```typescript
317
+ * // Single package
318
+ * const fs = new NodeFileSystem('/path/to/package');
319
+ * const project = await analyzeProject(fs);
300
320
  *
301
- * if (diff.docsImpact?.impactedFiles.length) {
302
- * console.log('Docs need updating!');
303
- * }
321
+ * // Monorepo with target package
322
+ * const fs = new NodeFileSystem('/path/to/monorepo');
323
+ * const project = await analyzeProject(fs, { targetPackage: '@scope/core' });
304
324
  * ```
305
325
  */
306
- declare function diffSpecWithDocs(oldSpec: OpenPkg3, newSpec: OpenPkg3, options?: DiffWithDocsOptions): SpecDiffWithDocs;
307
- /**
308
- * Check if a diff has any docs impact
309
- */
310
- declare function hasDocsImpact(diff: SpecDiffWithDocs): boolean;
311
- /**
312
- * Get summary of docs impact for display
313
- */
314
- declare function getDocsImpactSummary(diff: SpecDiffWithDocs): {
315
- impactedFileCount: number;
316
- impactedReferenceCount: number;
317
- missingDocsCount: number;
318
- totalIssues: number;
319
- memberChangesCount: number;
320
- };
326
+ declare function analyzeProject2(fs: FileSystem, options?: AnalyzeProjectOptions): Promise<ProjectInfo>;
321
327
  interface DocCovOptions {
322
328
  includePrivate?: boolean;
323
329
  followImports?: boolean;
@@ -466,348 +472,346 @@ declare function categorizeDrifts(drifts: SpecDocDrift2[]): {
466
472
  fixable: SpecDocDrift2[];
467
473
  nonFixable: SpecDocDrift2[];
468
474
  };
469
- interface Diagnostic {
475
+ import { SpecExport as SpecExport4 } from "@openpkg-ts/spec";
476
+ import { SpecExport as SpecExport3 } from "@openpkg-ts/spec";
477
+ type LintSeverity = "error" | "warn" | "off";
478
+ interface LintViolation {
479
+ rule: string;
480
+ severity: "error" | "warn";
470
481
  message: string;
471
- severity: "error" | "warning" | "info";
472
- suggestion?: string;
473
- location?: {
474
- file: string;
475
- line?: number;
476
- column?: number;
477
- };
482
+ line?: number;
483
+ fixable: boolean;
478
484
  }
479
- interface AnalysisResult {
480
- spec: OpenPkgSpec;
481
- diagnostics: Diagnostic[];
482
- metadata: AnalysisMetadata;
485
+ interface LintRule {
486
+ name: string;
487
+ defaultSeverity: LintSeverity;
488
+ check(exp: SpecExport3, rawJSDoc?: string): LintViolation[];
489
+ fix?(exp: SpecExport3, rawJSDoc?: string): JSDocPatch | null;
483
490
  }
484
- interface AnalysisMetadata {
485
- baseDir: string;
486
- configPath?: string;
487
- packageJsonPath?: string;
488
- hasNodeModules: boolean;
489
- resolveExternalTypes: boolean;
491
+ interface LintConfig {
492
+ rules: Record<string, LintSeverity>;
490
493
  }
491
- interface AnalyzeOptions {
492
- filters?: FilterOptions;
494
+ interface LintResult {
495
+ violations: LintViolation[];
496
+ errorCount: number;
497
+ warningCount: number;
498
+ fixableCount: number;
493
499
  }
494
- declare class DocCov {
495
- private readonly options;
496
- constructor(options?: DocCovOptions);
497
- analyze(code: string, fileName?: string, analyzeOptions?: AnalyzeOptions): Promise<OpenPkgSpec>;
498
- analyzeFile(filePath: string, analyzeOptions?: AnalyzeOptions): Promise<OpenPkgSpec>;
499
- analyzeProject(entryPath: string, analyzeOptions?: AnalyzeOptions): Promise<OpenPkgSpec>;
500
- analyzeWithDiagnostics(code: string, fileName?: string, analyzeOptions?: AnalyzeOptions): Promise<AnalysisResult>;
501
- analyzeFileWithDiagnostics(filePath: string, analyzeOptions?: AnalyzeOptions): Promise<AnalysisResult>;
502
- private normalizeDiagnostic;
503
- private mapSeverity;
504
- private normalizeMetadata;
505
- private applySpecFilters;
500
+ /** All available lint rules */
501
+ declare const allRules: LintRule[];
502
+ /** Default configuration with rule defaults */
503
+ declare function getDefaultConfig(): LintConfig;
504
+ /** Get a rule by name */
505
+ declare function getRule(name: string): LintRule | undefined;
506
+ /** Lint a single */
507
+ declare function lintExport(exp: SpecExport4, rawJSDoc?: string, config?: LintConfig): LintViolation[];
508
+ /** Lint multiple exports and aggregate results */
509
+ declare function lintExports(exports: Array<{
510
+ export: SpecExport4;
511
+ rawJSDoc?: string;
512
+ }>, config?: LintConfig): LintResult;
513
+ /** Merge user config with defaults */
514
+ declare function mergeConfig(userConfig: Partial<LintConfig>): LintConfig;
515
+ declare const consistentParamStyle: LintRule;
516
+ declare const noEmptyReturns: LintRule;
517
+ declare const requireDescription: LintRule;
518
+ declare const requireExample: LintRule;
519
+ import { SpecDiff } from "@openpkg-ts/spec";
520
+ type MemberChangeType = "added" | "removed" | "signature-changed";
521
+ interface MemberChange {
522
+ /** The class this member belongs to */
523
+ className: string;
524
+ /** The member name (e.g., "evaluateChainhook") */
525
+ memberName: string;
526
+ /** Kind of member */
527
+ memberKind: "method" | "property" | "accessor" | "constructor";
528
+ /** Type of change */
529
+ changeType: MemberChangeType;
530
+ /** Old signature string (for signature changes) */
531
+ oldSignature?: string;
532
+ /** New signature string (for signature changes) */
533
+ newSignature?: string;
534
+ /** Suggested replacement (e.g., "Use replayChainhook instead") */
535
+ suggestion?: string;
506
536
  }
507
- declare function analyze(code: string, options?: AnalyzeOptions): Promise<OpenPkgSpec>;
508
- declare function analyzeFile(filePath: string, options?: AnalyzeOptions): Promise<OpenPkgSpec>;
509
- /** @deprecated Use DocCov instead */
510
- declare const OpenPkg4: typeof DocCov;
511
537
  /**
512
- * Project detection types for I/O-agnostic project analysis.
513
- * Used by both CLI (NodeFileSystem) and API (SandboxFileSystem).
538
+ * Markdown/MDX documentation analysis types
514
539
  */
515
540
  /**
516
- * Minimal filesystem interface for I/O-agnostic detection.
517
- * Implementations: NodeFileSystem (CLI), SandboxFileSystem (API)
541
+ * A code block extracted from a markdown file
518
542
  */
519
- interface FileSystem {
520
- /** Check if a file or directory exists */
521
- exists(path: string): Promise<boolean>;
522
- /** Read file contents as string */
523
- readFile(path: string): Promise<string>;
524
- /** List directory contents (file/folder names only) */
525
- readDir(path: string): Promise<string[]>;
526
- /** Check if path is a directory */
527
- isDirectory(path: string): Promise<boolean>;
528
- }
529
- /** Supported package managers */
530
- type PackageManager = "npm" | "yarn" | "pnpm" | "bun";
531
- /** Package manager detection result with install/run commands */
532
- interface PackageManagerInfo {
533
- /** Package manager name */
534
- name: PackageManager;
535
- /** Lockfile that was detected (null if none found) */
536
- lockfile: string | null;
537
- /** Arguments for install command, e.g. ['install', '--frozen-lockfile'] */
538
- installArgs: string[];
539
- /** Prefix for running scripts, e.g. ['npm', 'run'] or ['pnpm'] */
540
- runPrefix: string[];
541
- }
542
- /** Monorepo type based on configuration */
543
- type MonorepoType = "npm-workspaces" | "pnpm-workspaces" | "lerna" | "none";
544
- /** Monorepo detection result */
545
- interface MonorepoInfo {
546
- /** Whether this is a monorepo */
547
- isMonorepo: boolean;
548
- /** Type of monorepo configuration */
549
- type: MonorepoType;
550
- /** Workspace patterns from config (e.g. ['packages/*']) */
551
- patterns: string[];
552
- /** Resolved workspace packages */
553
- packages: WorkspacePackage[];
554
- }
555
- /** A package within a monorepo workspace */
556
- interface WorkspacePackage {
557
- /** Package name from package.json */
558
- name: string;
559
- /** Relative path to package directory */
560
- path: string;
561
- /** Whether the package is marked as private */
562
- private: boolean;
543
+ interface MarkdownCodeBlock {
544
+ /** Language tag (ts, typescript, js, javascript, tsx, jsx) */
545
+ lang: string;
546
+ /** The code content */
547
+ code: string;
548
+ /** Raw meta string from code fence (e.g., "title=example.ts") */
549
+ meta?: string;
550
+ /** Starting line number in the markdown file */
551
+ lineStart: number;
552
+ /** Ending line number in the markdown file */
553
+ lineEnd: number;
563
554
  }
564
- /** Entry point source - where the entry was detected from */
565
- type EntryPointSource = "types" | "exports" | "main" | "module" | "fallback";
566
- /** Entry point detection result */
567
- interface EntryPointInfo {
568
- /** Path to entry file (relative to package root) */
555
+ /**
556
+ * A parsed markdown documentation file
557
+ */
558
+ interface MarkdownDocFile {
559
+ /** File path relative to project root */
569
560
  path: string;
570
- /** Where the entry point was detected from */
571
- source: EntryPointSource;
572
- /** Whether this is a .d.ts file (no source available) */
573
- isDeclarationOnly: boolean;
574
- }
575
- /** Build configuration detection result */
576
- interface BuildInfo {
577
- /** Build-related script names found (e.g. ['build', 'build:types']) */
578
- scripts: string[];
579
- /** Whether any build script was found */
580
- hasBuildScript: boolean;
581
- /** Whether TypeScript is configured/installed */
582
- hasTypeScript: boolean;
583
- /** Indicators for exotic project types */
584
- exoticIndicators: {
585
- /** WASM project (Cargo.toml or wasm-pack scripts) */
586
- wasm: boolean;
587
- /** napi-rs native addon project */
588
- napi: boolean;
589
- };
561
+ /** All executable code blocks found */
562
+ codeBlocks: MarkdownCodeBlock[];
590
563
  }
591
- /** Complete project analysis result */
592
- interface ProjectInfo {
593
- /** Package manager info */
594
- packageManager: PackageManagerInfo;
595
- /** Monorepo info */
596
- monorepo: MonorepoInfo;
597
- /** Entry point info */
598
- entryPoint: EntryPointInfo;
599
- /** Build info */
600
- build: BuildInfo;
564
+ /**
565
+ * A reference to an found in markdown
566
+ */
567
+ interface ExportReference {
568
+ /** The name of the being referenced */
569
+ exportName: string;
570
+ /** File path where the reference was found */
571
+ file: string;
572
+ /** Line number in the file */
573
+ line: number;
574
+ /** Surrounding code/text for context */
575
+ context: string;
576
+ /** Whether this reference is inside a code block */
577
+ inCodeBlock: boolean;
578
+ /** The code block index if inside a code block */
579
+ blockIndex?: number;
601
580
  }
602
- /** Options for analyzeProject() */
603
- interface AnalyzeProjectOptions {
604
- /** Target package name for monorepos */
605
- targetPackage?: string;
581
+ /**
582
+ * Change type for an impacted reference
583
+ */
584
+ type DocsChangeType = "signature-changed" | "removed" | "deprecated" | "method-removed" | "method-changed" | "method-deprecated";
585
+ /**
586
+ * Member-level change type
587
+ */
588
+ type MemberChangeType2 = "added" | "removed" | "signature-changed";
589
+ /**
590
+ * An impacted reference in a documentation file
591
+ */
592
+ interface DocsImpactReference {
593
+ /** The name that was changed */
594
+ exportName: string;
595
+ /** Line number in the file */
596
+ line: number;
597
+ /** Type of change affecting this reference */
598
+ changeType: DocsChangeType;
599
+ /** Suggested fix (AI-generated or deterministic) */
600
+ suggestion?: string;
601
+ /** Context around the reference */
602
+ context?: string;
603
+ /** Member/method name if this is a member-level change */
604
+ memberName?: string;
605
+ /** Type of member change (added, removed, signature-changed) */
606
+ memberChangeType?: MemberChangeType2;
607
+ /** Suggested replacement for removed/changed members */
608
+ replacementSuggestion?: string;
609
+ /** True if this is just a class instantiation (new ClassName()) */
610
+ isInstantiation?: boolean;
606
611
  }
607
- import { Sandbox } from "@vercel/sandbox";
608
612
  /**
609
- * Node.js filesystem implementation for CLI usage.
610
- * Wraps Node.js fs module with a base path.
613
+ * Documentation file impact summary
611
614
  */
612
- declare class NodeFileSystem implements FileSystem {
613
- private basePath;
614
- constructor(basePath: string);
615
- private resolve;
616
- exists(relativePath: string): Promise<boolean>;
617
- readFile(relativePath: string): Promise<string>;
618
- readDir(relativePath: string): Promise<string[]>;
619
- isDirectory(relativePath: string): Promise<boolean>;
615
+ interface DocsImpact {
616
+ /** File path */
617
+ file: string;
618
+ /** All impacted references in this file */
619
+ references: DocsImpactReference[];
620
620
  }
621
621
  /**
622
- * Vercel Sandbox filesystem implementation for API usage.
623
- * Uses sandbox.runCommand() with shell commands.
622
+ * Complete docs impact analysis result
624
623
  */
625
- declare class SandboxFileSystem implements FileSystem {
626
- private sandbox;
627
- constructor(sandbox: Sandbox);
628
- exists(path: string): Promise<boolean>;
629
- readFile(path: string): Promise<string>;
630
- readDir(path: string): Promise<string[]>;
631
- isDirectory(path: string): Promise<boolean>;
624
+ interface DocsImpactResult {
625
+ /** Files with impacted references */
626
+ impactedFiles: DocsImpact[];
627
+ /** New exports (from this diff) that have no documentation */
628
+ missingDocs: string[];
629
+ /** ALL exports from the spec that have no documentation in the scanned files */
630
+ allUndocumented: string[];
631
+ /** Statistics */
632
+ stats: {
633
+ /** Total markdown files scanned */
634
+ filesScanned: number;
635
+ /** Total code blocks found */
636
+ codeBlocksFound: number;
637
+ /** Total references found */
638
+ referencesFound: number;
639
+ /** References impacted by changes */
640
+ impactedReferences: number;
641
+ /** Total exports in the spec */
642
+ totalExports: number;
643
+ /** Exports found documented in markdown */
644
+ documentedExports: number;
645
+ };
632
646
  }
633
647
  /**
634
- * Detect package manager based on lockfile presence.
648
+ * Analyze docs impact from a spec diff
635
649
  *
636
- * Priority order:
637
- * 1. pnpm-lock.yaml
638
- * 2. bun.lock / bun.lockb
639
- * 3. yarn.lock
640
- * 4. package-lock.json
641
- * 5. Default to npm
650
+ * @param diff - The spec diff result
651
+ * @param markdownFiles - Parsed markdown files
652
+ * @param newExportNames - All names in the new spec (for missing docs detection)
653
+ * @param memberChanges - Optional member-level changes for granular detection
642
654
  */
643
- declare function detectPackageManager(fs: FileSystem): Promise<PackageManagerInfo>;
655
+ declare function analyzeDocsImpact(diff: SpecDiff, markdownFiles: MarkdownDocFile[], newExportNames?: string[], memberChanges?: MemberChange[]): DocsImpactResult;
644
656
  /**
645
- * Get install command for a package manager.
646
- * Returns [command, ...args] array.
657
+ * Find references to deprecated exports
647
658
  */
648
- declare function getInstallCommand(pm: PackageManagerInfo): string[];
659
+ declare function findDeprecatedReferences(markdownFiles: MarkdownDocFile[], deprecatedExports: string[]): ExportReference[];
649
660
  /**
650
- * Get run command for a package manager script.
651
- * Returns [command, ...args, scriptName] array.
661
+ * Find references to removed exports
652
662
  */
653
- declare function getRunCommand(pm: PackageManagerInfo, script: string): string[];
663
+ declare function findRemovedReferences(markdownFiles: MarkdownDocFile[], removedExports: string[]): ExportReference[];
654
664
  /**
655
- * Detect if a project is a monorepo and list its packages.
656
- *
657
- * Detection triggers (in order):
658
- * 1. package.json has workspaces field (npm/yarn)
659
- * 2. pnpm-workspace.yaml exists
660
- * 3. lerna.json exists
665
+ * Check if any docs reference a specific */
666
+ declare function hasDocsForExport(markdownFiles: MarkdownDocFile[], exportName: string): boolean;
667
+ /**
668
+ * Get all exports that have documentation
661
669
  */
662
- declare function detectMonorepo(fs: FileSystem): Promise<MonorepoInfo>;
670
+ declare function getDocumentedExports(markdownFiles: MarkdownDocFile[], exportNames: string[]): string[];
663
671
  /**
664
- * Find a package by name or path in a list of workspace packages.
672
+ * Get all exports that lack documentation
665
673
  */
666
- declare function findPackageByName(packages: WorkspacePackage[], nameOrPath: string): WorkspacePackage | undefined;
674
+ declare function getUndocumentedExports(markdownFiles: MarkdownDocFile[], exportNames: string[]): string[];
675
+ import { CategorizedBreaking, OpenPkg as OpenPkg3, SpecDiff as SpecDiff2 } from "@openpkg-ts/spec";
667
676
  /**
668
- * Format package list for display in error messages.
677
+ * Extended spec diff result with docs impact
669
678
  */
670
- declare function formatPackageList(packages: WorkspacePackage[], limit?: number): string;
679
+ interface SpecDiffWithDocs extends SpecDiff2 {
680
+ /** Docs impact analysis (only present if markdown files provided) */
681
+ docsImpact?: DocsImpactResult;
682
+ /** Member-level changes for classes (methods added/removed/changed) */
683
+ memberChanges?: MemberChange[];
684
+ /** Breaking changes categorized by severity (high/medium/low) */
685
+ categorizedBreaking?: CategorizedBreaking[];
686
+ }
671
687
  /**
672
- * Detect the TypeScript entry point for a package.
688
+ * Options for diffSpecWithDocs
689
+ */
690
+ interface DiffWithDocsOptions {
691
+ /** Parsed markdown documentation files */
692
+ markdownFiles?: MarkdownDocFile[];
693
+ }
694
+ /**
695
+ * Compute spec diff with optional docs impact analysis
673
696
  *
674
- * Priority order:
675
- * 1. package.json -> types or typings field
676
- * 2. package.json -> exports["."].types
677
- * 3. package.json -> main field (resolve to .ts)
678
- * 4. package.json -> module field (resolve to .ts)
679
- * 5. Common fallback paths
697
+ * @param oldSpec - Previous version of the spec
698
+ * @param newSpec - Current version of the spec
699
+ * @param options - Options including markdown files to analyze
700
+ * @returns Extended diff result with docs impact
680
701
  *
681
- * @param fs - FileSystem implementation
682
- * @param packagePath - Path to package directory (default: ".")
683
- * @returns Entry point info
684
- * @throws Error if no entry point can be found
702
+ * @example
703
+ * ```ts
704
+ * import { diffSpecWithDocs, parseMarkdownFiles } from '@doccov/sdk';
705
+ *
706
+ * const markdownFiles = parseMarkdownFiles([
707
+ * { path: 'docs/guide.md', content: '...' },
708
+ * ]);
709
+ *
710
+ * const diff = diffSpecWithDocs(oldSpec, newSpec, { markdownFiles });
711
+ *
712
+ * if (diff.docsImpact?.impactedFiles.length) {
713
+ * console.log('Docs need updating!');
714
+ * }
715
+ * ```
685
716
  */
686
- declare function detectEntryPoint(fs: FileSystem, packagePath?: string): Promise<EntryPointInfo>;
717
+ declare function diffSpecWithDocs(oldSpec: OpenPkg3, newSpec: OpenPkg3, options?: DiffWithDocsOptions): SpecDiffWithDocs;
687
718
  /**
688
- * Detect build configuration and exotic project indicators.
689
- *
690
- * @param fs - FileSystem implementation
691
- * @param packagePath - Path to package directory (default: ".")
692
- * @returns Build info including scripts and exotic indicators
719
+ * Check if a diff has any docs impact
693
720
  */
694
- declare function detectBuildInfo(fs: FileSystem, packagePath?: string): Promise<BuildInfo>;
721
+ declare function hasDocsImpact(diff: SpecDiffWithDocs): boolean;
695
722
  /**
696
- * Get the primary build script name to run.
697
- * Prefers 'build' over 'compile' over 'tsc'.
723
+ * Get summary of docs impact for display
698
724
  */
699
- declare function getPrimaryBuildScript(buildInfo: BuildInfo): string | null;
725
+ declare function getDocsImpactSummary(diff: SpecDiffWithDocs): {
726
+ impactedFileCount: number;
727
+ impactedReferenceCount: number;
728
+ missingDocsCount: number;
729
+ totalIssues: number;
730
+ memberChangesCount: number;
731
+ };
700
732
  /**
701
- * Safely parse a JSON file, returning null on any error.
733
+ * Extract import statements from code (legacy interface).
734
+ * @deprecated Use extractImportsAST for more detailed info.
702
735
  */
703
- declare function safeParseJson<T = Record<string, unknown>>(fs: FileSystem, path: string): Promise<T | null>;
736
+ declare function extractImports(code: string): Array<{
737
+ name: string;
738
+ from: string;
739
+ }>;
704
740
  /**
705
- * Standard package.json structure for detection purposes.
741
+ * Extract function calls from code (legacy interface).
742
+ * @deprecated Use extractCallsAST for more detailed info.
706
743
  */
707
- interface PackageJson {
708
- name?: string;
709
- version?: string;
710
- private?: boolean;
711
- main?: string;
712
- module?: string;
713
- types?: string;
714
- typings?: string;
715
- exports?: PackageExports;
716
- workspaces?: string[] | {
717
- packages: string[];
718
- };
719
- scripts?: Record<string, string>;
720
- dependencies?: Record<string, string>;
721
- devDependencies?: Record<string, string>;
744
+ declare function extractFunctionCalls(code: string): string[];
745
+ /** Options for parsing markdown files */
746
+ interface ParseOptions {
747
+ /** Custom set of executable language tags */
748
+ executableLangs?: string[];
722
749
  }
723
750
  /**
724
- * Package.json exports field structure.
751
+ * Check if a language tag represents executable code
725
752
  */
726
- type PackageExports = string | {
727
- "."?: string | {
728
- types?: string;
729
- import?: string;
730
- require?: string;
731
- default?: string;
732
- };
733
- [key: string]: unknown;
734
- };
753
+ declare function isExecutableLang(lang: string | null | undefined, customLangs?: Set<string>): boolean;
735
754
  /**
736
- * Read and parse package.json from a directory.
755
+ * Parse a markdown file and extract code blocks
737
756
  */
738
- declare function readPackageJson(fs: FileSystem, dir: string): Promise<PackageJson | null>;
757
+ declare function parseMarkdownFile(content: string, filePath: string, options?: ParseOptions): MarkdownDocFile;
739
758
  /**
740
- * Analyze a project's structure for scanning.
741
- *
742
- * This is the main entry point for project detection. It combines all
743
- * detection functions into a single call that returns complete project info.
744
- *
745
- * For monorepos, you must specify the target package via options.targetPackage.
746
- * If not specified and a monorepo is detected, an error is thrown with the
747
- * list of available packages.
748
- *
749
- * @param fs - FileSystem implementation (NodeFileSystem or SandboxFileSystem)
750
- * @param options - Options including targetPackage for monorepos
751
- * @returns Complete project info
752
- * @throws Error if monorepo detected without targetPackage specified
753
- * @throws Error if targetPackage not found in monorepo
754
- *
755
- * @example
756
- * ```typescript
757
- * // Single package
758
- * const fs = new NodeFileSystem('/path/to/package');
759
- * const project = await analyzeProject(fs);
760
- *
761
- * // Monorepo with target package
762
- * const fs = new NodeFileSystem('/path/to/monorepo');
763
- * const project = await analyzeProject(fs, { targetPackage: '@scope/core' });
764
- * ```
759
+ * Parse multiple markdown files
765
760
  */
766
- declare function analyzeProject2(fs: FileSystem, options?: AnalyzeProjectOptions): Promise<ProjectInfo>;
767
- import { SpecExport as SpecExport4 } from "@openpkg-ts/spec";
768
- import { SpecExport as SpecExport3 } from "@openpkg-ts/spec";
769
- type LintSeverity = "error" | "warn" | "off";
770
- interface LintViolation {
771
- rule: string;
772
- severity: "error" | "warn";
761
+ declare function parseMarkdownFiles(files: Array<{
762
+ path: string;
763
+ content: string;
764
+ }>, options?: ParseOptions): MarkdownDocFile[];
765
+ /**
766
+ * Find all references to given names in markdown files
767
+ */
768
+ declare function findExportReferences(files: MarkdownDocFile[], exportNames: string[]): ExportReference[];
769
+ /**
770
+ * Check if a code block references any of the given names
771
+ */
772
+ declare function blockReferencesExport(block: MarkdownCodeBlock, exportName: string): boolean;
773
+ interface Diagnostic {
773
774
  message: string;
774
- line?: number;
775
- fixable: boolean;
775
+ severity: "error" | "warning" | "info";
776
+ suggestion?: string;
777
+ location?: {
778
+ file: string;
779
+ line?: number;
780
+ column?: number;
781
+ };
776
782
  }
777
- interface LintRule {
778
- name: string;
779
- defaultSeverity: LintSeverity;
780
- check(exp: SpecExport3, rawJSDoc?: string): LintViolation[];
781
- fix?(exp: SpecExport3, rawJSDoc?: string): JSDocPatch | null;
783
+ interface AnalysisResult {
784
+ spec: OpenPkgSpec;
785
+ diagnostics: Diagnostic[];
786
+ metadata: AnalysisMetadata;
782
787
  }
783
- interface LintConfig {
784
- rules: Record<string, LintSeverity>;
788
+ interface AnalysisMetadata {
789
+ baseDir: string;
790
+ configPath?: string;
791
+ packageJsonPath?: string;
792
+ hasNodeModules: boolean;
793
+ resolveExternalTypes: boolean;
785
794
  }
786
- interface LintResult {
787
- violations: LintViolation[];
788
- errorCount: number;
789
- warningCount: number;
790
- fixableCount: number;
795
+ interface AnalyzeOptions {
796
+ filters?: FilterOptions;
791
797
  }
792
- /** All available lint rules */
793
- declare const allRules: LintRule[];
794
- /** Default configuration with rule defaults */
795
- declare function getDefaultConfig(): LintConfig;
796
- /** Get a rule by name */
797
- declare function getRule(name: string): LintRule | undefined;
798
- /** Lint a single */
799
- declare function lintExport(exp: SpecExport4, rawJSDoc?: string, config?: LintConfig): LintViolation[];
800
- /** Lint multiple exports and aggregate results */
801
- declare function lintExports(exports: Array<{
802
- export: SpecExport4;
803
- rawJSDoc?: string;
804
- }>, config?: LintConfig): LintResult;
805
- /** Merge user config with defaults */
806
- declare function mergeConfig(userConfig: Partial<LintConfig>): LintConfig;
807
- declare const consistentParamStyle: LintRule;
808
- declare const noEmptyReturns: LintRule;
809
- declare const requireDescription: LintRule;
810
- declare const requireExample: LintRule;
798
+ declare class DocCov {
799
+ private readonly options;
800
+ constructor(options?: DocCovOptions);
801
+ analyze(code: string, fileName?: string, analyzeOptions?: AnalyzeOptions): Promise<OpenPkgSpec>;
802
+ analyzeFile(filePath: string, analyzeOptions?: AnalyzeOptions): Promise<OpenPkgSpec>;
803
+ analyzeProject(entryPath: string, analyzeOptions?: AnalyzeOptions): Promise<OpenPkgSpec>;
804
+ analyzeWithDiagnostics(code: string, fileName?: string, analyzeOptions?: AnalyzeOptions): Promise<AnalysisResult>;
805
+ analyzeFileWithDiagnostics(filePath: string, analyzeOptions?: AnalyzeOptions): Promise<AnalysisResult>;
806
+ private normalizeDiagnostic;
807
+ private mapSeverity;
808
+ private normalizeMetadata;
809
+ private applySpecFilters;
810
+ }
811
+ declare function analyze(code: string, options?: AnalyzeOptions): Promise<OpenPkgSpec>;
812
+ declare function analyzeFile(filePath: string, options?: AnalyzeOptions): Promise<OpenPkgSpec>;
813
+ /** @deprecated Use DocCov instead */
814
+ declare const OpenPkg4: typeof DocCov;
811
815
  interface ExampleTypeError {
812
816
  /** Index of the example in the examples array */
813
817
  exampleIndex: number;