@doccov/sdk 0.6.0 → 0.7.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/dist/index.js CHANGED
@@ -1,3 +1,22 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = createRequire(import.meta.url);
19
+
1
20
  // src/analysis/docs-coverage.ts
2
21
  import ts from "typescript";
3
22
 
@@ -2042,7 +2061,7 @@ function createAnalysisContext({
2042
2061
  // src/analysis/spec-builder.ts
2043
2062
  import * as fs2 from "node:fs";
2044
2063
  import * as path3 from "node:path";
2045
- import { SCHEMA_URL } from "@openpkg-ts/spec";
2064
+ import { SCHEMA_URL, SCHEMA_VERSION } from "@openpkg-ts/spec";
2046
2065
 
2047
2066
  // src/utils/parameter-utils.ts
2048
2067
  var BUILTIN_TYPE_SCHEMAS = {
@@ -3141,14 +3160,23 @@ function processTagContent(result, tag, content) {
3141
3160
  result.rawParamNames.push(parsed.name);
3142
3161
  result.params.push(parsed);
3143
3162
  }
3144
- result.tags.push({ name: "param", text: trimmedContent });
3163
+ result.tags.push({
3164
+ name: "param",
3165
+ text: trimmedContent,
3166
+ paramName: parsed?.name,
3167
+ typeAnnotation: parsed?.type
3168
+ });
3145
3169
  break;
3146
3170
  }
3147
3171
  case "returns":
3148
3172
  case "return": {
3149
3173
  const parsed = parseReturnContent(trimmedContent);
3150
3174
  result.returns = parsed;
3151
- result.tags.push({ name: "returns", text: trimmedContent });
3175
+ result.tags.push({
3176
+ name: "returns",
3177
+ text: trimmedContent,
3178
+ typeAnnotation: parsed.type
3179
+ });
3152
3180
  break;
3153
3181
  }
3154
3182
  case "example": {
@@ -3156,24 +3184,68 @@ function processTagContent(result, tag, content) {
3156
3184
  if (example) {
3157
3185
  result.examples.push(example);
3158
3186
  }
3187
+ const langMatch = trimmedContent.match(/^```(\w+)/);
3188
+ result.tags.push({
3189
+ name: "example",
3190
+ text: example,
3191
+ language: langMatch?.[1]
3192
+ });
3159
3193
  break;
3160
3194
  }
3161
3195
  case "see": {
3162
3196
  const linkTargets = extractAllLinkTargets(trimmedContent);
3163
3197
  for (const target of linkTargets) {
3164
- result.tags.push({ name: "link", text: target });
3165
- result.tags.push({ name: "see", text: target });
3198
+ result.tags.push({ name: "link", text: target, reference: target });
3199
+ result.tags.push({ name: "see", text: target, reference: target });
3166
3200
  }
3167
3201
  if (linkTargets.length === 0) {
3168
- result.tags.push({ name: "see", text: trimmedContent });
3202
+ result.tags.push({ name: "see", text: trimmedContent, reference: trimmedContent });
3169
3203
  }
3170
3204
  break;
3171
3205
  }
3172
3206
  case "link": {
3173
3207
  const target = extractLinkTarget(trimmedContent);
3174
3208
  if (target) {
3175
- result.tags.push({ name: "link", text: target });
3209
+ result.tags.push({ name: "link", text: target, reference: target });
3210
+ }
3211
+ break;
3212
+ }
3213
+ case "since": {
3214
+ result.tags.push({ name: "since", text: trimmedContent, version: trimmedContent });
3215
+ break;
3216
+ }
3217
+ case "deprecated": {
3218
+ const versionMatch = trimmedContent.match(/^(\d+\.\d+(?:\.\d+)?)\s*(.*)?$/);
3219
+ if (versionMatch) {
3220
+ result.tags.push({
3221
+ name: "deprecated",
3222
+ text: trimmedContent,
3223
+ version: versionMatch[1],
3224
+ reason: versionMatch[2]?.trim() || undefined
3225
+ });
3226
+ } else {
3227
+ result.tags.push({
3228
+ name: "deprecated",
3229
+ text: trimmedContent,
3230
+ reason: trimmedContent || undefined
3231
+ });
3232
+ }
3233
+ break;
3234
+ }
3235
+ case "type":
3236
+ case "typedef": {
3237
+ let typeAnnotation;
3238
+ if (trimmedContent.startsWith("{")) {
3239
+ const typeEnd = findMatchingBrace(trimmedContent, 0);
3240
+ if (typeEnd > 0) {
3241
+ typeAnnotation = trimmedContent.slice(1, typeEnd).trim();
3242
+ }
3176
3243
  }
3244
+ result.tags.push({
3245
+ name: tag,
3246
+ text: trimmedContent,
3247
+ typeAnnotation
3248
+ });
3177
3249
  break;
3178
3250
  }
3179
3251
  case "inheritdoc": {
@@ -3515,10 +3587,12 @@ function serializeClassMembers(declaration, checker, typeRefs, referencedTypes)
3515
3587
  const memberName = member.name?.getText() ?? "method";
3516
3588
  const memberSymbol = member.name ? checker.getSymbolAtLocation(member.name) : undefined;
3517
3589
  const methodDoc = memberSymbol ? parseJSDocComment(memberSymbol, checker) : null;
3518
- const signature = checker.getSignatureFromDeclaration(member);
3519
- const signatures = signature ? [
3520
- serializeSignature(signature, checker, typeRefs, referencedTypes, methodDoc, memberSymbol)
3521
- ] : undefined;
3590
+ const methodType = memberSymbol ? checker.getTypeOfSymbolAtLocation(memberSymbol, member) : checker.getTypeAtLocation(member);
3591
+ const callSignatures = methodType.getCallSignatures();
3592
+ const signatures = callSignatures.length > 0 ? callSignatures.map((sig, index) => ({
3593
+ ...serializeSignature(sig, checker, typeRefs, referencedTypes, methodDoc, memberSymbol),
3594
+ overloadIndex: callSignatures.length > 1 ? index : undefined
3595
+ })) : undefined;
3522
3596
  members.push({
3523
3597
  id: memberName,
3524
3598
  name: memberName,
@@ -3534,8 +3608,12 @@ function serializeClassMembers(declaration, checker, typeRefs, referencedTypes)
3534
3608
  if (ts2.isConstructorDeclaration(member)) {
3535
3609
  const ctorSymbol = checker.getSymbolAtLocation(member);
3536
3610
  const ctorDoc = ctorSymbol ? parseJSDocComment(ctorSymbol, checker) : null;
3537
- const signature = checker.getSignatureFromDeclaration(member);
3538
- const signatures = signature ? [serializeSignature(signature, checker, typeRefs, referencedTypes, ctorDoc, ctorSymbol)] : undefined;
3611
+ const classType = checker.getTypeAtLocation(declaration);
3612
+ const constructSignatures = classType.getConstructSignatures();
3613
+ const signatures = constructSignatures.length > 0 ? constructSignatures.map((sig, index) => ({
3614
+ ...serializeSignature(sig, checker, typeRefs, referencedTypes, ctorDoc, ctorSymbol),
3615
+ overloadIndex: constructSignatures.length > 1 ? index : undefined
3616
+ })) : undefined;
3539
3617
  members.push({
3540
3618
  id: "constructor",
3541
3619
  name: "constructor",
@@ -3677,7 +3755,7 @@ function serializeCallSignatures(signatures, symbol, context, parsedDoc) {
3677
3755
  const typeRefs = typeRegistry.getTypeRefs();
3678
3756
  const referencedTypes = typeRegistry.getReferencedTypes();
3679
3757
  const functionDoc = parsedDoc ?? (symbol ? parseJSDocComment(symbol, checker) : null);
3680
- return signatures.map((signature) => {
3758
+ return signatures.map((signature, index) => {
3681
3759
  const parameters = signature.getParameters().map((param) => {
3682
3760
  const paramDecl = param.declarations?.find(ts2.isParameter);
3683
3761
  const location = symbol?.declarations?.[0] ?? signature.declaration ?? param.declarations?.[0] ?? param.valueDeclaration;
@@ -3724,7 +3802,8 @@ function serializeCallSignatures(signatures, symbol, context, parsedDoc) {
3724
3802
  ...typePredicateInfo ? { typePredicate: typePredicateInfo } : {}
3725
3803
  },
3726
3804
  description: functionDoc?.description || undefined,
3727
- typeParameters
3805
+ typeParameters,
3806
+ overloadIndex: signatures.length > 1 ? index : undefined
3728
3807
  };
3729
3808
  });
3730
3809
  }
@@ -3785,26 +3864,37 @@ function serializeInterfaceMembers(declaration, checker, typeRefs, referencedTyp
3785
3864
  continue;
3786
3865
  const memberSymbol = member.name ? checker.getSymbolAtLocation(member.name) : undefined;
3787
3866
  const methodDoc = memberSymbol ? parseJSDocComment(memberSymbol, checker) : null;
3788
- const signature = checker.getSignatureFromDeclaration(member);
3789
- if (signature) {
3790
- const parameters = signature.getParameters().map((param) => {
3791
- const paramDecl = param.declarations?.find(ts2.isParameter);
3792
- const paramType = paramDecl ? checker.getTypeAtLocation(paramDecl) : checker.getTypeOfSymbolAtLocation(param, member);
3793
- collectReferencedTypes(paramType, checker, referencedTypes);
3794
- if (paramDecl) {
3795
- const paramDoc = getParameterDocumentation(param, paramDecl, checker);
3796
- return structureParameter(param, paramDecl, paramType, checker, typeRefs, null, paramDoc, referencedTypes);
3867
+ const methodType = memberSymbol ? checker.getTypeOfSymbolAtLocation(memberSymbol, member) : checker.getTypeAtLocation(member);
3868
+ const callSignatures = methodType.getCallSignatures();
3869
+ if (callSignatures.length > 0) {
3870
+ const signatures = callSignatures.map((signature, index) => {
3871
+ const parameters = signature.getParameters().map((param) => {
3872
+ const paramDecl = param.declarations?.find(ts2.isParameter);
3873
+ const paramType = paramDecl ? checker.getTypeAtLocation(paramDecl) : checker.getTypeOfSymbolAtLocation(param, member);
3874
+ collectReferencedTypes(paramType, checker, referencedTypes);
3875
+ if (paramDecl) {
3876
+ const paramDoc = getParameterDocumentation(param, paramDecl, checker);
3877
+ return structureParameter(param, paramDecl, paramType, checker, typeRefs, null, paramDoc, referencedTypes);
3878
+ }
3879
+ return {
3880
+ name: param.getName(),
3881
+ required: !(param.flags & ts2.SymbolFlags.Optional),
3882
+ schema: formatTypeReference(paramType, checker, typeRefs, referencedTypes)
3883
+ };
3884
+ });
3885
+ const returnType = signature.getReturnType();
3886
+ if (returnType) {
3887
+ collectReferencedTypes(returnType, checker, referencedTypes);
3797
3888
  }
3798
3889
  return {
3799
- name: param.getName(),
3800
- required: !(param.flags & ts2.SymbolFlags.Optional),
3801
- schema: formatTypeReference(paramType, checker, typeRefs, referencedTypes)
3890
+ parameters,
3891
+ returns: {
3892
+ schema: returnType ? formatTypeReference(returnType, checker, typeRefs, referencedTypes) : { type: "void" }
3893
+ },
3894
+ description: methodDoc?.description,
3895
+ overloadIndex: callSignatures.length > 1 ? index : undefined
3802
3896
  };
3803
3897
  });
3804
- const returnType = signature.getReturnType();
3805
- if (returnType) {
3806
- collectReferencedTypes(returnType, checker, referencedTypes);
3807
- }
3808
3898
  const flags = {};
3809
3899
  if (member.questionToken) {
3810
3900
  flags.optional = true;
@@ -3813,15 +3903,7 @@ function serializeInterfaceMembers(declaration, checker, typeRefs, referencedTyp
3813
3903
  id: methodName,
3814
3904
  name: methodName,
3815
3905
  kind: "method",
3816
- signatures: [
3817
- {
3818
- parameters,
3819
- returns: {
3820
- schema: returnType ? formatTypeReference(returnType, checker, typeRefs, referencedTypes) : { type: "void" }
3821
- },
3822
- description: methodDoc?.description
3823
- }
3824
- ],
3906
+ signatures,
3825
3907
  description: methodDoc?.description ?? (memberSymbol ? getJSDocComment(memberSymbol, checker) : undefined),
3826
3908
  flags: Object.keys(flags).length > 0 ? flags : undefined,
3827
3909
  tags: methodDoc?.tags
@@ -4270,7 +4352,7 @@ function buildOpenPkgSpec(context, resolveExternalTypes) {
4270
4352
  const packageJson = fs2.existsSync(packageJsonPath) ? JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8")) : {};
4271
4353
  const spec = {
4272
4354
  $schema: SCHEMA_URL,
4273
- openpkg: "0.2.0",
4355
+ openpkg: SCHEMA_VERSION,
4274
4356
  meta: {
4275
4357
  name: packageJson.name || "unknown",
4276
4358
  version: packageJson.version || "1.0.0",
@@ -4666,6 +4748,48 @@ async function extractPackageSpec(entryFile, packageDir, content, options) {
4666
4748
  });
4667
4749
  return result.spec;
4668
4750
  }
4751
+ // src/filtering/merge.ts
4752
+ function unique(values) {
4753
+ return Array.from(new Set(values));
4754
+ }
4755
+ function parseListFlag(value) {
4756
+ if (!value) {
4757
+ return;
4758
+ }
4759
+ const rawItems = Array.isArray(value) ? value : [value];
4760
+ const normalized = rawItems.flatMap((item) => String(item).split(",")).map((item) => item.trim()).filter(Boolean);
4761
+ return normalized.length > 0 ? unique(normalized) : undefined;
4762
+ }
4763
+ function mergeFilters(config, overrides) {
4764
+ const configInclude = config?.include;
4765
+ const configExclude = config?.exclude;
4766
+ const overrideInclude = overrides.include;
4767
+ const overrideExclude = overrides.exclude;
4768
+ let include = configInclude;
4769
+ let exclude = configExclude;
4770
+ let source = include || exclude ? "config" : undefined;
4771
+ const fromConfig = !!(configInclude || configExclude);
4772
+ let fromOverride = false;
4773
+ if (overrideInclude) {
4774
+ include = include ? include.filter((item) => overrideInclude.includes(item)) : overrideInclude;
4775
+ source = source ? "combined" : "override";
4776
+ fromOverride = true;
4777
+ }
4778
+ if (overrideExclude) {
4779
+ exclude = exclude ? unique([...exclude, ...overrideExclude]) : overrideExclude;
4780
+ source = source ? "combined" : "override";
4781
+ fromOverride = true;
4782
+ }
4783
+ include = include ? unique(include) : undefined;
4784
+ exclude = exclude ? unique(exclude) : undefined;
4785
+ return {
4786
+ include,
4787
+ exclude,
4788
+ source,
4789
+ fromConfig,
4790
+ fromOverride
4791
+ };
4792
+ }
4669
4793
  // src/fix/deterministic-fixes.ts
4670
4794
  var FIXABLE_DRIFT_TYPES = new Set([
4671
4795
  "param-mismatch",
@@ -7056,6 +7180,390 @@ async function runExamplesWithPackage(examples, options) {
7056
7180
  } catch {}
7057
7181
  }
7058
7182
  }
7183
+ // src/scan/summary.ts
7184
+ function extractSpecSummary(spec) {
7185
+ const exports = spec.exports ?? [];
7186
+ const undocumented = [];
7187
+ const drift = [];
7188
+ for (const exp of exports) {
7189
+ const docs = exp.docs;
7190
+ if (!docs)
7191
+ continue;
7192
+ const hasMissing = (docs.missing?.length ?? 0) > 0;
7193
+ const isPartial = (docs.coverageScore ?? 0) < 100;
7194
+ if (hasMissing || isPartial) {
7195
+ undocumented.push(exp.name);
7196
+ }
7197
+ for (const d of docs.drift ?? []) {
7198
+ drift.push({
7199
+ export: exp.name,
7200
+ type: d.type,
7201
+ issue: d.issue,
7202
+ suggestion: d.suggestion
7203
+ });
7204
+ }
7205
+ }
7206
+ return {
7207
+ coverage: spec.docs?.coverageScore ?? 0,
7208
+ exportCount: exports.length,
7209
+ typeCount: spec.types?.length ?? 0,
7210
+ driftCount: drift.length,
7211
+ undocumented,
7212
+ drift
7213
+ };
7214
+ }
7215
+ // src/install/index.ts
7216
+ var DEFAULT_FALLBACK_ORDER = ["bun", "npm"];
7217
+ async function installDependencies(fs8, cwd, runCommand2, options = {}) {
7218
+ const {
7219
+ timeout = 180000,
7220
+ fallbackOrder = DEFAULT_FALLBACK_ORDER,
7221
+ onProgress
7222
+ } = options;
7223
+ const errors = [];
7224
+ onProgress?.({ stage: "installing", message: "Detecting package manager..." });
7225
+ const pmInfo = await detectPackageManager(fs8);
7226
+ if (pmInfo.lockfile) {
7227
+ onProgress?.({
7228
+ stage: "installing",
7229
+ message: `Installing with ${pmInfo.name}...`,
7230
+ progress: 25
7231
+ });
7232
+ const installCmd = getInstallCommand(pmInfo);
7233
+ const result = await runCommand2(installCmd[0], installCmd.slice(1), { cwd, timeout });
7234
+ if (result.exitCode === 0) {
7235
+ onProgress?.({ stage: "installing", message: "Dependencies installed", progress: 100 });
7236
+ return {
7237
+ success: true,
7238
+ packageManager: pmInfo.name
7239
+ };
7240
+ }
7241
+ const errorMsg = result.stderr.slice(0, 150) || `Exit code ${result.exitCode}`;
7242
+ errors.push(`[${installCmd.join(" ")}] ${errorMsg}`);
7243
+ if (result.stderr.includes("workspace:") || result.stderr.includes("EUNSUPPORTEDPROTOCOL")) {
7244
+ onProgress?.({
7245
+ stage: "installing",
7246
+ message: "Workspace protocol detected, trying bun...",
7247
+ progress: 35
7248
+ });
7249
+ }
7250
+ }
7251
+ for (const fallbackPm of fallbackOrder) {
7252
+ if (pmInfo.lockfile && fallbackPm === pmInfo.name)
7253
+ continue;
7254
+ onProgress?.({
7255
+ stage: "installing",
7256
+ message: `Trying ${fallbackPm} fallback...`,
7257
+ progress: 50
7258
+ });
7259
+ const fallbackCmd = getFallbackInstallCommand(fallbackPm);
7260
+ const result = await runCommand2(fallbackCmd[0], fallbackCmd.slice(1), { cwd, timeout });
7261
+ if (result.exitCode === 0) {
7262
+ onProgress?.({ stage: "installing", message: "Dependencies installed", progress: 100 });
7263
+ return {
7264
+ success: true,
7265
+ packageManager: fallbackPm,
7266
+ fallbackUsed: fallbackPm
7267
+ };
7268
+ }
7269
+ const errorMsg = result.stderr.slice(0, 150) || `Exit code ${result.exitCode}`;
7270
+ errors.push(`[${fallbackCmd.join(" ")}] ${errorMsg}`);
7271
+ }
7272
+ onProgress?.({
7273
+ stage: "installing",
7274
+ message: "Could not install dependencies",
7275
+ progress: 100
7276
+ });
7277
+ return {
7278
+ success: false,
7279
+ packageManager: pmInfo.name,
7280
+ error: "All installation attempts failed",
7281
+ errors
7282
+ };
7283
+ }
7284
+ function getFallbackInstallCommand(pm) {
7285
+ switch (pm) {
7286
+ case "npm":
7287
+ return ["npm", "install", "--legacy-peer-deps", "--ignore-scripts"];
7288
+ case "bun":
7289
+ return ["bun", "install"];
7290
+ case "yarn":
7291
+ return ["yarn", "install"];
7292
+ case "pnpm":
7293
+ return ["pnpm", "install"];
7294
+ default:
7295
+ return ["npm", "install"];
7296
+ }
7297
+ }
7298
+ function createNodeCommandRunner() {
7299
+ return async (cmd, args, options) => {
7300
+ const { execSync } = await import("node:child_process");
7301
+ const fullCmd = [cmd, ...args].join(" ");
7302
+ try {
7303
+ const stdout = execSync(fullCmd, {
7304
+ cwd: options.cwd,
7305
+ stdio: "pipe",
7306
+ timeout: options.timeout ?? 180000
7307
+ });
7308
+ return {
7309
+ exitCode: 0,
7310
+ stdout: stdout?.toString() ?? "",
7311
+ stderr: ""
7312
+ };
7313
+ } catch (error) {
7314
+ const err = error;
7315
+ return {
7316
+ exitCode: err.status ?? 1,
7317
+ stdout: err.stdout?.toString() ?? "",
7318
+ stderr: err.stderr?.toString() ?? err.message ?? "Unknown error"
7319
+ };
7320
+ }
7321
+ };
7322
+ }
7323
+
7324
+ // src/github/index.ts
7325
+ function parseGitHubUrl(input, defaultRef = "main") {
7326
+ const trimmed = input.trim();
7327
+ if (!trimmed) {
7328
+ throw new Error("GitHub URL cannot be empty");
7329
+ }
7330
+ let normalized = trimmed.replace(/^https?:\/\//, "").replace(/^git@github\.com:/, "").replace(/\.git$/, "");
7331
+ normalized = normalized.replace(/^github\.com\//, "");
7332
+ const parts = normalized.split("/").filter(Boolean);
7333
+ if (parts.length < 2) {
7334
+ throw new Error(`Invalid GitHub URL format: "${input}". Expected owner/repo or https://github.com/owner/repo`);
7335
+ }
7336
+ const owner = parts[0];
7337
+ const repo = parts[1];
7338
+ let ref = defaultRef;
7339
+ if (parts.length >= 4 && (parts[2] === "tree" || parts[2] === "blob")) {
7340
+ ref = parts.slice(3).join("/");
7341
+ }
7342
+ if (!owner || !repo) {
7343
+ throw new Error(`Could not parse owner/repo from: "${input}"`);
7344
+ }
7345
+ return { owner, repo, ref };
7346
+ }
7347
+ function buildCloneUrl(parsed) {
7348
+ return `https://github.com/${parsed.owner}/${parsed.repo}.git`;
7349
+ }
7350
+ function buildDisplayUrl(parsed) {
7351
+ return `github.com/${parsed.owner}/${parsed.repo}`;
7352
+ }
7353
+ function buildRawUrl(parsed, filePath) {
7354
+ return `https://raw.githubusercontent.com/${parsed.owner}/${parsed.repo}/${parsed.ref}/${filePath}`;
7355
+ }
7356
+ async function fetchSpecFromGitHub(parsed) {
7357
+ const urls = [
7358
+ buildRawUrl(parsed, "openpkg.json"),
7359
+ `https://raw.githubusercontent.com/${parsed.owner}/${parsed.repo}/master/openpkg.json`
7360
+ ];
7361
+ for (const url of urls) {
7362
+ try {
7363
+ const response = await fetch(url);
7364
+ if (response.ok) {
7365
+ return await response.json();
7366
+ }
7367
+ } catch {}
7368
+ }
7369
+ return null;
7370
+ }
7371
+ async function fetchSpec(owner, repo, branch = "main") {
7372
+ return fetchSpecFromGitHub({ owner, repo, ref: branch });
7373
+ }
7374
+
7375
+ // src/scan/orchestrator.ts
7376
+ class ScanOrchestrator {
7377
+ fs;
7378
+ options;
7379
+ constructor(fs8, options = {}) {
7380
+ this.fs = fs8;
7381
+ this.options = options;
7382
+ }
7383
+ emit(event) {
7384
+ this.options.onProgress?.(event);
7385
+ }
7386
+ async detectPackage(packageName) {
7387
+ this.emit({ stage: "detecting", message: "Detecting project structure...", progress: 10 });
7388
+ const mono = await detectMonorepo(this.fs);
7389
+ if (mono.isMonorepo) {
7390
+ if (!packageName) {
7391
+ const publicPackages = mono.packages.filter((p) => !p.private);
7392
+ throw new MonorepoRequiresPackageError(publicPackages.map((p) => p.name));
7393
+ }
7394
+ const pkg = findPackageByName(mono.packages, packageName);
7395
+ if (!pkg) {
7396
+ throw new Error(`Package "${packageName}" not found. Available: ${mono.packages.map((p) => p.name).join(", ")}`);
7397
+ }
7398
+ this.emit({ stage: "detecting", message: `Found package: ${pkg.name}`, progress: 15 });
7399
+ return { targetPath: pkg.path, resolvedPackage: pkg.name };
7400
+ }
7401
+ return { targetPath: "." };
7402
+ }
7403
+ async detectEntry(targetPath) {
7404
+ this.emit({ stage: "detecting", message: "Detecting entry point...", progress: 18 });
7405
+ const entry = await detectEntryPoint(this.fs, targetPath);
7406
+ const entryFile = targetPath === "." ? entry.path : `${targetPath}/${entry.path}`;
7407
+ this.emit({
7408
+ stage: "detecting",
7409
+ message: `Entry point: ${entry.path} (from ${entry.source})`,
7410
+ progress: 20
7411
+ });
7412
+ return entryFile;
7413
+ }
7414
+ async install(workDir) {
7415
+ if (!this.options.commandRunner) {
7416
+ return {
7417
+ success: false,
7418
+ packageManager: "npm",
7419
+ error: "No command runner provided"
7420
+ };
7421
+ }
7422
+ this.emit({ stage: "installing", message: "Installing dependencies...", progress: 25 });
7423
+ const result = await installDependencies(this.fs, workDir, this.options.commandRunner, {
7424
+ onProgress: this.options.onProgress
7425
+ });
7426
+ if (result.success) {
7427
+ this.emit({ stage: "installing", message: "Dependencies installed", progress: 45 });
7428
+ } else {
7429
+ this.emit({
7430
+ stage: "installing",
7431
+ message: "Install failed (continuing with limited analysis)",
7432
+ progress: 45
7433
+ });
7434
+ }
7435
+ return result;
7436
+ }
7437
+ async build(workDir, targetPath) {
7438
+ if (!this.options.commandRunner)
7439
+ return;
7440
+ const buildInfo = await detectBuildInfo(this.fs, targetPath);
7441
+ const buildScript = getPrimaryBuildScript(buildInfo);
7442
+ if (!buildScript)
7443
+ return;
7444
+ this.emit({ stage: "building", message: "Running build...", progress: 50 });
7445
+ const result = await this.options.commandRunner("npm", ["run", buildScript], {
7446
+ cwd: workDir,
7447
+ timeout: 300000
7448
+ });
7449
+ const buildMessage = result.exitCode === 0 ? "Build complete" : "Build failed (continuing)";
7450
+ this.emit({ stage: "building", message: buildMessage, progress: 60 });
7451
+ }
7452
+ async analyze(entryFile) {
7453
+ this.emit({ stage: "analyzing", message: "Analyzing documentation...", progress: 65 });
7454
+ const doccov = new DocCov({ resolveExternalTypes: !this.options.skipResolve });
7455
+ const result = await doccov.analyzeFileWithDiagnostics(entryFile);
7456
+ this.emit({ stage: "analyzing", message: "Analysis complete", progress: 90 });
7457
+ return result.spec;
7458
+ }
7459
+ async scan(options) {
7460
+ const parsed = parseGitHubUrl(options.url, options.ref ?? "main");
7461
+ this.emit({
7462
+ stage: "detecting",
7463
+ message: `Scanning ${parsed.owner}/${parsed.repo}...`,
7464
+ progress: 5
7465
+ });
7466
+ const { targetPath, resolvedPackage } = await this.detectPackage(options.package);
7467
+ const entryFile = await this.detectEntry(targetPath);
7468
+ if (!options.skipInstall && this.options.commandRunner) {
7469
+ await this.install(".");
7470
+ await this.build(".", targetPath);
7471
+ }
7472
+ const spec = await this.analyze(entryFile);
7473
+ this.emit({ stage: "complete", message: "Extracting results...", progress: 95 });
7474
+ const summary = extractSpecSummary(spec);
7475
+ this.emit({ stage: "complete", message: "Scan complete", progress: 100 });
7476
+ return {
7477
+ owner: parsed.owner,
7478
+ repo: parsed.repo,
7479
+ ref: parsed.ref,
7480
+ packageName: resolvedPackage ?? options.package,
7481
+ coverage: summary.coverage,
7482
+ exportCount: summary.exportCount,
7483
+ typeCount: summary.typeCount,
7484
+ driftCount: summary.driftCount,
7485
+ undocumented: summary.undocumented,
7486
+ drift: summary.drift
7487
+ };
7488
+ }
7489
+ }
7490
+
7491
+ class MonorepoRequiresPackageError extends Error {
7492
+ availablePackages;
7493
+ constructor(availablePackages) {
7494
+ super(`Monorepo detected with ${availablePackages.length} packages. ` + `Specify target with --package. Available: ${availablePackages.join(", ")}`);
7495
+ this.name = "MonorepoRequiresPackageError";
7496
+ this.availablePackages = availablePackages;
7497
+ }
7498
+ }
7499
+ // src/resolve/index.ts
7500
+ import * as path9 from "node:path";
7501
+ async function resolveTarget(fs8, options) {
7502
+ let targetDir = options.cwd;
7503
+ let packageInfo;
7504
+ if (options.package) {
7505
+ const mono = await detectMonorepo(fs8);
7506
+ if (!mono.isMonorepo) {
7507
+ throw new Error("Not a monorepo. Remove --package flag for single-package repos.");
7508
+ }
7509
+ const pkg = findPackageByName(mono.packages, options.package);
7510
+ if (!pkg) {
7511
+ const available = mono.packages.map((p) => p.name).join(", ");
7512
+ throw new Error(`Package "${options.package}" not found. Available: ${available}`);
7513
+ }
7514
+ targetDir = path9.join(options.cwd, pkg.path);
7515
+ packageInfo = pkg;
7516
+ }
7517
+ let entryFile;
7518
+ let entryPointInfo;
7519
+ if (!options.entry) {
7520
+ entryPointInfo = await detectEntryPoint(fs8, getRelativePath(options.cwd, targetDir));
7521
+ entryFile = path9.join(targetDir, entryPointInfo.path);
7522
+ } else {
7523
+ const explicitPath = path9.resolve(targetDir, options.entry);
7524
+ const isDirectory = await isDir(fs8, getRelativePath(options.cwd, explicitPath));
7525
+ if (isDirectory) {
7526
+ targetDir = explicitPath;
7527
+ entryPointInfo = await detectEntryPoint(fs8, getRelativePath(options.cwd, explicitPath));
7528
+ entryFile = path9.join(explicitPath, entryPointInfo.path);
7529
+ } else {
7530
+ entryFile = explicitPath;
7531
+ entryPointInfo = {
7532
+ path: options.entry,
7533
+ source: "explicit",
7534
+ isDeclarationOnly: options.entry.endsWith(".d.ts")
7535
+ };
7536
+ }
7537
+ }
7538
+ return {
7539
+ targetDir,
7540
+ entryFile,
7541
+ packageInfo,
7542
+ entryPointInfo
7543
+ };
7544
+ }
7545
+ function getRelativePath(base, target) {
7546
+ if (base === target)
7547
+ return ".";
7548
+ const rel = path9.relative(base, target);
7549
+ return rel || ".";
7550
+ }
7551
+ async function isDir(fs8, relativePath) {
7552
+ const hasPackageJson = await fs8.exists(path9.join(relativePath, "package.json"));
7553
+ if (hasPackageJson)
7554
+ return true;
7555
+ const commonEntryFiles = ["index.ts", "index.tsx", "src/index.ts", "main.ts"];
7556
+ for (const entry of commonEntryFiles) {
7557
+ if (await fs8.exists(path9.join(relativePath, entry))) {
7558
+ return true;
7559
+ }
7560
+ }
7561
+ return !path9.extname(relativePath);
7562
+ }
7563
+ // src/config/types.ts
7564
+ function defineConfig(config) {
7565
+ return config;
7566
+ }
7059
7567
  export {
7060
7568
  typecheckExamples,
7061
7569
  typecheckExample,
@@ -7064,20 +7572,25 @@ export {
7064
7572
  runExamplesWithPackage,
7065
7573
  runExamples,
7066
7574
  runExample,
7575
+ resolveTarget,
7067
7576
  requireExample,
7068
7577
  requireDescription,
7069
7578
  readPackageJson,
7070
7579
  parseMarkdownFiles,
7071
7580
  parseMarkdownFile,
7581
+ parseListFlag,
7072
7582
  parseJSDocToPatch,
7583
+ parseGitHubUrl,
7073
7584
  parseAssertions,
7074
7585
  noEmptyReturns,
7075
7586
  mergeFixes,
7587
+ mergeFilters,
7076
7588
  mergeConfig,
7077
7589
  lintExports,
7078
7590
  lintExport,
7079
7591
  isFixableDrift,
7080
7592
  isExecutableLang,
7593
+ installDependencies,
7081
7594
  hasNonAssertionComments,
7082
7595
  hasDocsImpact,
7083
7596
  hasDocsForExport,
@@ -7097,6 +7610,9 @@ export {
7097
7610
  findJSDocLocation,
7098
7611
  findExportReferences,
7099
7612
  findDeprecatedReferences,
7613
+ fetchSpecFromGitHub,
7614
+ fetchSpec,
7615
+ extractSpecSummary,
7100
7616
  extractPackageSpec,
7101
7617
  extractImports,
7102
7618
  extractFunctionCalls,
@@ -7107,9 +7623,14 @@ export {
7107
7623
  detectExampleAssertionFailures,
7108
7624
  detectEntryPoint,
7109
7625
  detectBuildInfo,
7626
+ defineConfig,
7110
7627
  createSourceFile,
7628
+ createNodeCommandRunner,
7111
7629
  consistentParamStyle,
7112
7630
  categorizeDrifts,
7631
+ buildRawUrl,
7632
+ buildDisplayUrl,
7633
+ buildCloneUrl,
7113
7634
  blockReferencesExport,
7114
7635
  applyPatchToJSDoc,
7115
7636
  applyEdits,
@@ -7118,8 +7639,10 @@ export {
7118
7639
  analyzeDocsImpact,
7119
7640
  analyze,
7120
7641
  allRules,
7642
+ ScanOrchestrator,
7121
7643
  SandboxFileSystem,
7122
7644
  OpenPkg,
7123
7645
  NodeFileSystem,
7646
+ MonorepoRequiresPackageError,
7124
7647
  DocCov
7125
7648
  };