@forge-ts/enforcer 0.7.2 → 0.13.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.d.ts CHANGED
@@ -41,6 +41,15 @@ declare function findDeprecatedUsages(symbols: ForgeSymbol[]): DeprecatedUsage[]
41
41
  * | W001 | warning | TSDoc comment contains parse errors. |
42
42
  * | W002 | warning | Function body throws but has no `@throws` tag. |
43
43
  * | W003 | warning | `@deprecated` tag is present without explanation. |
44
+ * | W006 | warning | TSDoc parser-level syntax error (invalid tag, malformed block, etc.). |
45
+ * | E009 | error | tsconfig.json required strict-mode flag is missing or disabled (guard). |
46
+ * | E010 | error | Config drift: a rule severity is weaker than the locked value. |
47
+ * | E013 | error | Exported function/class is missing a `@remarks` block. |
48
+ * | E014 | warn | Optional property of interface/type is missing `@defaultValue`. |
49
+ * | E015 | error | Generic symbol is missing `@typeParam` for a type parameter. |
50
+ * | W005 | warn | Symbol references other symbols via `{@link}` but has no `@see` tags. |
51
+ * | W007 | warn | Guide FORGE:AUTO section references a symbol that no longer exists. |
52
+ * | W008 | warn | Exported public symbol is not mentioned in any guide page. |
44
53
  *
45
54
  * When `config.enforce.strict` is `true` all warnings are promoted to errors.
46
55
  *
package/dist/index.js CHANGED
@@ -38,9 +38,14 @@ function extractPackageName(filePath) {
38
38
  }
39
39
 
40
40
  // src/enforcer.ts
41
+ import { existsSync, readdirSync, readFileSync } from "fs";
42
+ import { join } from "path";
41
43
  import {
42
44
  createWalker,
43
- filterByVisibility
45
+ filterByVisibility,
46
+ isRuleBypassed,
47
+ readLockFile,
48
+ validateAgainstLock
44
49
  } from "@forge-ts/core";
45
50
  function hasSummary(symbol) {
46
51
  return symbol.documentation?.summary !== void 0 && symbol.documentation.summary.trim().length > 0;
@@ -95,6 +100,83 @@ function deprecatedWithoutReason(symbol) {
95
100
  if (deprecated === void 0) return false;
96
101
  return deprecated === "true" || deprecated.trim().length === 0;
97
102
  }
103
+ function extractGenericTypeParams(signature) {
104
+ if (!signature || !signature.startsWith("<")) return [];
105
+ let depth = 0;
106
+ let endIdx = -1;
107
+ for (let i = 0; i < signature.length; i++) {
108
+ if (signature[i] === "<") depth++;
109
+ else if (signature[i] === ">") {
110
+ depth--;
111
+ if (depth === 0) {
112
+ endIdx = i;
113
+ break;
114
+ }
115
+ }
116
+ }
117
+ if (endIdx === -1) return [];
118
+ const inner = signature.slice(1, endIdx);
119
+ const params = [];
120
+ let paramDepth = 0;
121
+ let current = "";
122
+ for (const ch of inner) {
123
+ if (ch === "<" || ch === "(") {
124
+ paramDepth++;
125
+ current += ch;
126
+ } else if (ch === ">" || ch === ")") {
127
+ paramDepth--;
128
+ current += ch;
129
+ } else if (ch === "," && paramDepth === 0) {
130
+ params.push(current);
131
+ current = "";
132
+ } else {
133
+ current += ch;
134
+ }
135
+ }
136
+ if (current.trim()) params.push(current);
137
+ return params.map((p) => p.trim().split(/\s+/)[0].trim()).filter((p) => p.length > 0);
138
+ }
139
+ function isOptionalProperty(child) {
140
+ const sig = child.signature;
141
+ if (!sig) return false;
142
+ if (sig.includes("| undefined") || sig.includes("undefined |")) return true;
143
+ return false;
144
+ }
145
+ function extractBiomeRules(biome) {
146
+ const result = {};
147
+ const linter = biome.linter;
148
+ if (!linter) return result;
149
+ const rules = linter.rules;
150
+ if (!rules) return result;
151
+ for (const [group, groupRules] of Object.entries(rules)) {
152
+ if (group === "recommended" || group === "all" || typeof groupRules !== "object" || groupRules === null)
153
+ continue;
154
+ for (const [ruleName, ruleValue] of Object.entries(groupRules)) {
155
+ const fullName = `${group}/${ruleName}`;
156
+ if (typeof ruleValue === "string") {
157
+ result[fullName] = ruleValue;
158
+ } else if (typeof ruleValue === "object" && ruleValue !== null && "level" in ruleValue) {
159
+ result[fullName] = String(ruleValue.level);
160
+ }
161
+ }
162
+ }
163
+ return result;
164
+ }
165
+ function isWeakerBiomeLevel(current, locked) {
166
+ const rank = { off: 0, warn: 1, error: 2 };
167
+ const currentRank = rank[current] ?? 0;
168
+ const lockedRank = rank[locked] ?? 0;
169
+ return currentRank < lockedRank;
170
+ }
171
+ function parseSemverMajorMinor(version) {
172
+ const match = version.match(/(\d+)\.(\d+)/);
173
+ if (!match) return null;
174
+ return [Number(match[1]), Number(match[2])];
175
+ }
176
+ function compareMajorMinor(a, b) {
177
+ if (a[0] !== b[0]) return a[0] - b[0];
178
+ return a[1] - b[1];
179
+ }
98
180
  var RULE_MAP = {
99
181
  E001: "require-summary",
100
182
  E002: "require-param",
@@ -102,7 +184,15 @@ var RULE_MAP = {
102
184
  E004: "require-example",
103
185
  E005: "require-package-doc",
104
186
  E006: "require-class-member-doc",
105
- E007: "require-interface-member-doc"
187
+ E007: "require-interface-member-doc",
188
+ W006: "require-tsdoc-syntax",
189
+ E013: "require-remarks",
190
+ E014: "require-default-value",
191
+ E015: "require-type-param",
192
+ W005: "require-see",
193
+ E016: "require-release-tag",
194
+ W007: "require-fresh-guides",
195
+ W008: "require-guide-coverage"
106
196
  };
107
197
  async function enforce(config) {
108
198
  const start = Date.now();
@@ -226,6 +316,107 @@ async function enforce(config) {
226
316
  }
227
317
  }
228
318
  }
319
+ if (symbol.kind === "function" || symbol.kind === "class") {
320
+ const hasRemarks = symbol.documentation?.tags?.remarks !== void 0;
321
+ if (!hasRemarks) {
322
+ emit(
323
+ "E013",
324
+ `Exported ${symbol.kind} "${symbol.name}" is missing a @remarks block.`,
325
+ symbol.filePath,
326
+ symbol.line,
327
+ symbol.column,
328
+ {
329
+ suggestedFix: `@remarks [Detailed description of ${symbol.name}]`,
330
+ symbolName: symbol.name,
331
+ symbolKind: symbol.kind
332
+ }
333
+ );
334
+ }
335
+ }
336
+ if (symbol.kind === "interface" || symbol.kind === "type") {
337
+ for (const child of symbol.children ?? []) {
338
+ if (child.kind !== "property") continue;
339
+ if (!isOptionalProperty(child)) continue;
340
+ const hasDefaultValue = child.documentation?.tags?.defaultValue !== void 0;
341
+ if (!hasDefaultValue) {
342
+ emit(
343
+ "E014",
344
+ `Optional property "${child.name}" of ${symbol.kind} "${symbol.name}" is missing @defaultValue.`,
345
+ child.filePath,
346
+ child.line,
347
+ child.column,
348
+ {
349
+ suggestedFix: `@defaultValue [Default value of ${child.name}]`,
350
+ symbolName: child.name,
351
+ symbolKind: child.kind
352
+ }
353
+ );
354
+ }
355
+ }
356
+ }
357
+ if (symbol.kind === "function" || symbol.kind === "class" || symbol.kind === "interface") {
358
+ const typeParamNames = extractGenericTypeParams(symbol.signature);
359
+ if (typeParamNames.length > 0) {
360
+ const documentedTypeParams = new Set(
361
+ (symbol.documentation?.tags?.typeParam ?? []).map(
362
+ (tp) => tp.split(/\s/)[0].replace(/-$/, "").trim()
363
+ )
364
+ );
365
+ for (const typeParamName of typeParamNames) {
366
+ if (!documentedTypeParams.has(typeParamName)) {
367
+ emit(
368
+ "E015",
369
+ `Type parameter "${typeParamName}" of "${symbol.name}" is not documented with @typeParam.`,
370
+ symbol.filePath,
371
+ symbol.line,
372
+ symbol.column,
373
+ {
374
+ suggestedFix: `@typeParam ${typeParamName} - [Description of ${typeParamName}]`,
375
+ symbolName: symbol.name,
376
+ symbolKind: symbol.kind
377
+ }
378
+ );
379
+ }
380
+ }
381
+ }
382
+ }
383
+ if (symbol.documentation?.links && symbol.documentation.links.length > 0) {
384
+ const hasSee = symbol.documentation.tags?.see !== void 0 && symbol.documentation.tags.see.length > 0;
385
+ if (!hasSee) {
386
+ emit(
387
+ "W005",
388
+ `"${symbol.name}" references other symbols via {@link} but has no @see tags.`,
389
+ symbol.filePath,
390
+ symbol.line,
391
+ symbol.column,
392
+ {
393
+ suggestedFix: "@see [Related symbol name]",
394
+ symbolName: symbol.name,
395
+ symbolKind: symbol.kind
396
+ }
397
+ );
398
+ }
399
+ }
400
+ {
401
+ const releaseTags = ["public", "beta", "internal", "alpha"];
402
+ const hasReleaseTag = releaseTags.some(
403
+ (tag) => symbol.documentation?.tags?.[tag] !== void 0
404
+ );
405
+ if (!hasReleaseTag) {
406
+ emit(
407
+ "E016",
408
+ `Exported symbol "${symbol.name}" is missing a release tag (@public, @beta, or @internal).`,
409
+ symbol.filePath,
410
+ symbol.line,
411
+ symbol.column,
412
+ {
413
+ suggestedFix: "@public",
414
+ symbolName: symbol.name,
415
+ symbolKind: symbol.kind
416
+ }
417
+ );
418
+ }
419
+ }
229
420
  if (deprecatedWithoutReason(symbol)) {
230
421
  emit(
231
422
  "W003",
@@ -239,6 +430,40 @@ async function enforce(config) {
239
430
  }
240
431
  );
241
432
  }
433
+ if (symbol.documentation?.parseMessages) {
434
+ for (const msg of symbol.documentation.parseMessages) {
435
+ emit(
436
+ "W006",
437
+ `TSDoc syntax: ${msg.text} [${msg.messageId}]`,
438
+ symbol.filePath,
439
+ msg.line,
440
+ symbol.column,
441
+ {
442
+ symbolName: symbol.name,
443
+ symbolKind: symbol.kind
444
+ }
445
+ );
446
+ }
447
+ }
448
+ if (symbol.children) {
449
+ for (const child of symbol.children) {
450
+ if (child.documentation?.parseMessages) {
451
+ for (const msg of child.documentation.parseMessages) {
452
+ emit(
453
+ "W006",
454
+ `TSDoc syntax: ${msg.text} [${msg.messageId}]`,
455
+ child.filePath,
456
+ msg.line,
457
+ child.column,
458
+ {
459
+ symbolName: child.name,
460
+ symbolKind: child.kind
461
+ }
462
+ );
463
+ }
464
+ }
465
+ }
466
+ }
242
467
  }
243
468
  const indexFiles = /* @__PURE__ */ new Map();
244
469
  for (const symbol of allSymbols) {
@@ -312,6 +537,280 @@ async function enforce(config) {
312
537
  }
313
538
  );
314
539
  }
540
+ const e009Bypassed = isRuleBypassed(config.rootDir, "E009");
541
+ if (config.guards.tsconfig.enabled) {
542
+ const tsconfigPath = join(config.rootDir, "tsconfig.json");
543
+ try {
544
+ const raw = readFileSync(tsconfigPath, "utf-8");
545
+ let parsed;
546
+ try {
547
+ parsed = JSON.parse(raw);
548
+ } catch (parseErr) {
549
+ if (e009Bypassed) {
550
+ warnings.push({
551
+ code: "E009",
552
+ message: `[BYPASSED] tsconfig.json: failed to parse \u2014 ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`,
553
+ filePath: tsconfigPath,
554
+ line: 1,
555
+ column: 0
556
+ });
557
+ } else {
558
+ errors.push({
559
+ code: "E009",
560
+ message: `tsconfig.json: failed to parse \u2014 ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`,
561
+ filePath: tsconfigPath,
562
+ line: 1,
563
+ column: 0
564
+ });
565
+ }
566
+ }
567
+ if (parsed) {
568
+ const compilerOptions = parsed.compilerOptions ?? {};
569
+ const requiredFlags = config.guards.tsconfig.requiredFlags;
570
+ for (const flag of requiredFlags) {
571
+ const value = compilerOptions[flag];
572
+ if (value !== true) {
573
+ if (e009Bypassed) {
574
+ warnings.push({
575
+ code: "E009",
576
+ message: `[BYPASSED] tsconfig.json: required flag "${flag}" is ${value === false ? "disabled" : "missing"} \u2014 expected true`,
577
+ filePath: tsconfigPath,
578
+ line: 1,
579
+ column: 0
580
+ });
581
+ } else {
582
+ errors.push({
583
+ code: "E009",
584
+ message: `tsconfig.json: required flag "${flag}" is ${value === false ? "disabled" : "missing"} \u2014 expected true`,
585
+ filePath: tsconfigPath,
586
+ line: 1,
587
+ column: 0
588
+ });
589
+ }
590
+ }
591
+ }
592
+ }
593
+ } catch (readErr) {
594
+ if (readErr instanceof Error && "code" in readErr && readErr.code === "ENOENT") {
595
+ } else {
596
+ throw readErr;
597
+ }
598
+ }
599
+ }
600
+ const e010Bypassed = isRuleBypassed(config.rootDir, "E010");
601
+ const lockManifest = readLockFile(config.rootDir);
602
+ if (lockManifest) {
603
+ const lockViolations = validateAgainstLock(config, lockManifest);
604
+ const lockFilePath = join(config.rootDir, ".forge-lock.json");
605
+ for (const violation of lockViolations) {
606
+ if (e010Bypassed) {
607
+ warnings.push({
608
+ code: "E010",
609
+ message: `[BYPASSED] Config drift: ${violation.message}`,
610
+ filePath: lockFilePath,
611
+ line: 1,
612
+ column: 0
613
+ });
614
+ } else {
615
+ errors.push({
616
+ code: "E010",
617
+ message: `Config drift: ${violation.message}`,
618
+ filePath: lockFilePath,
619
+ line: 1,
620
+ column: 0
621
+ });
622
+ }
623
+ }
624
+ }
625
+ const e011Bypassed = isRuleBypassed(config.rootDir, "E011");
626
+ if (config.guards.biome.enabled && lockManifest?.config.biome) {
627
+ const biomePath = join(config.rootDir, "biome.json");
628
+ const biomePathC = join(config.rootDir, "biome.jsonc");
629
+ const actualBiomePath = existsSync(biomePath) ? biomePath : existsSync(biomePathC) ? biomePathC : null;
630
+ if (actualBiomePath) {
631
+ try {
632
+ const biomeRaw = readFileSync(actualBiomePath, "utf-8");
633
+ let biomeParsed;
634
+ try {
635
+ biomeParsed = JSON.parse(biomeRaw);
636
+ } catch {
637
+ const diag = {
638
+ code: "E011",
639
+ message: `Biome config "${actualBiomePath}": failed to parse \u2014 file may contain invalid JSON.`,
640
+ filePath: actualBiomePath,
641
+ line: 1,
642
+ column: 0
643
+ };
644
+ if (e011Bypassed) {
645
+ warnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });
646
+ } else {
647
+ errors.push(diag);
648
+ }
649
+ biomeParsed = void 0;
650
+ }
651
+ if (biomeParsed) {
652
+ const currentBiomeRules = extractBiomeRules(biomeParsed);
653
+ const lockedBiomeRules = lockManifest.config.biome.rules ?? {};
654
+ for (const [ruleName, lockedLevel] of Object.entries(lockedBiomeRules)) {
655
+ const currentLevel = currentBiomeRules[ruleName] ?? "off";
656
+ if (isWeakerBiomeLevel(currentLevel, lockedLevel)) {
657
+ const diag = {
658
+ code: "E011",
659
+ message: `Biome rule "${ruleName}" was weakened from "${lockedLevel}" to "${currentLevel}".`,
660
+ filePath: actualBiomePath,
661
+ line: 1,
662
+ column: 0
663
+ };
664
+ if (e011Bypassed) {
665
+ warnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });
666
+ } else {
667
+ errors.push(diag);
668
+ }
669
+ }
670
+ }
671
+ }
672
+ } catch (readErr) {
673
+ if (readErr instanceof Error && "code" in readErr && readErr.code === "ENOENT") {
674
+ } else {
675
+ throw readErr;
676
+ }
677
+ }
678
+ }
679
+ }
680
+ const e012Bypassed = isRuleBypassed(config.rootDir, "E012");
681
+ if (config.guards.packageJson.enabled) {
682
+ const pkgJsonPath = join(config.rootDir, "package.json");
683
+ try {
684
+ const pkgRaw = readFileSync(pkgJsonPath, "utf-8");
685
+ const pkg = JSON.parse(pkgRaw);
686
+ for (const field of config.guards.packageJson.requiredFields) {
687
+ if (pkg[field] === void 0) {
688
+ const diag = {
689
+ code: "E012",
690
+ message: `package.json: required field "${field}" is missing.`,
691
+ filePath: pkgJsonPath,
692
+ line: 1,
693
+ column: 0
694
+ };
695
+ if (e012Bypassed) {
696
+ warnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });
697
+ } else {
698
+ errors.push(diag);
699
+ }
700
+ }
701
+ }
702
+ const engines = pkg.engines;
703
+ const nodeEngine = engines?.node;
704
+ if (!nodeEngine) {
705
+ if (engines !== void 0) {
706
+ const diag = {
707
+ code: "E012",
708
+ message: `package.json: "engines.node" field is missing.`,
709
+ filePath: pkgJsonPath,
710
+ line: 1,
711
+ column: 0
712
+ };
713
+ if (e012Bypassed) {
714
+ warnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });
715
+ } else {
716
+ errors.push(diag);
717
+ }
718
+ }
719
+ } else {
720
+ const minVersion = parseSemverMajorMinor(config.guards.packageJson.minNodeVersion);
721
+ const engineVersion = parseSemverMajorMinor(nodeEngine);
722
+ if (minVersion && engineVersion && compareMajorMinor(engineVersion, minVersion) < 0) {
723
+ const diag = {
724
+ code: "E012",
725
+ message: `package.json: "engines.node" specifies "${nodeEngine}" which is lower than the minimum required "${config.guards.packageJson.minNodeVersion}".`,
726
+ filePath: pkgJsonPath,
727
+ line: 1,
728
+ column: 0
729
+ };
730
+ if (e012Bypassed) {
731
+ warnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });
732
+ } else {
733
+ errors.push(diag);
734
+ }
735
+ }
736
+ }
737
+ } catch (readErr) {
738
+ if (readErr instanceof Error && "code" in readErr && readErr.code === "ENOENT") {
739
+ } else if (readErr instanceof SyntaxError) {
740
+ } else {
741
+ throw readErr;
742
+ }
743
+ }
744
+ }
745
+ const guidesDir = join(config.outDir, "guides");
746
+ let guideFiles = [];
747
+ let guideContents;
748
+ if (existsSync(guidesDir)) {
749
+ try {
750
+ const entries = readdirSync(guidesDir);
751
+ guideFiles = entries.filter((f) => f.endsWith(".md") || f.endsWith(".mdx"));
752
+ } catch {
753
+ }
754
+ }
755
+ if (guideFiles.length > 0) {
756
+ guideContents = /* @__PURE__ */ new Map();
757
+ for (const file of guideFiles) {
758
+ try {
759
+ const content = readFileSync(join(guidesDir, file), "utf-8");
760
+ guideContents.set(file, content);
761
+ } catch {
762
+ }
763
+ }
764
+ const autoStartRe = /<!--\s*FORGE:AUTO-START\s+(\S+)\s*-->/g;
765
+ const autoEndRe = /<!--\s*FORGE:AUTO-END\s+(\S+)\s*-->/;
766
+ const symbolRefRe = /`([A-Za-z_$][A-Za-z0-9_$]*)`/g;
767
+ for (const [file, content] of guideContents) {
768
+ const filePath = join(guidesDir, file);
769
+ autoStartRe.lastIndex = 0;
770
+ for (let match = autoStartRe.exec(content); match !== null; match = autoStartRe.exec(content)) {
771
+ const startIdx = match.index + match[0].length;
772
+ const restContent = content.slice(startIdx);
773
+ const endMatch = autoEndRe.exec(restContent);
774
+ if (!endMatch) continue;
775
+ const autoBlock = restContent.slice(0, endMatch.index);
776
+ symbolRefRe.lastIndex = 0;
777
+ for (let refMatch = symbolRefRe.exec(autoBlock); refMatch !== null; refMatch = symbolRefRe.exec(autoBlock)) {
778
+ const refName = refMatch[1];
779
+ if (!knownSymbols.has(refName)) {
780
+ emit(
781
+ "W007",
782
+ `Guide "${file}" FORGE:AUTO section references symbol "${refName}" which no longer exists in the symbol graph.`,
783
+ filePath,
784
+ 1,
785
+ 0,
786
+ {
787
+ symbolName: refName
788
+ }
789
+ );
790
+ }
791
+ }
792
+ }
793
+ }
794
+ const allGuideText = [...guideContents.values()].join("\n");
795
+ const indexExportedSymbols = allSymbols.filter(
796
+ (s) => s.exported && /[/\\]index\.ts$/.test(s.filePath)
797
+ );
798
+ for (const sym of indexExportedSymbols) {
799
+ if (!allGuideText.includes(sym.name)) {
800
+ emit(
801
+ "W008",
802
+ `Exported symbol "${sym.name}" from "${sym.filePath}" is not mentioned in any guide page.`,
803
+ sym.filePath,
804
+ sym.line,
805
+ sym.column,
806
+ {
807
+ symbolName: sym.name,
808
+ symbolKind: sym.kind
809
+ }
810
+ );
811
+ }
812
+ }
813
+ }
315
814
  const success = errors.length === 0;
316
815
  return { success, symbols: allSymbols, errors, warnings, duration: Date.now() - start };
317
816
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/deprecation-tracker.ts","../src/enforcer.ts","../src/formatter.ts"],"sourcesContent":["import type { ForgeSymbol } from \"@forge-ts/core\";\n\n/** A detected usage of a deprecated symbol. */\nexport interface DeprecatedUsage {\n\t/** The deprecated symbol being consumed. */\n\tdeprecatedSymbol: string;\n\t/** The package that exports the deprecated symbol. */\n\tsourcePackage: string;\n\t/** The file importing the deprecated symbol. */\n\tconsumingFile: string;\n\t/** Line number of the import. */\n\tline: number;\n\t/** The deprecation message. */\n\tdeprecationMessage: string;\n}\n\n/**\n * Scans symbols for imports of deprecated exports from other packages.\n *\n * @param symbols - All symbols from the walker across the entire project.\n * @returns Array of deprecated usages found.\n */\nexport function findDeprecatedUsages(symbols: ForgeSymbol[]): DeprecatedUsage[] {\n\t// Build a set of deprecated symbol names with their source info\n\tconst deprecatedExports = new Map<string, { sourceFile: string; message: string }>();\n\n\tfor (const symbol of symbols) {\n\t\tif (symbol.exported && symbol.documentation?.deprecated) {\n\t\t\tdeprecatedExports.set(symbol.name, {\n\t\t\t\tsourceFile: symbol.filePath,\n\t\t\t\tmessage: symbol.documentation.deprecated,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (deprecatedExports.size === 0) return [];\n\n\t// For each symbol that has a {@link} or references a deprecated name,\n\t// check if it's from a different package\n\tconst usages: DeprecatedUsage[] = [];\n\n\t// Check links\n\tfor (const symbol of symbols) {\n\t\tconst links = symbol.documentation?.links ?? [];\n\t\tfor (const link of links) {\n\t\t\tconst deprecated = deprecatedExports.get(link.target);\n\t\t\tif (deprecated && deprecated.sourceFile !== symbol.filePath) {\n\t\t\t\t// Different file references a deprecated symbol\n\t\t\t\tconst sourcePackage = extractPackageName(deprecated.sourceFile);\n\t\t\t\tconst consumingPackage = extractPackageName(symbol.filePath);\n\n\t\t\t\tif (sourcePackage !== consumingPackage) {\n\t\t\t\t\tusages.push({\n\t\t\t\t\t\tdeprecatedSymbol: link.target,\n\t\t\t\t\t\tsourcePackage,\n\t\t\t\t\t\tconsumingFile: symbol.filePath,\n\t\t\t\t\t\tline: link.line,\n\t\t\t\t\t\tdeprecationMessage: deprecated.message,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn usages;\n}\n\n/** Extract package name from file path (e.g., \"packages/core/src/...\" -> \"core\"). */\nfunction extractPackageName(filePath: string): string {\n\tconst match = filePath.match(/packages\\/([^/]+)\\//);\n\treturn match?.[1] ?? \"root\";\n}\n","import {\n\tcreateWalker,\n\ttype EnforceRules,\n\ttype ForgeConfig,\n\ttype ForgeError,\n\ttype ForgeResult,\n\ttype ForgeSymbol,\n\ttype ForgeWarning,\n\tfilterByVisibility,\n} from \"@forge-ts/core\";\nimport { findDeprecatedUsages } from \"./deprecation-tracker.js\";\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Checks whether a symbol has at least a summary in its documentation.\n * @internal\n */\nfunction hasSummary(symbol: ForgeSymbol): boolean {\n\treturn (\n\t\tsymbol.documentation?.summary !== undefined && symbol.documentation.summary.trim().length > 0\n\t);\n}\n\n/**\n * Splits a signature parameter list on top-level commas, respecting angle\n * bracket nesting so that `Record<string, string[]>` is not split.\n * @internal\n */\nfunction splitParams(raw: string): string[] {\n\tconst parts: string[] = [];\n\tlet depth = 0;\n\tlet current = \"\";\n\tfor (const ch of raw) {\n\t\tif (ch === \"<\" || ch === \"(\") {\n\t\t\tdepth++;\n\t\t\tcurrent += ch;\n\t\t} else if (ch === \">\" || ch === \")\") {\n\t\t\tdepth--;\n\t\t\tcurrent += ch;\n\t\t} else if (ch === \",\" && depth === 0) {\n\t\t\tparts.push(current);\n\t\t\tcurrent = \"\";\n\t\t} else {\n\t\t\tcurrent += ch;\n\t\t}\n\t}\n\tif (current.trim()) {\n\t\tparts.push(current);\n\t}\n\treturn parts;\n}\n\n/**\n * Returns the names of parameters that are declared on a function/method symbol\n * but lack a corresponding `@param` tag in its documentation.\n *\n * Since the AST walker populates `documentation.params` from parsed TSDoc, we\n * compare the set of documented param names against the names that appear in\n * the symbol's type signature. When no signature is available the check is\n * skipped (returns empty array).\n *\n * @internal\n */\nfunction undocumentedParams(symbol: ForgeSymbol): string[] {\n\tconst sig = symbol.signature;\n\tif (!sig) return [];\n\n\t// Parse parameter names out of the signature string.\n\t// Signatures look like: \"(a: string, b: number) => void\"\n\t// Must handle nested generics: \"(tags: Record<string, string[]>) => void\"\n\tconst parenMatch = sig.match(/^\\(([^)]*)\\)/);\n\tif (!parenMatch || !parenMatch[1].trim()) return [];\n\n\tconst rawParams = splitParams(parenMatch[1])\n\t\t.map((p) =>\n\t\t\tp\n\t\t\t\t.trim()\n\t\t\t\t.split(\":\")[0]\n\t\t\t\t.trim()\n\t\t\t\t.replace(/^\\.{3}/, \"\")\n\t\t\t\t.replace(/\\?$/, \"\")\n\t\t\t\t.trim(),\n\t\t)\n\t\t.filter((p) => p.length > 0 && p !== \"this\");\n\n\tif (rawParams.length === 0) return [];\n\n\tconst documentedNames = new Set((symbol.documentation?.params ?? []).map((p) => p.name));\n\treturn rawParams.filter((name) => !documentedNames.has(name));\n}\n\n/**\n * Returns `true` when a function/method symbol has a non-void return type but\n * no `@returns` block in its documentation.\n * @internal\n */\nfunction missingReturns(symbol: ForgeSymbol): boolean {\n\tconst sig = symbol.signature;\n\tif (!sig) return false;\n\n\t// Extract return type: everything after the last \"=>\"\n\tconst arrowIdx = sig.lastIndexOf(\"=>\");\n\tif (arrowIdx === -1) return false;\n\tconst returnType = sig.slice(arrowIdx + 2).trim();\n\n\tconst isVoidLike =\n\t\treturnType === \"void\" ||\n\t\treturnType === \"never\" ||\n\t\treturnType === \"undefined\" ||\n\t\treturnType.startsWith(\"Promise<void>\") ||\n\t\treturnType.startsWith(\"Promise<never>\") ||\n\t\treturnType.startsWith(\"Promise<undefined>\");\n\n\tif (isVoidLike) return false;\n\treturn symbol.documentation?.returns === undefined;\n}\n\n/**\n * Returns `true` when a `@deprecated` tag is present but carries no\n * explanatory text.\n * @internal\n */\nfunction deprecatedWithoutReason(symbol: ForgeSymbol): boolean {\n\tconst deprecated = symbol.documentation?.deprecated;\n\tif (deprecated === undefined) return false;\n\t// The walker stores `\"true\"` when the tag has no content.\n\treturn deprecated === \"true\" || deprecated.trim().length === 0;\n}\n\n// ---------------------------------------------------------------------------\n// Rule map\n// ---------------------------------------------------------------------------\n\n/**\n * Maps E-code strings to their corresponding {@link EnforceRules} key.\n * @internal\n */\nconst RULE_MAP: Record<string, keyof EnforceRules> = {\n\tE001: \"require-summary\",\n\tE002: \"require-param\",\n\tE003: \"require-returns\",\n\tE004: \"require-example\",\n\tE005: \"require-package-doc\",\n\tE006: \"require-class-member-doc\",\n\tE007: \"require-interface-member-doc\",\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Runs the TSDoc enforcement pass against a project.\n *\n * The enforcer walks all exported symbols that meet the configured minimum\n * visibility threshold and emits diagnostics for any documentation deficiencies\n * it finds.\n *\n * ### Error codes\n * | Code | Severity | Condition |\n * |------|----------|-----------|\n * | E001 | error | Exported symbol is missing a TSDoc summary. |\n * | E002 | error | Function/method parameter lacks a `@param` tag. |\n * | E003 | error | Non-void function/method lacks a `@returns` tag. |\n * | E004 | error | Exported function/method is missing an `@example` block. |\n * | E005 | error | Package entry point (index.ts) is missing `@packageDocumentation`. |\n * | E006 | error | Public/protected class member is missing a TSDoc comment. |\n * | E007 | error | Interface/type alias property is missing a TSDoc comment. |\n * | W001 | warning | TSDoc comment contains parse errors. |\n * | W002 | warning | Function body throws but has no `@throws` tag. |\n * | W003 | warning | `@deprecated` tag is present without explanation. |\n *\n * When `config.enforce.strict` is `true` all warnings are promoted to errors.\n *\n * @param config - The resolved {@link ForgeConfig} for the project.\n * @returns A {@link ForgeResult} describing which symbols passed or failed.\n * @example\n * ```typescript\n * import { loadConfig } from \"@forge-ts/core\";\n * import { enforce } from \"@forge-ts/enforcer\";\n * const config = await loadConfig();\n * const result = await enforce(config);\n * if (!result.success) {\n * console.error(`${result.errors.length} errors found`);\n * }\n * ```\n * @public\n */\nexport async function enforce(config: ForgeConfig): Promise<ForgeResult> {\n\tconst start = Date.now();\n\tconst errors: ForgeError[] = [];\n\tconst warnings: ForgeWarning[] = [];\n\n\tconst walker = createWalker(config);\n\tconst allSymbols = walker.walk();\n\tconst symbols = filterByVisibility(allSymbols, config.enforce.minVisibility);\n\n\t/**\n\t * Emit a diagnostic. The configured per-rule severity determines whether\n\t * the diagnostic is an error or warning; \"off\" suppresses it entirely.\n\t * When `strict` is enabled every warning is promoted to an error.\n\t */\n\tfunction emit(\n\t\tcode: string,\n\t\tmessage: string,\n\t\tfilePath: string,\n\t\tline: number,\n\t\tcolumn: number,\n\t\tguidance?: { suggestedFix?: string; symbolName?: string; symbolKind?: string },\n\t): void {\n\t\tconst ruleKey = RULE_MAP[code];\n\n\t\t// For rule codes tracked in the map, honour the per-rule severity.\n\t\tif (ruleKey !== undefined) {\n\t\t\tconst configuredSeverity = config.enforce.rules[ruleKey];\n\t\t\tif (configuredSeverity === \"off\") return;\n\t\t\tconst effectiveSeverity = config.enforce.strict ? \"error\" : configuredSeverity;\n\t\t\tconst diag = { code, message, filePath, line, column, ...guidance };\n\t\t\tif (effectiveSeverity === \"error\") {\n\t\t\t\terrors.push(diag);\n\t\t\t} else {\n\t\t\t\twarnings.push(diag);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// For codes not in the map (W003, E008, etc.) fall back to the old\n\t\t// behaviour: always emit, respect strict mode for warnings.\n\t\tconst diag = { code, message, filePath, line, column, ...guidance };\n\t\t// Codes starting with \"W\" are warnings by default.\n\t\tif (code.startsWith(\"W\") && !config.enforce.strict) {\n\t\t\twarnings.push(diag);\n\t\t} else {\n\t\t\terrors.push(diag);\n\t\t}\n\t}\n\n\tfor (const symbol of symbols) {\n\t\tif (!symbol.exported) continue;\n\n\t\tconst isFunctionLike = symbol.kind === \"function\" || symbol.kind === \"method\";\n\n\t\t// E001 — Missing summary\n\t\tif (!hasSummary(symbol)) {\n\t\t\temit(\n\t\t\t\t\"E001\",\n\t\t\t\t`Exported symbol \"${symbol.name}\" is missing a TSDoc summary comment.`,\n\t\t\t\tsymbol.filePath,\n\t\t\t\tsymbol.line,\n\t\t\t\tsymbol.column,\n\t\t\t\t{\n\t\t\t\t\tsuggestedFix: `/**\\n * [Description of ${symbol.name}]\\n */`,\n\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// E002 — Undocumented parameters\n\t\tif (isFunctionLike) {\n\t\t\tconst missing = undocumentedParams(symbol);\n\t\t\tfor (const paramName of missing) {\n\t\t\t\temit(\n\t\t\t\t\t\"E002\",\n\t\t\t\t\t`Parameter \"${paramName}\" of \"${symbol.name}\" is not documented with a @param tag.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tsymbol.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: `@param ${paramName} - [Description of ${paramName}]`,\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// E003 — Missing @returns\n\t\tif (isFunctionLike && missingReturns(symbol)) {\n\t\t\temit(\n\t\t\t\t\"E003\",\n\t\t\t\t`\"${symbol.name}\" has a non-void return type but is missing a @returns tag.`,\n\t\t\t\tsymbol.filePath,\n\t\t\t\tsymbol.line,\n\t\t\t\tsymbol.column,\n\t\t\t\t{\n\t\t\t\t\tsuggestedFix: `@returns [Description of the return value]`,\n\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// E004 — Missing @example\n\t\tif (isFunctionLike && symbol.documentation) {\n\t\t\tconst hasExample = (symbol.documentation.examples ?? []).length > 0;\n\t\t\tif (!hasExample) {\n\t\t\t\temit(\n\t\t\t\t\t\"E004\",\n\t\t\t\t\t`Exported function \"${symbol.name}\" is missing an @example block. Add a fenced code block showing usage.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tsymbol.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: `@example\\n * \\`\\`\\`typescript\\n * // Usage of ${symbol.name}\\n * ${symbol.name}();\\n * \\`\\`\\``,\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// E006 — Class member missing documentation\n\t\t// E007 — Interface/type member missing documentation\n\t\tif (symbol.kind === \"class\" || symbol.kind === \"interface\") {\n\t\t\tconst errorCode = symbol.kind === \"class\" ? \"E006\" : \"E007\";\n\t\t\tfor (const child of symbol.children ?? []) {\n\t\t\t\tif (child.kind === \"property\" || child.kind === \"method\") {\n\t\t\t\t\tif (!hasSummary(child)) {\n\t\t\t\t\t\temit(\n\t\t\t\t\t\t\terrorCode,\n\t\t\t\t\t\t\t`Member \"${child.name}\" of ${symbol.kind} \"${symbol.name}\" is missing a TSDoc comment.`,\n\t\t\t\t\t\t\tchild.filePath,\n\t\t\t\t\t\t\tchild.line,\n\t\t\t\t\t\t\tchild.column,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsuggestedFix: `/**\\n * [Description of ${child.name}]\\n */`,\n\t\t\t\t\t\t\t\tsymbolName: child.name,\n\t\t\t\t\t\t\t\tsymbolKind: child.kind,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// W003 — @deprecated without reason\n\t\tif (deprecatedWithoutReason(symbol)) {\n\t\t\temit(\n\t\t\t\t\"W003\",\n\t\t\t\t`\"${symbol.name}\" is marked @deprecated but provides no explanation.`,\n\t\t\t\tsymbol.filePath,\n\t\t\t\tsymbol.line,\n\t\t\t\tsymbol.column,\n\t\t\t\t{\n\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t// E005 — Missing @packageDocumentation on index.ts entry points\n\t// Group symbols by file to check if any index.ts file lacks @packageDocumentation.\n\tconst indexFiles = new Map<string, ForgeSymbol[]>();\n\tfor (const symbol of allSymbols) {\n\t\tif (symbol.filePath.endsWith(\"index.ts\")) {\n\t\t\tconst bucket = indexFiles.get(symbol.filePath) ?? [];\n\t\t\tbucket.push(symbol);\n\t\t\tindexFiles.set(symbol.filePath, bucket);\n\t\t}\n\t}\n\tfor (const [filePath, fileSymbols] of indexFiles) {\n\t\tconst hasPackageDoc = fileSymbols.some(\n\t\t\t(s) => s.documentation?.tags?.packageDocumentation !== undefined,\n\t\t);\n\t\tif (!hasPackageDoc) {\n\t\t\temit(\n\t\t\t\t\"E005\",\n\t\t\t\t`Package entry point \"${filePath}\" is missing a @packageDocumentation TSDoc comment.`,\n\t\t\t\tfilePath,\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t\t{\n\t\t\t\t\tsuggestedFix: `/**\\n * @packageDocumentation\\n * [Package overview description]\\n */`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t// E008 — Dead {@link} references\n\t// Build a set of all known symbol names (simple and qualified).\n\tconst knownSymbols = new Set<string>();\n\tfor (const s of allSymbols) {\n\t\tknownSymbols.add(s.name);\n\t\tif (s.children) {\n\t\t\tfor (const child of s.children) {\n\t\t\t\tknownSymbols.add(`${s.name}.${child.name}`);\n\t\t\t\tknownSymbols.add(child.name);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check all {@link} references across every symbol (not just filtered ones).\n\tfor (const symbol of allSymbols) {\n\t\tconst docLinks = symbol.documentation?.links ?? [];\n\t\tfor (const link of docLinks) {\n\t\t\tif (!knownSymbols.has(link.target)) {\n\t\t\t\temit(\n\t\t\t\t\t\"E008\",\n\t\t\t\t\t`{@link ${link.target}} in \"${symbol.name}\" references a symbol that does not exist in this project.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tlink.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: `Remove or update the {@link ${link.target}} reference to point to an existing symbol.`,\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// W004 — Cross-package deprecated symbol usage\n\tconst deprecatedUsages = findDeprecatedUsages(allSymbols);\n\tfor (const usage of deprecatedUsages) {\n\t\temit(\n\t\t\t\"W004\",\n\t\t\t`Import of deprecated symbol \"${usage.deprecatedSymbol}\" from package \"${usage.sourcePackage}\": ${usage.deprecationMessage}`,\n\t\t\tusage.consumingFile,\n\t\t\tusage.line,\n\t\t\t0,\n\t\t\t{\n\t\t\t\tsuggestedFix: `Replace usage of \"${usage.deprecatedSymbol}\" with its recommended replacement.`,\n\t\t\t\tsymbolName: usage.deprecatedSymbol,\n\t\t\t\tsymbolKind: \"variable\",\n\t\t\t},\n\t\t);\n\t}\n\n\tconst success = errors.length === 0;\n\treturn { success, symbols: allSymbols, errors, warnings, duration: Date.now() - start };\n}\n","import type { ForgeResult } from \"@forge-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Options that control how {@link formatResults} renders its output.\n * @public\n */\nexport interface FormatOptions {\n\t/** Emit ANSI colour escape sequences when `true`. */\n\tcolors: boolean;\n\t/**\n\t * When `true`, include the symbol's type signature alongside each\n\t * diagnostic so the reader has immediate context.\n\t */\n\tverbose: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// ANSI helpers\n// ---------------------------------------------------------------------------\n\n/** @internal */\nconst RESET = \"\\x1b[0m\";\n/** @internal */\nconst RED = \"\\x1b[31m\";\n/** @internal */\nconst YELLOW = \"\\x1b[33m\";\n/** @internal */\nconst BOLD = \"\\x1b[1m\";\n/** @internal */\nconst DIM = \"\\x1b[2m\";\n\n/** @internal */\nfunction colorize(text: string, color: string, useColors: boolean): string {\n\treturn useColors ? `${color}${text}${RESET}` : text;\n}\n\n/** @internal */\nfunction bold(text: string, useColors: boolean): string {\n\treturn useColors ? `${BOLD}${text}${RESET}` : text;\n}\n\n/** @internal */\nfunction dim(text: string, useColors: boolean): string {\n\treturn useColors ? `${DIM}${text}${RESET}` : text;\n}\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\n/** @internal */\ninterface Diagnostic {\n\tcode: string;\n\tmessage: string;\n\tfilePath: string;\n\tline: number;\n\tcolumn: number;\n}\n\n/** @internal */\nfunction isError(code: string): boolean {\n\treturn code.startsWith(\"E\");\n}\n\n/** @internal */\nfunction renderDiagnostic(diag: Diagnostic, opts: FormatOptions): string {\n\tconst label = isError(diag.code)\n\t\t? colorize(`error[${diag.code}]`, RED, opts.colors)\n\t\t: colorize(`warning[${diag.code}]`, YELLOW, opts.colors);\n\n\tconst location = dim(`${diag.line}:${diag.column}`, opts.colors);\n\treturn ` ${label} ${diag.message} ${location}`;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Formats a {@link ForgeResult} into a human-readable string suitable for\n * printing to a terminal.\n *\n * Diagnostics are grouped by source file. Each file heading shows the\n * relative-ish path, followed by indented error and warning lines. A summary\n * line is appended at the end.\n *\n * @param result - The result produced by {@link enforce}.\n * @param options - Rendering options (colours, verbosity).\n * @returns A formatted string ready to write to stdout or stderr.\n * @example\n * ```typescript\n * import { enforce } from \"@forge-ts/enforcer\";\n * import { formatResults } from \"@forge-ts/enforcer\";\n * import { loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const result = await enforce(config);\n * console.log(formatResults(result, { colors: true, verbose: false }));\n * ```\n * @public\n */\nexport function formatResults(result: ForgeResult, options: FormatOptions): string {\n\tconst allDiags: Diagnostic[] = [\n\t\t...result.errors.map((e) => ({ ...e })),\n\t\t...result.warnings.map((w) => ({ ...w })),\n\t];\n\n\tif (allDiags.length === 0) {\n\t\tconst msg = `No issues found across ${result.symbols.length} symbol(s).`;\n\t\treturn bold(msg, options.colors);\n\t}\n\n\t// Group by filePath\n\tconst byFile = new Map<string, Diagnostic[]>();\n\tfor (const diag of allDiags) {\n\t\tconst list = byFile.get(diag.filePath);\n\t\tif (list) {\n\t\t\tlist.push(diag);\n\t\t} else {\n\t\t\tbyFile.set(diag.filePath, [diag]);\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\n\tfor (const [filePath, diags] of byFile) {\n\t\tlines.push(bold(filePath, options.colors));\n\n\t\t// Sort: errors before warnings, then by line\n\t\tconst sorted = [...diags].sort((a, b) => {\n\t\t\tconst aIsErr = isError(a.code) ? 0 : 1;\n\t\t\tconst bIsErr = isError(b.code) ? 0 : 1;\n\t\t\tif (aIsErr !== bIsErr) return aIsErr - bIsErr;\n\t\t\treturn a.line - b.line;\n\t\t});\n\n\t\tfor (const diag of sorted) {\n\t\t\tlines.push(renderDiagnostic(diag, options));\n\n\t\t\tif (options.verbose) {\n\t\t\t\t// Find the matching symbol to show its signature\n\t\t\t\tconst sym = result.symbols.find(\n\t\t\t\t\t(s) => s.filePath === diag.filePath && s.line === diag.line,\n\t\t\t\t);\n\t\t\t\tif (sym?.signature) {\n\t\t\t\t\tlines.push(dim(` signature: ${sym.signature}`, options.colors));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlines.push(\"\");\n\t}\n\n\t// Summary line\n\tconst errorCount = result.errors.length;\n\tconst warnCount = result.warnings.length;\n\tconst fileCount = byFile.size;\n\n\tconst errorPart =\n\t\terrorCount > 0\n\t\t\t? colorize(`${errorCount} error${errorCount !== 1 ? \"s\" : \"\"}`, RED, options.colors)\n\t\t\t: `0 errors`;\n\tconst warnPart =\n\t\twarnCount > 0\n\t\t\t? colorize(`${warnCount} warning${warnCount !== 1 ? \"s\" : \"\"}`, YELLOW, options.colors)\n\t\t\t: `0 warnings`;\n\tconst filePart = `${fileCount} file${fileCount !== 1 ? \"s\" : \"\"}`;\n\n\tlines.push(`${errorPart}, ${warnPart} in ${filePart}`);\n\n\treturn lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["diag"]}
1
+ {"version":3,"sources":["../src/deprecation-tracker.ts","../src/enforcer.ts","../src/formatter.ts"],"sourcesContent":["import type { ForgeSymbol } from \"@forge-ts/core\";\n\n/** A detected usage of a deprecated symbol. */\nexport interface DeprecatedUsage {\n\t/** The deprecated symbol being consumed. */\n\tdeprecatedSymbol: string;\n\t/** The package that exports the deprecated symbol. */\n\tsourcePackage: string;\n\t/** The file importing the deprecated symbol. */\n\tconsumingFile: string;\n\t/** Line number of the import. */\n\tline: number;\n\t/** The deprecation message. */\n\tdeprecationMessage: string;\n}\n\n/**\n * Scans symbols for imports of deprecated exports from other packages.\n *\n * @param symbols - All symbols from the walker across the entire project.\n * @returns Array of deprecated usages found.\n */\nexport function findDeprecatedUsages(symbols: ForgeSymbol[]): DeprecatedUsage[] {\n\t// Build a set of deprecated symbol names with their source info\n\tconst deprecatedExports = new Map<string, { sourceFile: string; message: string }>();\n\n\tfor (const symbol of symbols) {\n\t\tif (symbol.exported && symbol.documentation?.deprecated) {\n\t\t\tdeprecatedExports.set(symbol.name, {\n\t\t\t\tsourceFile: symbol.filePath,\n\t\t\t\tmessage: symbol.documentation.deprecated,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (deprecatedExports.size === 0) return [];\n\n\t// For each symbol that has a {@link} or references a deprecated name,\n\t// check if it's from a different package\n\tconst usages: DeprecatedUsage[] = [];\n\n\t// Check links\n\tfor (const symbol of symbols) {\n\t\tconst links = symbol.documentation?.links ?? [];\n\t\tfor (const link of links) {\n\t\t\tconst deprecated = deprecatedExports.get(link.target);\n\t\t\tif (deprecated && deprecated.sourceFile !== symbol.filePath) {\n\t\t\t\t// Different file references a deprecated symbol\n\t\t\t\tconst sourcePackage = extractPackageName(deprecated.sourceFile);\n\t\t\t\tconst consumingPackage = extractPackageName(symbol.filePath);\n\n\t\t\t\tif (sourcePackage !== consumingPackage) {\n\t\t\t\t\tusages.push({\n\t\t\t\t\t\tdeprecatedSymbol: link.target,\n\t\t\t\t\t\tsourcePackage,\n\t\t\t\t\t\tconsumingFile: symbol.filePath,\n\t\t\t\t\t\tline: link.line,\n\t\t\t\t\t\tdeprecationMessage: deprecated.message,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn usages;\n}\n\n/** Extract package name from file path (e.g., \"packages/core/src/...\" -> \"core\"). */\nfunction extractPackageName(filePath: string): string {\n\tconst match = filePath.match(/packages\\/([^/]+)\\//);\n\treturn match?.[1] ?? \"root\";\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tcreateWalker,\n\ttype EnforceRules,\n\ttype ForgeConfig,\n\ttype ForgeError,\n\ttype ForgeResult,\n\ttype ForgeSymbol,\n\ttype ForgeWarning,\n\tfilterByVisibility,\n\tisRuleBypassed,\n\treadLockFile,\n\tvalidateAgainstLock,\n} from \"@forge-ts/core\";\nimport { findDeprecatedUsages } from \"./deprecation-tracker.js\";\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Checks whether a symbol has at least a summary in its documentation.\n * @internal\n */\nfunction hasSummary(symbol: ForgeSymbol): boolean {\n\treturn (\n\t\tsymbol.documentation?.summary !== undefined && symbol.documentation.summary.trim().length > 0\n\t);\n}\n\n/**\n * Splits a signature parameter list on top-level commas, respecting angle\n * bracket nesting so that `Record<string, string[]>` is not split.\n * @internal\n */\nfunction splitParams(raw: string): string[] {\n\tconst parts: string[] = [];\n\tlet depth = 0;\n\tlet current = \"\";\n\tfor (const ch of raw) {\n\t\tif (ch === \"<\" || ch === \"(\") {\n\t\t\tdepth++;\n\t\t\tcurrent += ch;\n\t\t} else if (ch === \">\" || ch === \")\") {\n\t\t\tdepth--;\n\t\t\tcurrent += ch;\n\t\t} else if (ch === \",\" && depth === 0) {\n\t\t\tparts.push(current);\n\t\t\tcurrent = \"\";\n\t\t} else {\n\t\t\tcurrent += ch;\n\t\t}\n\t}\n\tif (current.trim()) {\n\t\tparts.push(current);\n\t}\n\treturn parts;\n}\n\n/**\n * Returns the names of parameters that are declared on a function/method symbol\n * but lack a corresponding `@param` tag in its documentation.\n *\n * Since the AST walker populates `documentation.params` from parsed TSDoc, we\n * compare the set of documented param names against the names that appear in\n * the symbol's type signature. When no signature is available the check is\n * skipped (returns empty array).\n *\n * @internal\n */\nfunction undocumentedParams(symbol: ForgeSymbol): string[] {\n\tconst sig = symbol.signature;\n\tif (!sig) return [];\n\n\t// Parse parameter names out of the signature string.\n\t// Signatures look like: \"(a: string, b: number) => void\"\n\t// Must handle nested generics: \"(tags: Record<string, string[]>) => void\"\n\tconst parenMatch = sig.match(/^\\(([^)]*)\\)/);\n\tif (!parenMatch || !parenMatch[1].trim()) return [];\n\n\tconst rawParams = splitParams(parenMatch[1])\n\t\t.map((p) =>\n\t\t\tp\n\t\t\t\t.trim()\n\t\t\t\t.split(\":\")[0]\n\t\t\t\t.trim()\n\t\t\t\t.replace(/^\\.{3}/, \"\")\n\t\t\t\t.replace(/\\?$/, \"\")\n\t\t\t\t.trim(),\n\t\t)\n\t\t.filter((p) => p.length > 0 && p !== \"this\");\n\n\tif (rawParams.length === 0) return [];\n\n\tconst documentedNames = new Set((symbol.documentation?.params ?? []).map((p) => p.name));\n\treturn rawParams.filter((name) => !documentedNames.has(name));\n}\n\n/**\n * Returns `true` when a function/method symbol has a non-void return type but\n * no `@returns` block in its documentation.\n * @internal\n */\nfunction missingReturns(symbol: ForgeSymbol): boolean {\n\tconst sig = symbol.signature;\n\tif (!sig) return false;\n\n\t// Extract return type: everything after the last \"=>\"\n\tconst arrowIdx = sig.lastIndexOf(\"=>\");\n\tif (arrowIdx === -1) return false;\n\tconst returnType = sig.slice(arrowIdx + 2).trim();\n\n\tconst isVoidLike =\n\t\treturnType === \"void\" ||\n\t\treturnType === \"never\" ||\n\t\treturnType === \"undefined\" ||\n\t\treturnType.startsWith(\"Promise<void>\") ||\n\t\treturnType.startsWith(\"Promise<never>\") ||\n\t\treturnType.startsWith(\"Promise<undefined>\");\n\n\tif (isVoidLike) return false;\n\treturn symbol.documentation?.returns === undefined;\n}\n\n/**\n * Returns `true` when a `@deprecated` tag is present but carries no\n * explanatory text.\n * @internal\n */\nfunction deprecatedWithoutReason(symbol: ForgeSymbol): boolean {\n\tconst deprecated = symbol.documentation?.deprecated;\n\tif (deprecated === undefined) return false;\n\t// The walker stores `\"true\"` when the tag has no content.\n\treturn deprecated === \"true\" || deprecated.trim().length === 0;\n}\n\n/**\n * Extracts generic type parameter names from a symbol's signature.\n * Handles patterns like `<T>`, `<T, U>`, `<T extends Record<string, unknown>>`, etc.\n * Respects nested angle brackets so constraints are not prematurely closed.\n * @internal\n */\nfunction extractGenericTypeParams(signature: string | undefined): string[] {\n\tif (!signature || !signature.startsWith(\"<\")) return [];\n\t// Find the matching closing '>' respecting nesting\n\tlet depth = 0;\n\tlet endIdx = -1;\n\tfor (let i = 0; i < signature.length; i++) {\n\t\tif (signature[i] === \"<\") depth++;\n\t\telse if (signature[i] === \">\") {\n\t\t\tdepth--;\n\t\t\tif (depth === 0) {\n\t\t\t\tendIdx = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (endIdx === -1) return [];\n\tconst inner = signature.slice(1, endIdx);\n\t// Split on top-level commas (depth 0)\n\tconst params: string[] = [];\n\tlet paramDepth = 0;\n\tlet current = \"\";\n\tfor (const ch of inner) {\n\t\tif (ch === \"<\" || ch === \"(\") {\n\t\t\tparamDepth++;\n\t\t\tcurrent += ch;\n\t\t} else if (ch === \">\" || ch === \")\") {\n\t\t\tparamDepth--;\n\t\t\tcurrent += ch;\n\t\t} else if (ch === \",\" && paramDepth === 0) {\n\t\t\tparams.push(current);\n\t\t\tcurrent = \"\";\n\t\t} else {\n\t\t\tcurrent += ch;\n\t\t}\n\t}\n\tif (current.trim()) params.push(current);\n\treturn params.map((p) => p.trim().split(/\\s+/)[0].trim()).filter((p) => p.length > 0);\n}\n\n/**\n * Returns `true` when a property signature looks optional —\n * i.e. includes `| undefined` in the type.\n * @internal\n */\nfunction isOptionalProperty(child: ForgeSymbol): boolean {\n\tconst sig = child.signature;\n\tif (!sig) return false;\n\tif (sig.includes(\"| undefined\") || sig.includes(\"undefined |\")) return true;\n\treturn false;\n}\n\n/**\n * Extracts a flat map of biome rule names to their configured level\n * from a parsed biome.json structure.\n * Walks `linter.rules.<group>.<ruleName>` and normalises both string\n * and object (`{ level: \"error\" }`) forms.\n * @internal\n */\nfunction extractBiomeRules(biome: Record<string, unknown>): Record<string, string> {\n\tconst result: Record<string, string> = {};\n\tconst linter = biome.linter as Record<string, unknown> | undefined;\n\tif (!linter) return result;\n\tconst rules = linter.rules as Record<string, unknown> | undefined;\n\tif (!rules) return result;\n\tfor (const [group, groupRules] of Object.entries(rules)) {\n\t\tif (\n\t\t\tgroup === \"recommended\" ||\n\t\t\tgroup === \"all\" ||\n\t\t\ttypeof groupRules !== \"object\" ||\n\t\t\tgroupRules === null\n\t\t)\n\t\t\tcontinue;\n\t\tfor (const [ruleName, ruleValue] of Object.entries(groupRules as Record<string, unknown>)) {\n\t\t\tconst fullName = `${group}/${ruleName}`;\n\t\t\tif (typeof ruleValue === \"string\") {\n\t\t\t\tresult[fullName] = ruleValue;\n\t\t\t} else if (typeof ruleValue === \"object\" && ruleValue !== null && \"level\" in ruleValue) {\n\t\t\t\tresult[fullName] = String((ruleValue as Record<string, unknown>).level);\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\n/**\n * Returns `true` when `current` is a weaker biome level than `locked`.\n * Ranking: error > warn > off.\n * @internal\n */\nfunction isWeakerBiomeLevel(current: string, locked: string): boolean {\n\tconst rank: Record<string, number> = { off: 0, warn: 1, error: 2 };\n\tconst currentRank = rank[current] ?? 0;\n\tconst lockedRank = rank[locked] ?? 0;\n\treturn currentRank < lockedRank;\n}\n\n/**\n * Extracts major.minor from a semver-like string.\n * Handles patterns like \">=22.0.0\", \"^22.0.0\", \"~22.0.0\", \"22.0.0\", \"22.0\".\n * @internal\n */\nfunction parseSemverMajorMinor(version: string): [number, number] | null {\n\tconst match = version.match(/(\\d+)\\.(\\d+)/);\n\tif (!match) return null;\n\treturn [Number(match[1]), Number(match[2])];\n}\n\n/**\n * Compares two [major, minor] tuples.\n * Returns negative if a < b, 0 if equal, positive if a > b.\n * @internal\n */\nfunction compareMajorMinor(a: [number, number], b: [number, number]): number {\n\tif (a[0] !== b[0]) return a[0] - b[0];\n\treturn a[1] - b[1];\n}\n\n// ---------------------------------------------------------------------------\n// Rule map\n// ---------------------------------------------------------------------------\n\n/**\n * Maps E-code strings to their corresponding {@link EnforceRules} key.\n * @internal\n */\nconst RULE_MAP: Record<string, keyof EnforceRules> = {\n\tE001: \"require-summary\",\n\tE002: \"require-param\",\n\tE003: \"require-returns\",\n\tE004: \"require-example\",\n\tE005: \"require-package-doc\",\n\tE006: \"require-class-member-doc\",\n\tE007: \"require-interface-member-doc\",\n\tW006: \"require-tsdoc-syntax\",\n\tE013: \"require-remarks\",\n\tE014: \"require-default-value\",\n\tE015: \"require-type-param\",\n\tW005: \"require-see\",\n\tE016: \"require-release-tag\",\n\tW007: \"require-fresh-guides\",\n\tW008: \"require-guide-coverage\",\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Runs the TSDoc enforcement pass against a project.\n *\n * The enforcer walks all exported symbols that meet the configured minimum\n * visibility threshold and emits diagnostics for any documentation deficiencies\n * it finds.\n *\n * ### Error codes\n * | Code | Severity | Condition |\n * |------|----------|-----------|\n * | E001 | error | Exported symbol is missing a TSDoc summary. |\n * | E002 | error | Function/method parameter lacks a `@param` tag. |\n * | E003 | error | Non-void function/method lacks a `@returns` tag. |\n * | E004 | error | Exported function/method is missing an `@example` block. |\n * | E005 | error | Package entry point (index.ts) is missing `@packageDocumentation`. |\n * | E006 | error | Public/protected class member is missing a TSDoc comment. |\n * | E007 | error | Interface/type alias property is missing a TSDoc comment. |\n * | W001 | warning | TSDoc comment contains parse errors. |\n * | W002 | warning | Function body throws but has no `@throws` tag. |\n * | W003 | warning | `@deprecated` tag is present without explanation. |\n * | W006 | warning | TSDoc parser-level syntax error (invalid tag, malformed block, etc.). |\n * | E009 | error | tsconfig.json required strict-mode flag is missing or disabled (guard). |\n * | E010 | error | Config drift: a rule severity is weaker than the locked value. |\n * | E013 | error | Exported function/class is missing a `@remarks` block. |\n * | E014 | warn | Optional property of interface/type is missing `@defaultValue`. |\n * | E015 | error | Generic symbol is missing `@typeParam` for a type parameter. |\n * | W005 | warn | Symbol references other symbols via `{@link}` but has no `@see` tags. |\n * | W007 | warn | Guide FORGE:AUTO section references a symbol that no longer exists. |\n * | W008 | warn | Exported public symbol is not mentioned in any guide page. |\n *\n * When `config.enforce.strict` is `true` all warnings are promoted to errors.\n *\n * @param config - The resolved {@link ForgeConfig} for the project.\n * @returns A {@link ForgeResult} describing which symbols passed or failed.\n * @example\n * ```typescript\n * import { loadConfig } from \"@forge-ts/core\";\n * import { enforce } from \"@forge-ts/enforcer\";\n * const config = await loadConfig();\n * const result = await enforce(config);\n * if (!result.success) {\n * console.error(`${result.errors.length} errors found`);\n * }\n * ```\n * @public\n */\nexport async function enforce(config: ForgeConfig): Promise<ForgeResult> {\n\tconst start = Date.now();\n\tconst errors: ForgeError[] = [];\n\tconst warnings: ForgeWarning[] = [];\n\n\tconst walker = createWalker(config);\n\tconst allSymbols = walker.walk();\n\tconst symbols = filterByVisibility(allSymbols, config.enforce.minVisibility);\n\n\t/**\n\t * Emit a diagnostic. The configured per-rule severity determines whether\n\t * the diagnostic is an error or warning; \"off\" suppresses it entirely.\n\t * When `strict` is enabled every warning is promoted to an error.\n\t */\n\tfunction emit(\n\t\tcode: string,\n\t\tmessage: string,\n\t\tfilePath: string,\n\t\tline: number,\n\t\tcolumn: number,\n\t\tguidance?: { suggestedFix?: string; symbolName?: string; symbolKind?: string },\n\t): void {\n\t\tconst ruleKey = RULE_MAP[code];\n\n\t\t// For rule codes tracked in the map, honour the per-rule severity.\n\t\tif (ruleKey !== undefined) {\n\t\t\tconst configuredSeverity = config.enforce.rules[ruleKey];\n\t\t\tif (configuredSeverity === \"off\") return;\n\t\t\tconst effectiveSeverity = config.enforce.strict ? \"error\" : configuredSeverity;\n\t\t\tconst diag = { code, message, filePath, line, column, ...guidance };\n\t\t\tif (effectiveSeverity === \"error\") {\n\t\t\t\terrors.push(diag);\n\t\t\t} else {\n\t\t\t\twarnings.push(diag);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// For codes not in the map (W003, E008, etc.) fall back to the old\n\t\t// behaviour: always emit, respect strict mode for warnings.\n\t\tconst diag = { code, message, filePath, line, column, ...guidance };\n\t\t// Codes starting with \"W\" are warnings by default.\n\t\tif (code.startsWith(\"W\") && !config.enforce.strict) {\n\t\t\twarnings.push(diag);\n\t\t} else {\n\t\t\terrors.push(diag);\n\t\t}\n\t}\n\n\tfor (const symbol of symbols) {\n\t\tif (!symbol.exported) continue;\n\n\t\tconst isFunctionLike = symbol.kind === \"function\" || symbol.kind === \"method\";\n\n\t\t// E001 — Missing summary\n\t\tif (!hasSummary(symbol)) {\n\t\t\temit(\n\t\t\t\t\"E001\",\n\t\t\t\t`Exported symbol \"${symbol.name}\" is missing a TSDoc summary comment.`,\n\t\t\t\tsymbol.filePath,\n\t\t\t\tsymbol.line,\n\t\t\t\tsymbol.column,\n\t\t\t\t{\n\t\t\t\t\tsuggestedFix: `/**\\n * [Description of ${symbol.name}]\\n */`,\n\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// E002 — Undocumented parameters\n\t\tif (isFunctionLike) {\n\t\t\tconst missing = undocumentedParams(symbol);\n\t\t\tfor (const paramName of missing) {\n\t\t\t\temit(\n\t\t\t\t\t\"E002\",\n\t\t\t\t\t`Parameter \"${paramName}\" of \"${symbol.name}\" is not documented with a @param tag.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tsymbol.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: `@param ${paramName} - [Description of ${paramName}]`,\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// E003 — Missing @returns\n\t\tif (isFunctionLike && missingReturns(symbol)) {\n\t\t\temit(\n\t\t\t\t\"E003\",\n\t\t\t\t`\"${symbol.name}\" has a non-void return type but is missing a @returns tag.`,\n\t\t\t\tsymbol.filePath,\n\t\t\t\tsymbol.line,\n\t\t\t\tsymbol.column,\n\t\t\t\t{\n\t\t\t\t\tsuggestedFix: `@returns [Description of the return value]`,\n\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// E004 — Missing @example\n\t\tif (isFunctionLike && symbol.documentation) {\n\t\t\tconst hasExample = (symbol.documentation.examples ?? []).length > 0;\n\t\t\tif (!hasExample) {\n\t\t\t\temit(\n\t\t\t\t\t\"E004\",\n\t\t\t\t\t`Exported function \"${symbol.name}\" is missing an @example block. Add a fenced code block showing usage.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tsymbol.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: `@example\\n * \\`\\`\\`typescript\\n * // Usage of ${symbol.name}\\n * ${symbol.name}();\\n * \\`\\`\\``,\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// E006 — Class member missing documentation\n\t\t// E007 — Interface/type member missing documentation\n\t\tif (symbol.kind === \"class\" || symbol.kind === \"interface\") {\n\t\t\tconst errorCode = symbol.kind === \"class\" ? \"E006\" : \"E007\";\n\t\t\tfor (const child of symbol.children ?? []) {\n\t\t\t\tif (child.kind === \"property\" || child.kind === \"method\") {\n\t\t\t\t\tif (!hasSummary(child)) {\n\t\t\t\t\t\temit(\n\t\t\t\t\t\t\terrorCode,\n\t\t\t\t\t\t\t`Member \"${child.name}\" of ${symbol.kind} \"${symbol.name}\" is missing a TSDoc comment.`,\n\t\t\t\t\t\t\tchild.filePath,\n\t\t\t\t\t\t\tchild.line,\n\t\t\t\t\t\t\tchild.column,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsuggestedFix: `/**\\n * [Description of ${child.name}]\\n */`,\n\t\t\t\t\t\t\t\tsymbolName: child.name,\n\t\t\t\t\t\t\t\tsymbolKind: child.kind,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// E013 — Missing @remarks on exported functions/classes\n\t\tif (symbol.kind === \"function\" || symbol.kind === \"class\") {\n\t\t\tconst hasRemarks = symbol.documentation?.tags?.remarks !== undefined;\n\t\t\tif (!hasRemarks) {\n\t\t\t\temit(\n\t\t\t\t\t\"E013\",\n\t\t\t\t\t`Exported ${symbol.kind} \"${symbol.name}\" is missing a @remarks block.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tsymbol.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: `@remarks [Detailed description of ${symbol.name}]`,\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// E014 — Missing @defaultValue on optional properties of interfaces/types\n\t\tif (symbol.kind === \"interface\" || symbol.kind === \"type\") {\n\t\t\tfor (const child of symbol.children ?? []) {\n\t\t\t\tif (child.kind !== \"property\") continue;\n\t\t\t\tif (!isOptionalProperty(child)) continue;\n\t\t\t\tconst hasDefaultValue = child.documentation?.tags?.defaultValue !== undefined;\n\t\t\t\tif (!hasDefaultValue) {\n\t\t\t\t\temit(\n\t\t\t\t\t\t\"E014\",\n\t\t\t\t\t\t`Optional property \"${child.name}\" of ${symbol.kind} \"${symbol.name}\" is missing @defaultValue.`,\n\t\t\t\t\t\tchild.filePath,\n\t\t\t\t\t\tchild.line,\n\t\t\t\t\t\tchild.column,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsuggestedFix: `@defaultValue [Default value of ${child.name}]`,\n\t\t\t\t\t\t\tsymbolName: child.name,\n\t\t\t\t\t\t\tsymbolKind: child.kind,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// E015 — Missing @typeParam on generic symbols\n\t\tif (symbol.kind === \"function\" || symbol.kind === \"class\" || symbol.kind === \"interface\") {\n\t\t\tconst typeParamNames = extractGenericTypeParams(symbol.signature);\n\t\t\tif (typeParamNames.length > 0) {\n\t\t\t\tconst documentedTypeParams = new Set(\n\t\t\t\t\t(symbol.documentation?.tags?.typeParam ?? []).map((tp) =>\n\t\t\t\t\t\ttp.split(/\\s/)[0].replace(/-$/, \"\").trim(),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tfor (const typeParamName of typeParamNames) {\n\t\t\t\t\tif (!documentedTypeParams.has(typeParamName)) {\n\t\t\t\t\t\temit(\n\t\t\t\t\t\t\t\"E015\",\n\t\t\t\t\t\t\t`Type parameter \"${typeParamName}\" of \"${symbol.name}\" is not documented with @typeParam.`,\n\t\t\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\t\t\tsymbol.line,\n\t\t\t\t\t\t\tsymbol.column,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsuggestedFix: `@typeParam ${typeParamName} - [Description of ${typeParamName}]`,\n\t\t\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// W005 — {@link} references present but no @see tags\n\t\tif (symbol.documentation?.links && symbol.documentation.links.length > 0) {\n\t\t\tconst hasSee =\n\t\t\t\tsymbol.documentation.tags?.see !== undefined && symbol.documentation.tags.see.length > 0;\n\t\t\tif (!hasSee) {\n\t\t\t\temit(\n\t\t\t\t\t\"W005\",\n\t\t\t\t\t`\"${symbol.name}\" references other symbols via {@link} but has no @see tags.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tsymbol.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: \"@see [Related symbol name]\",\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// E016 — Missing release tag on exported symbols\n\t\t{\n\t\t\tconst releaseTags = [\"public\", \"beta\", \"internal\", \"alpha\"];\n\t\t\tconst hasReleaseTag = releaseTags.some(\n\t\t\t\t(tag) => symbol.documentation?.tags?.[tag] !== undefined,\n\t\t\t);\n\t\t\tif (!hasReleaseTag) {\n\t\t\t\temit(\n\t\t\t\t\t\"E016\",\n\t\t\t\t\t`Exported symbol \"${symbol.name}\" is missing a release tag (@public, @beta, or @internal).`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tsymbol.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: \"@public\",\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// W003 — @deprecated without reason\n\t\tif (deprecatedWithoutReason(symbol)) {\n\t\t\temit(\n\t\t\t\t\"W003\",\n\t\t\t\t`\"${symbol.name}\" is marked @deprecated but provides no explanation.`,\n\t\t\t\tsymbol.filePath,\n\t\t\t\tsymbol.line,\n\t\t\t\tsymbol.column,\n\t\t\t\t{\n\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// W006 — TSDoc parser syntax messages\n\t\tif (symbol.documentation?.parseMessages) {\n\t\t\tfor (const msg of symbol.documentation.parseMessages) {\n\t\t\t\temit(\n\t\t\t\t\t\"W006\",\n\t\t\t\t\t`TSDoc syntax: ${msg.text} [${msg.messageId}]`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tmsg.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// W006 — TSDoc parser syntax messages on child symbols\n\t\tif (symbol.children) {\n\t\t\tfor (const child of symbol.children) {\n\t\t\t\tif (child.documentation?.parseMessages) {\n\t\t\t\t\tfor (const msg of child.documentation.parseMessages) {\n\t\t\t\t\t\temit(\n\t\t\t\t\t\t\t\"W006\",\n\t\t\t\t\t\t\t`TSDoc syntax: ${msg.text} [${msg.messageId}]`,\n\t\t\t\t\t\t\tchild.filePath,\n\t\t\t\t\t\t\tmsg.line,\n\t\t\t\t\t\t\tchild.column,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsymbolName: child.name,\n\t\t\t\t\t\t\t\tsymbolKind: child.kind,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// E005 — Missing @packageDocumentation on index.ts entry points\n\t// Group symbols by file to check if any index.ts file lacks @packageDocumentation.\n\tconst indexFiles = new Map<string, ForgeSymbol[]>();\n\tfor (const symbol of allSymbols) {\n\t\tif (symbol.filePath.endsWith(\"index.ts\")) {\n\t\t\tconst bucket = indexFiles.get(symbol.filePath) ?? [];\n\t\t\tbucket.push(symbol);\n\t\t\tindexFiles.set(symbol.filePath, bucket);\n\t\t}\n\t}\n\tfor (const [filePath, fileSymbols] of indexFiles) {\n\t\tconst hasPackageDoc = fileSymbols.some(\n\t\t\t(s) => s.documentation?.tags?.packageDocumentation !== undefined,\n\t\t);\n\t\tif (!hasPackageDoc) {\n\t\t\temit(\n\t\t\t\t\"E005\",\n\t\t\t\t`Package entry point \"${filePath}\" is missing a @packageDocumentation TSDoc comment.`,\n\t\t\t\tfilePath,\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t\t{\n\t\t\t\t\tsuggestedFix: `/**\\n * @packageDocumentation\\n * [Package overview description]\\n */`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t// E008 — Dead {@link} references\n\t// Build a set of all known symbol names (simple and qualified).\n\tconst knownSymbols = new Set<string>();\n\tfor (const s of allSymbols) {\n\t\tknownSymbols.add(s.name);\n\t\tif (s.children) {\n\t\t\tfor (const child of s.children) {\n\t\t\t\tknownSymbols.add(`${s.name}.${child.name}`);\n\t\t\t\tknownSymbols.add(child.name);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check all {@link} references across every symbol (not just filtered ones).\n\tfor (const symbol of allSymbols) {\n\t\tconst docLinks = symbol.documentation?.links ?? [];\n\t\tfor (const link of docLinks) {\n\t\t\tif (!knownSymbols.has(link.target)) {\n\t\t\t\temit(\n\t\t\t\t\t\"E008\",\n\t\t\t\t\t`{@link ${link.target}} in \"${symbol.name}\" references a symbol that does not exist in this project.`,\n\t\t\t\t\tsymbol.filePath,\n\t\t\t\t\tlink.line,\n\t\t\t\t\tsymbol.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsuggestedFix: `Remove or update the {@link ${link.target}} reference to point to an existing symbol.`,\n\t\t\t\t\t\tsymbolName: symbol.name,\n\t\t\t\t\t\tsymbolKind: symbol.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// W004 — Cross-package deprecated symbol usage\n\tconst deprecatedUsages = findDeprecatedUsages(allSymbols);\n\tfor (const usage of deprecatedUsages) {\n\t\temit(\n\t\t\t\"W004\",\n\t\t\t`Import of deprecated symbol \"${usage.deprecatedSymbol}\" from package \"${usage.sourcePackage}\": ${usage.deprecationMessage}`,\n\t\t\tusage.consumingFile,\n\t\t\tusage.line,\n\t\t\t0,\n\t\t\t{\n\t\t\t\tsuggestedFix: `Replace usage of \"${usage.deprecatedSymbol}\" with its recommended replacement.`,\n\t\t\t\tsymbolName: usage.deprecatedSymbol,\n\t\t\t\tsymbolKind: \"variable\",\n\t\t\t},\n\t\t);\n\t}\n\n\t// E009 — tsconfig strictness regression\n\tconst e009Bypassed = isRuleBypassed(config.rootDir, \"E009\");\n\tif (config.guards.tsconfig.enabled) {\n\t\tconst tsconfigPath = join(config.rootDir, \"tsconfig.json\");\n\t\ttry {\n\t\t\tconst raw = readFileSync(tsconfigPath, \"utf-8\");\n\t\t\tlet parsed: { compilerOptions?: Record<string, unknown> } | undefined;\n\t\t\ttry {\n\t\t\t\tparsed = JSON.parse(raw) as { compilerOptions?: Record<string, unknown> };\n\t\t\t} catch (parseErr) {\n\t\t\t\tif (e009Bypassed) {\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\tcode: \"E009\",\n\t\t\t\t\t\tmessage: `[BYPASSED] tsconfig.json: failed to parse — ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`,\n\t\t\t\t\t\tfilePath: tsconfigPath,\n\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\tcode: \"E009\",\n\t\t\t\t\t\tmessage: `tsconfig.json: failed to parse — ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`,\n\t\t\t\t\t\tfilePath: tsconfigPath,\n\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (parsed) {\n\t\t\t\tconst compilerOptions = parsed.compilerOptions ?? {};\n\t\t\t\tconst requiredFlags = config.guards.tsconfig.requiredFlags;\n\t\t\t\tfor (const flag of requiredFlags) {\n\t\t\t\t\tconst value = compilerOptions[flag];\n\t\t\t\t\tif (value !== true) {\n\t\t\t\t\t\tif (e009Bypassed) {\n\t\t\t\t\t\t\twarnings.push({\n\t\t\t\t\t\t\t\tcode: \"E009\",\n\t\t\t\t\t\t\t\tmessage: `[BYPASSED] tsconfig.json: required flag \"${flag}\" is ${value === false ? \"disabled\" : \"missing\"} — expected true`,\n\t\t\t\t\t\t\t\tfilePath: tsconfigPath,\n\t\t\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\terrors.push({\n\t\t\t\t\t\t\t\tcode: \"E009\",\n\t\t\t\t\t\t\t\tmessage: `tsconfig.json: required flag \"${flag}\" is ${value === false ? \"disabled\" : \"missing\"} — expected true`,\n\t\t\t\t\t\t\t\tfilePath: tsconfigPath,\n\t\t\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (readErr) {\n\t\t\t// tsconfig.json not found — skip E009 gracefully\n\t\t\tif (\n\t\t\t\treadErr instanceof Error &&\n\t\t\t\t\"code\" in readErr &&\n\t\t\t\t(readErr as NodeJS.ErrnoException).code === \"ENOENT\"\n\t\t\t) {\n\t\t\t\t// Intentionally ignored: missing tsconfig.json is not an E009 error\n\t\t\t} else {\n\t\t\t\tthrow readErr;\n\t\t\t}\n\t\t}\n\t}\n\n\t// E010 — forge-ts config drift detection via lock file\n\tconst e010Bypassed = isRuleBypassed(config.rootDir, \"E010\");\n\tconst lockManifest = readLockFile(config.rootDir);\n\tif (lockManifest) {\n\t\tconst lockViolations = validateAgainstLock(config, lockManifest);\n\t\tconst lockFilePath = join(config.rootDir, \".forge-lock.json\");\n\t\tfor (const violation of lockViolations) {\n\t\t\tif (e010Bypassed) {\n\t\t\t\twarnings.push({\n\t\t\t\t\tcode: \"E010\",\n\t\t\t\t\tmessage: `[BYPASSED] Config drift: ${violation.message}`,\n\t\t\t\t\tfilePath: lockFilePath,\n\t\t\t\t\tline: 1,\n\t\t\t\t\tcolumn: 0,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\terrors.push({\n\t\t\t\t\tcode: \"E010\",\n\t\t\t\t\tmessage: `Config drift: ${violation.message}`,\n\t\t\t\t\tfilePath: lockFilePath,\n\t\t\t\t\tline: 1,\n\t\t\t\t\tcolumn: 0,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// E011 — Biome config weakening detection\n\tconst e011Bypassed = isRuleBypassed(config.rootDir, \"E011\");\n\tif (config.guards.biome.enabled && lockManifest?.config.biome) {\n\t\tconst biomePath = join(config.rootDir, \"biome.json\");\n\t\tconst biomePathC = join(config.rootDir, \"biome.jsonc\");\n\t\tconst actualBiomePath = existsSync(biomePath)\n\t\t\t? biomePath\n\t\t\t: existsSync(biomePathC)\n\t\t\t\t? biomePathC\n\t\t\t\t: null;\n\t\tif (actualBiomePath) {\n\t\t\ttry {\n\t\t\t\tconst biomeRaw = readFileSync(actualBiomePath, \"utf-8\");\n\t\t\t\tlet biomeParsed: Record<string, unknown>;\n\t\t\t\ttry {\n\t\t\t\t\tbiomeParsed = JSON.parse(biomeRaw) as Record<string, unknown>;\n\t\t\t\t} catch {\n\t\t\t\t\t// Biome file exists but is invalid JSON/JSONC — emit one E011\n\t\t\t\t\tconst diag = {\n\t\t\t\t\t\tcode: \"E011\",\n\t\t\t\t\t\tmessage: `Biome config \"${actualBiomePath}\": failed to parse — file may contain invalid JSON.`,\n\t\t\t\t\t\tfilePath: actualBiomePath,\n\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t};\n\t\t\t\t\tif (e011Bypassed) {\n\t\t\t\t\t\twarnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });\n\t\t\t\t\t} else {\n\t\t\t\t\t\terrors.push(diag);\n\t\t\t\t\t}\n\t\t\t\t\tbiomeParsed = undefined as unknown as Record<string, unknown>;\n\t\t\t\t}\n\t\t\t\tif (biomeParsed) {\n\t\t\t\t\t// Extract current biome rules from linter.rules\n\t\t\t\t\tconst currentBiomeRules = extractBiomeRules(biomeParsed);\n\t\t\t\t\t// Compare against locked biome rules snapshot\n\t\t\t\t\tconst lockedBiomeRules =\n\t\t\t\t\t\t(lockManifest.config.biome as { rules?: Record<string, string> }).rules ?? {};\n\t\t\t\t\tfor (const [ruleName, lockedLevel] of Object.entries(lockedBiomeRules)) {\n\t\t\t\t\t\tconst currentLevel = currentBiomeRules[ruleName] ?? \"off\";\n\t\t\t\t\t\tif (isWeakerBiomeLevel(currentLevel, lockedLevel)) {\n\t\t\t\t\t\t\tconst diag = {\n\t\t\t\t\t\t\t\tcode: \"E011\",\n\t\t\t\t\t\t\t\tmessage: `Biome rule \"${ruleName}\" was weakened from \"${lockedLevel}\" to \"${currentLevel}\".`,\n\t\t\t\t\t\t\t\tfilePath: actualBiomePath,\n\t\t\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (e011Bypassed) {\n\t\t\t\t\t\t\t\twarnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\terrors.push(diag);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (readErr) {\n\t\t\t\t// biome.json not found after existsSync — skip\n\t\t\t\tif (\n\t\t\t\t\treadErr instanceof Error &&\n\t\t\t\t\t\"code\" in readErr &&\n\t\t\t\t\t(readErr as NodeJS.ErrnoException).code === \"ENOENT\"\n\t\t\t\t) {\n\t\t\t\t\t// Intentionally ignored\n\t\t\t\t} else {\n\t\t\t\t\tthrow readErr;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// E012 — package.json engine field tampering\n\tconst e012Bypassed = isRuleBypassed(config.rootDir, \"E012\");\n\tif (config.guards.packageJson.enabled) {\n\t\tconst pkgJsonPath = join(config.rootDir, \"package.json\");\n\t\ttry {\n\t\t\tconst pkgRaw = readFileSync(pkgJsonPath, \"utf-8\");\n\t\t\tconst pkg = JSON.parse(pkgRaw) as Record<string, unknown>;\n\n\t\t\t// Check required fields\n\t\t\tfor (const field of config.guards.packageJson.requiredFields) {\n\t\t\t\tif (pkg[field] === undefined) {\n\t\t\t\t\tconst diag = {\n\t\t\t\t\t\tcode: \"E012\",\n\t\t\t\t\t\tmessage: `package.json: required field \"${field}\" is missing.`,\n\t\t\t\t\t\tfilePath: pkgJsonPath,\n\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t};\n\t\t\t\t\tif (e012Bypassed) {\n\t\t\t\t\t\twarnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });\n\t\t\t\t\t} else {\n\t\t\t\t\t\terrors.push(diag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check engines.node against minNodeVersion\n\t\t\tconst engines = pkg.engines as Record<string, string> | undefined;\n\t\t\tconst nodeEngine = engines?.node;\n\t\t\tif (!nodeEngine) {\n\t\t\t\t// Only emit if \"engines\" is in requiredFields (missing engines already caught above)\n\t\t\t\t// But also check specifically for missing engines.node\n\t\t\t\tif (engines !== undefined) {\n\t\t\t\t\tconst diag = {\n\t\t\t\t\t\tcode: \"E012\",\n\t\t\t\t\t\tmessage: `package.json: \"engines.node\" field is missing.`,\n\t\t\t\t\t\tfilePath: pkgJsonPath,\n\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t};\n\t\t\t\t\tif (e012Bypassed) {\n\t\t\t\t\t\twarnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });\n\t\t\t\t\t} else {\n\t\t\t\t\t\terrors.push(diag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Simple semver comparison: extract major.minor from the engines string\n\t\t\t\tconst minVersion = parseSemverMajorMinor(config.guards.packageJson.minNodeVersion);\n\t\t\t\tconst engineVersion = parseSemverMajorMinor(nodeEngine);\n\t\t\t\tif (minVersion && engineVersion && compareMajorMinor(engineVersion, minVersion) < 0) {\n\t\t\t\t\tconst diag = {\n\t\t\t\t\t\tcode: \"E012\",\n\t\t\t\t\t\tmessage: `package.json: \"engines.node\" specifies \"${nodeEngine}\" which is lower than the minimum required \"${config.guards.packageJson.minNodeVersion}\".`,\n\t\t\t\t\t\tfilePath: pkgJsonPath,\n\t\t\t\t\t\tline: 1,\n\t\t\t\t\t\tcolumn: 0,\n\t\t\t\t\t};\n\t\t\t\t\tif (e012Bypassed) {\n\t\t\t\t\t\twarnings.push({ ...diag, message: `[BYPASSED] ${diag.message}` });\n\t\t\t\t\t} else {\n\t\t\t\t\t\terrors.push(diag);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (readErr) {\n\t\t\t// package.json not found — skip\n\t\t\tif (\n\t\t\t\treadErr instanceof Error &&\n\t\t\t\t\"code\" in readErr &&\n\t\t\t\t(readErr as NodeJS.ErrnoException).code === \"ENOENT\"\n\t\t\t) {\n\t\t\t\t// Intentionally ignored\n\t\t\t} else if (readErr instanceof SyntaxError) {\n\t\t\t\t// Invalid JSON — skip gracefully\n\t\t\t} else {\n\t\t\t\tthrow readErr;\n\t\t\t}\n\t\t}\n\t}\n\n\t// W007 — Stale guide FORGE:AUTO sections (references to removed/renamed symbols)\n\t// W008 — Undocumented public symbol in guides (exported but not mentioned)\n\tconst guidesDir = join(config.outDir, \"guides\");\n\tlet guideFiles: string[] = [];\n\tlet guideContents: Map<string, string> | undefined;\n\n\t// Only attempt guide checks if the guides directory exists\n\tif (existsSync(guidesDir)) {\n\t\ttry {\n\t\t\tconst entries = readdirSync(guidesDir);\n\t\t\tguideFiles = entries.filter((f) => f.endsWith(\".md\") || f.endsWith(\".mdx\"));\n\t\t} catch {\n\t\t\t// If we cannot read the directory, skip guide rules\n\t\t}\n\t}\n\n\tif (guideFiles.length > 0) {\n\t\tguideContents = new Map<string, string>();\n\t\tfor (const file of guideFiles) {\n\t\t\ttry {\n\t\t\t\tconst content = readFileSync(join(guidesDir, file), \"utf-8\");\n\t\t\t\tguideContents.set(file, content);\n\t\t\t} catch {\n\t\t\t\t// Skip unreadable files\n\t\t\t}\n\t\t}\n\n\t\t// W007 — Check FORGE:AUTO sections for references to symbols that no longer exist\n\t\tconst autoStartRe = /<!--\\s*FORGE:AUTO-START\\s+(\\S+)\\s*-->/g;\n\t\tconst autoEndRe = /<!--\\s*FORGE:AUTO-END\\s+(\\S+)\\s*-->/;\n\t\t// Match symbol references inside auto sections: `symbolName` in backticks or bare names\n\t\t// after typical markdown patterns (links, bold, code).\n\t\t// Simplified heuristic: extract all backtick-quoted identifiers inside FORGE:AUTO blocks.\n\t\tconst symbolRefRe = /`([A-Za-z_$][A-Za-z0-9_$]*)`/g;\n\n\t\tfor (const [file, content] of guideContents) {\n\t\t\tconst filePath = join(guidesDir, file);\n\t\t\t// Find each FORGE:AUTO block\n\t\t\tautoStartRe.lastIndex = 0;\n\t\t\tfor (\n\t\t\t\tlet match = autoStartRe.exec(content);\n\t\t\t\tmatch !== null;\n\t\t\t\tmatch = autoStartRe.exec(content)\n\t\t\t) {\n\t\t\t\tconst startIdx = match.index + match[0].length;\n\t\t\t\t// Find the matching end marker\n\t\t\t\tconst restContent = content.slice(startIdx);\n\t\t\t\tconst endMatch = autoEndRe.exec(restContent);\n\t\t\t\tif (!endMatch) continue;\n\t\t\t\tconst autoBlock = restContent.slice(0, endMatch.index);\n\n\t\t\t\t// Extract all backtick-quoted identifiers from the auto block\n\t\t\t\tsymbolRefRe.lastIndex = 0;\n\t\t\t\tfor (\n\t\t\t\t\tlet refMatch = symbolRefRe.exec(autoBlock);\n\t\t\t\t\trefMatch !== null;\n\t\t\t\t\trefMatch = symbolRefRe.exec(autoBlock)\n\t\t\t\t) {\n\t\t\t\t\tconst refName = refMatch[1];\n\t\t\t\t\t// Check if this name exists in the known symbols set\n\t\t\t\t\tif (!knownSymbols.has(refName)) {\n\t\t\t\t\t\temit(\n\t\t\t\t\t\t\t\"W007\",\n\t\t\t\t\t\t\t`Guide \"${file}\" FORGE:AUTO section references symbol \"${refName}\" which no longer exists in the symbol graph.`,\n\t\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\t\t1,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsymbolName: refName,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// W008 — Check that all exported symbols from index.ts are mentioned in at least one guide\n\t\tconst allGuideText = [...guideContents.values()].join(\"\\n\");\n\t\tconst indexExportedSymbols = allSymbols.filter(\n\t\t\t(s) => s.exported && /[/\\\\]index\\.ts$/.test(s.filePath),\n\t\t);\n\t\tfor (const sym of indexExportedSymbols) {\n\t\t\tif (!allGuideText.includes(sym.name)) {\n\t\t\t\temit(\n\t\t\t\t\t\"W008\",\n\t\t\t\t\t`Exported symbol \"${sym.name}\" from \"${sym.filePath}\" is not mentioned in any guide page.`,\n\t\t\t\t\tsym.filePath,\n\t\t\t\t\tsym.line,\n\t\t\t\t\tsym.column,\n\t\t\t\t\t{\n\t\t\t\t\t\tsymbolName: sym.name,\n\t\t\t\t\t\tsymbolKind: sym.kind,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst success = errors.length === 0;\n\treturn { success, symbols: allSymbols, errors, warnings, duration: Date.now() - start };\n}\n","import type { ForgeResult } from \"@forge-ts/core\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Options that control how {@link formatResults} renders its output.\n * @public\n */\nexport interface FormatOptions {\n\t/** Emit ANSI colour escape sequences when `true`. */\n\tcolors: boolean;\n\t/**\n\t * When `true`, include the symbol's type signature alongside each\n\t * diagnostic so the reader has immediate context.\n\t */\n\tverbose: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// ANSI helpers\n// ---------------------------------------------------------------------------\n\n/** @internal */\nconst RESET = \"\\x1b[0m\";\n/** @internal */\nconst RED = \"\\x1b[31m\";\n/** @internal */\nconst YELLOW = \"\\x1b[33m\";\n/** @internal */\nconst BOLD = \"\\x1b[1m\";\n/** @internal */\nconst DIM = \"\\x1b[2m\";\n\n/** @internal */\nfunction colorize(text: string, color: string, useColors: boolean): string {\n\treturn useColors ? `${color}${text}${RESET}` : text;\n}\n\n/** @internal */\nfunction bold(text: string, useColors: boolean): string {\n\treturn useColors ? `${BOLD}${text}${RESET}` : text;\n}\n\n/** @internal */\nfunction dim(text: string, useColors: boolean): string {\n\treturn useColors ? `${DIM}${text}${RESET}` : text;\n}\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\n/** @internal */\ninterface Diagnostic {\n\tcode: string;\n\tmessage: string;\n\tfilePath: string;\n\tline: number;\n\tcolumn: number;\n}\n\n/** @internal */\nfunction isError(code: string): boolean {\n\treturn code.startsWith(\"E\");\n}\n\n/** @internal */\nfunction renderDiagnostic(diag: Diagnostic, opts: FormatOptions): string {\n\tconst label = isError(diag.code)\n\t\t? colorize(`error[${diag.code}]`, RED, opts.colors)\n\t\t: colorize(`warning[${diag.code}]`, YELLOW, opts.colors);\n\n\tconst location = dim(`${diag.line}:${diag.column}`, opts.colors);\n\treturn ` ${label} ${diag.message} ${location}`;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Formats a {@link ForgeResult} into a human-readable string suitable for\n * printing to a terminal.\n *\n * Diagnostics are grouped by source file. Each file heading shows the\n * relative-ish path, followed by indented error and warning lines. A summary\n * line is appended at the end.\n *\n * @param result - The result produced by {@link enforce}.\n * @param options - Rendering options (colours, verbosity).\n * @returns A formatted string ready to write to stdout or stderr.\n * @example\n * ```typescript\n * import { enforce } from \"@forge-ts/enforcer\";\n * import { formatResults } from \"@forge-ts/enforcer\";\n * import { loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const result = await enforce(config);\n * console.log(formatResults(result, { colors: true, verbose: false }));\n * ```\n * @public\n */\nexport function formatResults(result: ForgeResult, options: FormatOptions): string {\n\tconst allDiags: Diagnostic[] = [\n\t\t...result.errors.map((e) => ({ ...e })),\n\t\t...result.warnings.map((w) => ({ ...w })),\n\t];\n\n\tif (allDiags.length === 0) {\n\t\tconst msg = `No issues found across ${result.symbols.length} symbol(s).`;\n\t\treturn bold(msg, options.colors);\n\t}\n\n\t// Group by filePath\n\tconst byFile = new Map<string, Diagnostic[]>();\n\tfor (const diag of allDiags) {\n\t\tconst list = byFile.get(diag.filePath);\n\t\tif (list) {\n\t\t\tlist.push(diag);\n\t\t} else {\n\t\t\tbyFile.set(diag.filePath, [diag]);\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\n\tfor (const [filePath, diags] of byFile) {\n\t\tlines.push(bold(filePath, options.colors));\n\n\t\t// Sort: errors before warnings, then by line\n\t\tconst sorted = [...diags].sort((a, b) => {\n\t\t\tconst aIsErr = isError(a.code) ? 0 : 1;\n\t\t\tconst bIsErr = isError(b.code) ? 0 : 1;\n\t\t\tif (aIsErr !== bIsErr) return aIsErr - bIsErr;\n\t\t\treturn a.line - b.line;\n\t\t});\n\n\t\tfor (const diag of sorted) {\n\t\t\tlines.push(renderDiagnostic(diag, options));\n\n\t\t\tif (options.verbose) {\n\t\t\t\t// Find the matching symbol to show its signature\n\t\t\t\tconst sym = result.symbols.find(\n\t\t\t\t\t(s) => s.filePath === diag.filePath && s.line === diag.line,\n\t\t\t\t);\n\t\t\t\tif (sym?.signature) {\n\t\t\t\t\tlines.push(dim(` signature: ${sym.signature}`, options.colors));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlines.push(\"\");\n\t}\n\n\t// Summary line\n\tconst errorCount = result.errors.length;\n\tconst warnCount = result.warnings.length;\n\tconst fileCount = byFile.size;\n\n\tconst errorPart =\n\t\terrorCount > 0\n\t\t\t? colorize(`${errorCount} error${errorCount !== 1 ? \"s\" : \"\"}`, RED, options.colors)\n\t\t\t: `0 errors`;\n\tconst warnPart =\n\t\twarnCount > 0\n\t\t\t? colorize(`${warnCount} warning${warnCount !== 1 ? \"s\" : \"\"}`, YELLOW, options.colors)\n\t\t\t: `0 warnings`;\n\tconst filePart = `${fileCount} file${fileCount !== 1 ? \"s\" : \"\"}`;\n\n\tlines.push(`${errorPart}, ${warnPart} in ${filePart}`);\n\n\treturn lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["diag"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge-ts/enforcer",
3
- "version": "0.7.2",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "description": "TSDoc enforcement linter for forge-ts",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@forge-ts/core": "0.7.2"
27
+ "@forge-ts/core": "0.13.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "tsup": "^8.3.5",