@forge-ts/core 0.18.0 → 0.19.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 +15 -0
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/tsdoc-preset/tsdoc.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -303,6 +303,7 @@ interface ForgeSymbol {
|
|
|
303
303
|
links?: Array<{
|
|
304
304
|
target: string;
|
|
305
305
|
line: number;
|
|
306
|
+
text?: string;
|
|
306
307
|
}>;
|
|
307
308
|
/** TSDoc parser messages (syntax warnings/errors) from @microsoft/tsdoc. */
|
|
308
309
|
parseMessages?: Array<{
|
|
@@ -372,6 +373,14 @@ interface EnforceRules {
|
|
|
372
373
|
"require-migration-path": RuleSeverity;
|
|
373
374
|
/** W011: New public export missing @since version tag. */
|
|
374
375
|
"require-since": RuleSeverity;
|
|
376
|
+
/** W013: @example block may be stale — function call arg count mismatches parameter count. */
|
|
377
|
+
"require-fresh-examples": RuleSeverity;
|
|
378
|
+
/** E019: Non-test file contains ts-ignore or ts-expect-error directive. */
|
|
379
|
+
"require-no-ts-ignore": RuleSeverity;
|
|
380
|
+
/** E020: Exported symbol has `any` in its public API signature. */
|
|
381
|
+
"require-no-any-in-api": RuleSeverity;
|
|
382
|
+
/** W012: {@link} display text appears stale relative to target summary. */
|
|
383
|
+
"require-fresh-link-text": RuleSeverity;
|
|
375
384
|
}
|
|
376
385
|
/**
|
|
377
386
|
* Full configuration for a forge-ts run.
|
|
@@ -400,6 +409,12 @@ interface ForgeConfig {
|
|
|
400
409
|
strict: boolean;
|
|
401
410
|
/** Per-rule severity overrides. When strict is true, all "warn" become "error". */
|
|
402
411
|
rules: EnforceRules;
|
|
412
|
+
/**
|
|
413
|
+
* Path to a file listing symbol names to skip enforcement on (one per line).
|
|
414
|
+
* Lines starting with `#` are treated as comments.
|
|
415
|
+
* Typically generated by Knip: `npx knip --reporter json | jq -r '.exports[].name' > .forge-ignore`
|
|
416
|
+
*/
|
|
417
|
+
ignoreFile?: string;
|
|
403
418
|
};
|
|
404
419
|
/** DocTest configuration. */
|
|
405
420
|
doctest: {
|
package/dist/index.js
CHANGED
|
@@ -207,7 +207,11 @@ function defaultConfig(rootDir) {
|
|
|
207
207
|
"require-route-response": "warn",
|
|
208
208
|
"require-inheritdoc-source": "warn",
|
|
209
209
|
"require-migration-path": "warn",
|
|
210
|
-
"require-since": "warn"
|
|
210
|
+
"require-since": "warn",
|
|
211
|
+
"require-fresh-examples": "warn",
|
|
212
|
+
"require-no-ts-ignore": "error",
|
|
213
|
+
"require-no-any-in-api": "warn",
|
|
214
|
+
"require-fresh-link-text": "warn"
|
|
211
215
|
}
|
|
212
216
|
},
|
|
213
217
|
doctest: {
|
|
@@ -297,7 +301,11 @@ var KNOWN_RULE_KEYS = /* @__PURE__ */ new Set([
|
|
|
297
301
|
"require-route-response",
|
|
298
302
|
"require-inheritdoc-source",
|
|
299
303
|
"require-migration-path",
|
|
300
|
-
"require-since"
|
|
304
|
+
"require-since",
|
|
305
|
+
"require-fresh-examples",
|
|
306
|
+
"require-no-ts-ignore",
|
|
307
|
+
"require-no-any-in-api",
|
|
308
|
+
"require-fresh-link-text"
|
|
301
309
|
]);
|
|
302
310
|
var KNOWN_TSDOC_KEYS = /* @__PURE__ */ new Set(["writeConfig", "customTags", "enforce"]);
|
|
303
311
|
var KNOWN_TSDOC_ENFORCE_KEYS = /* @__PURE__ */ new Set(["core", "extended", "discretionary"]);
|
|
@@ -977,7 +985,8 @@ function parseTSDoc(rawComment, startLine, folderPath) {
|
|
|
977
985
|
if (linkTag.codeDestination) {
|
|
978
986
|
const target = linkTag.codeDestination.memberReferences.map((ref) => ref.memberIdentifier?.identifier ?? "").filter(Boolean).join(".");
|
|
979
987
|
if (target) {
|
|
980
|
-
|
|
988
|
+
const text = linkTag.linkText?.trim() || void 0;
|
|
989
|
+
links.push({ target, line: startLine, text });
|
|
981
990
|
}
|
|
982
991
|
}
|
|
983
992
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/audit.ts","../src/bypass.ts","../src/config.ts","../src/types.ts","../src/lock.ts","../src/visibility.ts","../src/walker.ts"],"sourcesContent":["/**\n * Append-only audit trail for forge-ts configuration and governance events.\n *\n * Events are stored as JSON Lines in `.forge-audit.jsonl` at the project root.\n * Each line is a single JSON object — the file is never truncated or overwritten.\n *\n * @packageDocumentation\n * @public\n */\n\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport { join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Discriminated event types recorded in the audit trail.\n *\n * @public\n */\nexport type AuditEventType =\n\t| \"config.lock\"\n\t| \"config.unlock\"\n\t| \"config.drift\"\n\t| \"bypass.create\"\n\t| \"bypass.expire\"\n\t| \"rule.change\";\n\n/**\n * A single audit event recorded in the forge-ts audit trail.\n *\n * @public\n */\nexport interface AuditEvent {\n\t/** ISO 8601 timestamp of when the event occurred. */\n\ttimestamp: string;\n\t/** Discriminated event type. */\n\tevent: AuditEventType;\n\t/** OS username of the actor (falls back to \"unknown\"). */\n\tuser: string;\n\t/** Mandatory for lock/unlock/bypass events; optional otherwise. */\n\treason?: string;\n\t/** Event-specific payload. */\n\tdetails: Record<string, unknown>;\n}\n\n/** Default filename for the audit trail. */\nconst AUDIT_FILENAME = \".forge-audit.jsonl\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current OS username, or \"unknown\" if unavailable.\n *\n * @returns The OS username string.\n * @example\n * ```typescript\n * import { getCurrentUser } from \"@forge-ts/core/audit\";\n * const user = getCurrentUser(); // e.g. \"alice\"\n * ```\n * @internal\n */\nexport function getCurrentUser(): string {\n\ttry {\n\t\treturn userInfo().username;\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Appends a single audit event to the `.forge-audit.jsonl` file.\n *\n * Creates the file if it does not exist. The file is strictly append-only —\n * existing content is never modified or truncated.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param event - The audit event to record.\n * @example\n * ```typescript\n * import { appendAuditEvent } from \"@forge-ts/core\";\n * appendAuditEvent(\"/path/to/project\", {\n * timestamp: new Date().toISOString(),\n * event: \"config.lock\",\n * user: \"alice\",\n * reason: \"Stabilize v2 config\",\n * details: { hash: \"abc123\" },\n * });\n * ```\n * @public\n */\nexport function appendAuditEvent(rootDir: string, event: AuditEvent): void {\n\tconst filePath = join(rootDir, AUDIT_FILENAME);\n\tappendFileSync(filePath, `${JSON.stringify(event)}\\n`, \"utf-8\");\n}\n\n/**\n * Options for reading the audit log.\n *\n * @public\n */\nexport interface ReadAuditOptions {\n\t/** Maximum number of events to return. */\n\tlimit?: number;\n\t/** Filter to a single event type. */\n\teventType?: AuditEventType;\n}\n\n/**\n * Reads the `.forge-audit.jsonl` file and returns parsed audit events.\n *\n * Returns newest events first. If the file does not exist, returns an empty\n * array.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param options - Optional limit and event type filter.\n * @returns Array of audit events, newest first.\n * @example\n * ```typescript\n * import { readAuditLog } from \"@forge-ts/core\";\n * const events = readAuditLog(\"/path/to/project\", { limit: 10 });\n * console.log(events.length); // up to 10\n * ```\n * @public\n */\nexport function readAuditLog(rootDir: string, options?: ReadAuditOptions): AuditEvent[] {\n\tconst filePath = join(rootDir, AUDIT_FILENAME);\n\tif (!existsSync(filePath)) {\n\t\treturn [];\n\t}\n\n\tconst raw = readFileSync(filePath, \"utf-8\");\n\tconst lines = raw.split(\"\\n\").filter((line) => line.trim().length > 0);\n\n\tlet events: AuditEvent[] = lines.map((line) => JSON.parse(line) as AuditEvent);\n\n\t// Filter by event type if specified\n\tif (options?.eventType) {\n\t\tevents = events.filter((e) => e.event === options.eventType);\n\t}\n\n\t// Newest first\n\tevents.reverse();\n\n\t// Apply limit\n\tif (options?.limit !== undefined && options.limit >= 0) {\n\t\tevents = events.slice(0, options.limit);\n\t}\n\n\treturn events;\n}\n\n/**\n * Formats a single audit event as a human-readable string.\n *\n * @param event - The audit event to format.\n * @returns A single-line human-readable representation.\n * @example\n * ```typescript\n * import { formatAuditEvent } from \"@forge-ts/core\";\n * const line = formatAuditEvent({\n * timestamp: \"2026-03-21T12:00:00.000Z\",\n * event: \"config.lock\",\n * user: \"alice\",\n * reason: \"Stabilize v2 config\",\n * details: { hash: \"abc123\" },\n * });\n * console.log(line);\n * // \"[2026-03-21T12:00:00.000Z] config.lock by alice — Stabilize v2 config {hash: abc123}\"\n * ```\n * @public\n */\nexport function formatAuditEvent(event: AuditEvent): string {\n\tconst reasonPart = event.reason ? ` — ${event.reason}` : \"\";\n\tconst detailKeys = Object.keys(event.details);\n\tconst detailPart = detailKeys.length > 0 ? ` ${JSON.stringify(event.details)}` : \"\";\n\n\treturn `[${event.timestamp}] ${event.event} by ${event.user}${reasonPart}${detailPart}`;\n}\n","/**\n * Bypass budget system for forge-ts config governance.\n *\n * Allows agents to temporarily bypass locked rules with a limited daily budget.\n * Each bypass requires a mandatory justification and expires after a configurable\n * duration. All bypass events are recorded in the audit trail.\n *\n * Bypass records are stored in `.forge-bypass.json` at the project root as a\n * JSON array of {@link BypassRecord} objects.\n *\n * @packageDocumentation\n * @public\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport { join } from \"node:path\";\nimport { appendAuditEvent } from \"./audit.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for the bypass budget system.\n *\n * @public\n */\nexport interface BypassConfig {\n\t/** Maximum number of bypasses allowed per calendar day. Default: 3 */\n\tdailyBudget: number;\n\t/** Duration in hours before a bypass automatically expires. Default: 24 */\n\tdurationHours: number;\n}\n\n/**\n * A single bypass record stored in `.forge-bypass.json`.\n *\n * @public\n */\nexport interface BypassRecord {\n\t/** Unique identifier for this bypass. */\n\tid: string;\n\t/** ISO 8601 timestamp when the bypass was created. */\n\tcreatedAt: string;\n\t/** ISO 8601 timestamp when the bypass expires. */\n\texpiresAt: string;\n\t/** Mandatory justification for why the bypass was created. */\n\treason: string;\n\t/** Specific rule code bypassed (e.g., \"E009\"), or \"all\" for a blanket bypass. */\n\trule: string;\n\t/** OS username of the actor who created the bypass. */\n\tuser: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default filename for the bypass records. */\nconst BYPASS_FILENAME = \".forge-bypass.json\";\n\n/** Default bypass configuration values. */\nconst DEFAULT_BYPASS_CONFIG: BypassConfig = {\n\tdailyBudget: 3,\n\tdurationHours: 24,\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current OS username, or \"unknown\" if unavailable.\n *\n * @returns The OS username string.\n * @internal\n */\nfunction getCurrentUser(): string {\n\ttry {\n\t\treturn userInfo().username;\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n}\n\n/**\n * Reads the raw bypass records from `.forge-bypass.json`.\n * Returns an empty array if the file does not exist or contains invalid JSON.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns Array of all bypass records (including expired).\n * @internal\n */\nfunction readBypassFile(rootDir: string): BypassRecord[] {\n\tconst filePath = join(rootDir, BYPASS_FILENAME);\n\tif (!existsSync(filePath)) {\n\t\treturn [];\n\t}\n\ttry {\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\treturn JSON.parse(raw) as BypassRecord[];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Writes bypass records to `.forge-bypass.json`.\n *\n * @param rootDir - Absolute path to the project root.\n * @param records - Array of bypass records to write.\n * @internal\n */\nfunction writeBypassFile(rootDir: string, records: BypassRecord[]): void {\n\tconst filePath = join(rootDir, BYPASS_FILENAME);\n\twriteFileSync(filePath, `${JSON.stringify(records, null, 2)}\\n`, \"utf-8\");\n}\n\n/**\n * Merges user-provided bypass config with defaults.\n *\n * @param config - Partial bypass configuration.\n * @returns Fully-resolved bypass configuration.\n * @internal\n */\nfunction resolveConfig(config?: Partial<BypassConfig>): BypassConfig {\n\treturn {\n\t\t...DEFAULT_BYPASS_CONFIG,\n\t\t...config,\n\t};\n}\n\n/**\n * Returns the start of the current UTC day as a Date object.\n *\n * @returns Date representing midnight UTC of the current day.\n * @internal\n */\nfunction startOfToday(): Date {\n\tconst now = new Date();\n\treturn new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a new bypass record, writes it to `.forge-bypass.json`, and appends\n * an audit event.\n *\n * Throws an error if the daily budget is exhausted.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param reason - Mandatory justification for the bypass.\n * @param rule - Specific rule code to bypass (e.g., \"E009\"), or \"all\". Defaults to \"all\".\n * @param config - Optional bypass budget configuration overrides.\n * @returns The created bypass record.\n * @throws Error when the daily bypass budget is exhausted.\n * @example\n * ```typescript\n * import { createBypass } from \"@forge-ts/core\";\n * const bypass = createBypass(\"/path/to/project\", \"hotfix for release\", \"E009\");\n * console.log(bypass.id); // unique bypass ID\n * ```\n * @public\n */\nexport function createBypass(\n\trootDir: string,\n\treason: string,\n\trule?: string,\n\tconfig?: Partial<BypassConfig>,\n): BypassRecord {\n\tconst resolved = resolveConfig(config);\n\tconst remaining = getRemainingBudget(rootDir, config);\n\n\tif (remaining <= 0) {\n\t\tthrow new Error(\n\t\t\t`Bypass budget exhausted: ${resolved.dailyBudget}/${resolved.dailyBudget} bypasses used today. ` +\n\t\t\t\t\"Wait until tomorrow or increase bypass.dailyBudget in your forge-ts config.\",\n\t\t);\n\t}\n\n\tconst now = new Date();\n\tconst expiresAt = new Date(now.getTime() + resolved.durationHours * 60 * 60 * 1000);\n\n\tconst record: BypassRecord = {\n\t\tid: randomUUID(),\n\t\tcreatedAt: now.toISOString(),\n\t\texpiresAt: expiresAt.toISOString(),\n\t\treason,\n\t\trule: rule ?? \"all\",\n\t\tuser: getCurrentUser(),\n\t};\n\n\t// Read existing records and append the new one\n\tconst records = readBypassFile(rootDir);\n\trecords.push(record);\n\twriteBypassFile(rootDir, records);\n\n\t// Append audit event\n\tappendAuditEvent(rootDir, {\n\t\ttimestamp: record.createdAt,\n\t\tevent: \"bypass.create\",\n\t\tuser: record.user,\n\t\treason,\n\t\tdetails: {\n\t\t\tbypassId: record.id,\n\t\t\trule: record.rule,\n\t\t\texpiresAt: record.expiresAt,\n\t\t\tdurationHours: resolved.durationHours,\n\t\t},\n\t});\n\n\treturn record;\n}\n\n/**\n * Returns all currently active (non-expired) bypass records.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @returns Array of active bypass records.\n * @example\n * ```typescript\n * import { getActiveBypasses } from \"@forge-ts/core\";\n * const active = getActiveBypasses(\"/path/to/project\");\n * console.log(`${active.length} active bypass(es)`);\n * ```\n * @public\n */\nexport function getActiveBypasses(rootDir: string): BypassRecord[] {\n\tconst records = readBypassFile(rootDir);\n\tconst now = new Date();\n\treturn records.filter((r) => new Date(r.expiresAt) > now);\n}\n\n/**\n * Checks whether a specific rule has an active bypass.\n *\n * A rule is considered bypassed if there is an active bypass with the exact\n * rule code or an \"all\" bypass.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param ruleCode - The rule code to check (e.g., \"E009\", \"E010\").\n * @returns `true` if the rule is currently bypassed.\n * @example\n * ```typescript\n * import { isRuleBypassed } from \"@forge-ts/core\";\n * if (isRuleBypassed(\"/path/to/project\", \"E009\")) {\n * console.log(\"E009 is currently bypassed\");\n * }\n * ```\n * @public\n */\nexport function isRuleBypassed(rootDir: string, ruleCode: string): boolean {\n\tconst active = getActiveBypasses(rootDir);\n\treturn active.some((r) => r.rule === ruleCode || r.rule === \"all\");\n}\n\n/**\n * Returns the number of bypass budget slots remaining for today.\n *\n * Counts bypasses created today (UTC) against the configured daily budget.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param config - Optional bypass budget configuration overrides.\n * @returns Number of remaining bypass slots for today.\n * @example\n * ```typescript\n * import { getRemainingBudget } from \"@forge-ts/core\";\n * const remaining = getRemainingBudget(\"/path/to/project\");\n * console.log(`${remaining} bypass(es) remaining today`);\n * ```\n * @public\n */\nexport function getRemainingBudget(rootDir: string, config?: Partial<BypassConfig>): number {\n\tconst resolved = resolveConfig(config);\n\tconst records = readBypassFile(rootDir);\n\tconst todayStart = startOfToday();\n\n\tconst todayCount = records.filter((r) => new Date(r.createdAt) >= todayStart).length;\n\n\treturn Math.max(0, resolved.dailyBudget - todayCount);\n}\n\n/**\n * Removes expired bypass records from `.forge-bypass.json`.\n *\n * Also appends a `bypass.expire` audit event for each expired record removed.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @returns The number of expired records removed.\n * @example\n * ```typescript\n * import { expireOldBypasses } from \"@forge-ts/core\";\n * const removed = expireOldBypasses(\"/path/to/project\");\n * console.log(`${removed} expired bypass(es) removed`);\n * ```\n * @public\n */\nexport function expireOldBypasses(rootDir: string): number {\n\tconst records = readBypassFile(rootDir);\n\tconst now = new Date();\n\n\tconst active = records.filter((r) => new Date(r.expiresAt) > now);\n\tconst expired = records.filter((r) => new Date(r.expiresAt) <= now);\n\n\tif (expired.length === 0) {\n\t\treturn 0;\n\t}\n\n\t// Write back only active records\n\twriteBypassFile(rootDir, active);\n\n\t// Append audit events for each expired bypass\n\tfor (const record of expired) {\n\t\tappendAuditEvent(rootDir, {\n\t\t\ttimestamp: now.toISOString(),\n\t\t\tevent: \"bypass.expire\",\n\t\t\tuser: record.user,\n\t\t\tdetails: {\n\t\t\t\tbypassId: record.id,\n\t\t\t\trule: record.rule,\n\t\t\t\tcreatedAt: record.createdAt,\n\t\t\t\texpiresAt: record.expiresAt,\n\t\t\t},\n\t\t});\n\t}\n\n\treturn expired.length;\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { type ForgeConfig, Visibility } from \"./types.js\";\n\n/**\n * Type-safe helper for defining a partial forge-ts configuration.\n *\n * Only include the settings you want to override — everything else\n * inherits sensible defaults via `loadConfig()`.\n *\n * @param config - Partial configuration overrides.\n * @returns The same object (identity function for type checking).\n * @example\n * ```typescript\n * import { defineConfig } from \"@forge-ts/core\";\n *\n * export default defineConfig({\n * outDir: \"docs\",\n * enforce: { strict: true },\n * gen: { ssgTarget: \"mintlify\" },\n * });\n * ```\n * @public\n */\nexport function defineConfig(config: Partial<ForgeConfig>): Partial<ForgeConfig> {\n\treturn config;\n}\n\n/**\n * Constructs a sensible default {@link ForgeConfig} rooted at `rootDir`.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns A fully-populated default configuration.\n * @example\n * ```typescript\n * import { defaultConfig } from \"@forge-ts/core\";\n * const config = defaultConfig(\"/path/to/project\");\n * console.log(config.enforce.enabled); // true\n * ```\n * @public\n */\nexport function defaultConfig(rootDir: string): ForgeConfig {\n\treturn {\n\t\trootDir,\n\t\ttsconfig: join(rootDir, \"tsconfig.json\"),\n\t\toutDir: join(rootDir, \"docs\"),\n\t\tenforce: {\n\t\t\tenabled: true,\n\t\t\tminVisibility: Visibility.Public,\n\t\t\tstrict: false,\n\t\t\trules: {\n\t\t\t\t\"require-summary\": \"error\",\n\t\t\t\t\"require-param\": \"error\",\n\t\t\t\t\"require-returns\": \"error\",\n\t\t\t\t\"require-example\": \"error\",\n\t\t\t\t\"require-package-doc\": \"warn\",\n\t\t\t\t\"require-class-member-doc\": \"error\",\n\t\t\t\t\"require-interface-member-doc\": \"error\",\n\t\t\t\t\"require-tsdoc-syntax\": \"warn\",\n\t\t\t\t\"require-remarks\": \"error\",\n\t\t\t\t\"require-default-value\": \"warn\",\n\t\t\t\t\"require-type-param\": \"error\",\n\t\t\t\t\"require-see\": \"warn\",\n\t\t\t\t\"require-release-tag\": \"error\",\n\t\t\t\t\"require-fresh-guides\": \"warn\",\n\t\t\t\t\"require-guide-coverage\": \"warn\",\n\t\t\t\t\"require-internal-boundary\": \"error\",\n\t\t\t\t\"require-route-response\": \"warn\",\n\t\t\t\t\"require-inheritdoc-source\": \"warn\",\n\t\t\t\t\"require-migration-path\": \"warn\",\n\t\t\t\t\"require-since\": \"warn\",\n\t\t\t},\n\t\t},\n\t\tdoctest: {\n\t\t\tenabled: true,\n\t\t\tcacheDir: join(rootDir, \".cache\", \"doctest\"),\n\t\t},\n\t\tapi: {\n\t\t\tenabled: false,\n\t\t\topenapi: false,\n\t\t\topenapiPath: join(rootDir, \"docs\", \"openapi.json\"),\n\t\t},\n\t\tgen: {\n\t\t\tenabled: true,\n\t\t\tformats: [\"markdown\"],\n\t\t\tllmsTxt: true,\n\t\t\treadmeSync: false,\n\t\t},\n\t\tskill: {},\n\t\tbypass: {\n\t\t\tdailyBudget: 3,\n\t\t\tdurationHours: 24,\n\t\t},\n\t\ttsdoc: {\n\t\t\twriteConfig: true,\n\t\t\tcustomTags: [],\n\t\t\tenforce: {\n\t\t\t\tcore: \"error\",\n\t\t\t\textended: \"warn\",\n\t\t\t\tdiscretionary: \"off\",\n\t\t\t},\n\t\t},\n\t\tguides: {\n\t\t\tenabled: true,\n\t\t\tautoDiscover: true,\n\t\t\tcustom: [],\n\t\t},\n\t\tguards: {\n\t\t\ttsconfig: {\n\t\t\t\tenabled: true,\n\t\t\t\trequiredFlags: [\"strict\", \"strictNullChecks\", \"noImplicitAny\"],\n\t\t\t},\n\t\t\tbiome: {\n\t\t\t\tenabled: false,\n\t\t\t\tlockedRules: [],\n\t\t\t},\n\t\t\tpackageJson: {\n\t\t\t\tenabled: true,\n\t\t\t\tminNodeVersion: \"22.0.0\",\n\t\t\t\trequiredFields: [\"type\", \"engines\"],\n\t\t\t},\n\t\t},\n\t\tproject: {},\n\t};\n}\n\n/**\n * Known top-level keys in {@link ForgeConfig}.\n * Used to warn about unrecognised config keys that are silently ignored.\n * @internal\n */\nconst KNOWN_TOP_KEYS = new Set([\n\t\"rootDir\",\n\t\"tsconfig\",\n\t\"outDir\",\n\t\"enforce\",\n\t\"doctest\",\n\t\"api\",\n\t\"gen\",\n\t\"skill\",\n\t\"bypass\",\n\t\"tsdoc\",\n\t\"guides\",\n\t\"guards\",\n\t\"project\",\n]);\n\n/**\n * Known keys within `enforce.rules`.\n * @internal\n */\nconst KNOWN_RULE_KEYS = new Set([\n\t\"require-summary\",\n\t\"require-param\",\n\t\"require-returns\",\n\t\"require-example\",\n\t\"require-package-doc\",\n\t\"require-class-member-doc\",\n\t\"require-interface-member-doc\",\n\t\"require-tsdoc-syntax\",\n\t\"require-remarks\",\n\t\"require-default-value\",\n\t\"require-type-param\",\n\t\"require-see\",\n\t\"require-release-tag\",\n\t\"require-fresh-guides\",\n\t\"require-guide-coverage\",\n\t\"require-internal-boundary\",\n\t\"require-route-response\",\n\t\"require-inheritdoc-source\",\n\t\"require-migration-path\",\n\t\"require-since\",\n]);\n\n/**\n * Known keys within `tsdoc`.\n * @internal\n */\nconst KNOWN_TSDOC_KEYS = new Set([\"writeConfig\", \"customTags\", \"enforce\"]);\n\n/**\n * Known keys within `tsdoc.enforce`.\n * @internal\n */\nconst KNOWN_TSDOC_ENFORCE_KEYS = new Set([\"core\", \"extended\", \"discretionary\"]);\n\n/**\n * Known keys within `guides`.\n * @internal\n */\nconst KNOWN_GUIDES_KEYS = new Set([\"enabled\", \"autoDiscover\", \"custom\"]);\n\n/**\n * Known keys within `guards`.\n * @internal\n */\nconst KNOWN_GUARDS_KEYS = new Set([\"tsconfig\", \"biome\", \"packageJson\"]);\n\n/**\n * Known keys within `guards.tsconfig`.\n * @internal\n */\nconst KNOWN_GUARDS_TSCONFIG_KEYS = new Set([\"enabled\", \"requiredFlags\"]);\n\n/**\n * Known keys within `guards.biome`.\n * @internal\n */\nconst KNOWN_GUARDS_BIOME_KEYS = new Set([\"enabled\", \"lockedRules\"]);\n\n/**\n * Known keys within `guards.packageJson`.\n * @internal\n */\nconst KNOWN_GUARDS_PACKAGE_JSON_KEYS = new Set([\"enabled\", \"minNodeVersion\", \"requiredFields\"]);\n\n/**\n * Validates an object against a set of known keys and collects warnings.\n * @internal\n */\nfunction validateKnownKeys(\n\tobj: Record<string, unknown>,\n\tknownKeys: Set<string>,\n\tsection: string,\n\twarnings: string[],\n): void {\n\tfor (const key of Object.keys(obj)) {\n\t\tif (!knownKeys.has(key)) {\n\t\t\twarnings.push(`Unknown key \"${key}\" in ${section} — ignored.`);\n\t\t}\n\t}\n}\n\n/**\n * Collects warnings about unknown keys in user config.\n * Also emits to stderr for TTY consumers.\n * Returns the warnings array so it can be attached to the config for\n * agents that only see structured JSON output.\n * @internal\n */\nfunction collectUnknownKeyWarnings(partial: Partial<ForgeConfig>): string[] {\n\tconst warnings: string[] = [];\n\tfor (const key of Object.keys(partial)) {\n\t\tif (!KNOWN_TOP_KEYS.has(key)) {\n\t\t\twarnings.push(`Unknown config key \"${key}\" — ignored.`);\n\t\t}\n\t}\n\tif (partial.enforce?.rules) {\n\t\tfor (const key of Object.keys(partial.enforce.rules)) {\n\t\t\tif (!KNOWN_RULE_KEYS.has(key)) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Unknown enforce rule \"${key}\" — ignored. Valid rules: ${[...KNOWN_RULE_KEYS].join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\tif (partial.tsdoc) {\n\t\tvalidateKnownKeys(\n\t\t\tpartial.tsdoc as unknown as Record<string, unknown>,\n\t\t\tKNOWN_TSDOC_KEYS,\n\t\t\t\"tsdoc\",\n\t\t\twarnings,\n\t\t);\n\t\tif (partial.tsdoc.enforce) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.tsdoc.enforce as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_TSDOC_ENFORCE_KEYS,\n\t\t\t\t\"tsdoc.enforce\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t}\n\tif (partial.guides) {\n\t\tvalidateKnownKeys(\n\t\t\tpartial.guides as unknown as Record<string, unknown>,\n\t\t\tKNOWN_GUIDES_KEYS,\n\t\t\t\"guides\",\n\t\t\twarnings,\n\t\t);\n\t}\n\tif (partial.guards) {\n\t\tvalidateKnownKeys(\n\t\t\tpartial.guards as unknown as Record<string, unknown>,\n\t\t\tKNOWN_GUARDS_KEYS,\n\t\t\t\"guards\",\n\t\t\twarnings,\n\t\t);\n\t\tif (partial.guards.tsconfig) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.guards.tsconfig as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_GUARDS_TSCONFIG_KEYS,\n\t\t\t\t\"guards.tsconfig\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t\tif (partial.guards.biome) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.guards.biome as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_GUARDS_BIOME_KEYS,\n\t\t\t\t\"guards.biome\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t\tif (partial.guards.packageJson) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.guards.packageJson as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_GUARDS_PACKAGE_JSON_KEYS,\n\t\t\t\t\"guards.packageJson\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t}\n\tfor (const w of warnings) {\n\t\tconsole.error(`[forge-ts] warning: ${w}`);\n\t}\n\treturn warnings;\n}\n\n/**\n * Merges a partial user config with the defaults so every field is present.\n *\n * @param rootDir - Absolute path to the project root.\n * @param partial - Partial config from the user's config file.\n * @returns A fully-populated {@link ForgeConfig}.\n * @internal\n */\nfunction mergeWithDefaults(rootDir: string, partial: Partial<ForgeConfig>): ForgeConfig {\n\tconst warnings = collectUnknownKeyWarnings(partial);\n\tconst defaults = defaultConfig(rootDir);\n\tconst config: ForgeConfig = {\n\t\t...defaults,\n\t\t...partial,\n\t\tenforce: {\n\t\t\t...defaults.enforce,\n\t\t\t...partial.enforce,\n\t\t\trules: { ...defaults.enforce.rules, ...partial.enforce?.rules },\n\t\t},\n\t\tdoctest: { ...defaults.doctest, ...partial.doctest },\n\t\tapi: { ...defaults.api, ...partial.api },\n\t\tgen: { ...defaults.gen, ...partial.gen },\n\t\tskill: { ...defaults.skill, ...partial.skill },\n\t\tbypass: { ...defaults.bypass, ...partial.bypass },\n\t\tguides: { ...defaults.guides, ...partial.guides },\n\t\ttsdoc: {\n\t\t\t...defaults.tsdoc,\n\t\t\t...partial.tsdoc,\n\t\t\tenforce: { ...defaults.tsdoc.enforce, ...partial.tsdoc?.enforce },\n\t\t},\n\t\tguards: {\n\t\t\t...defaults.guards,\n\t\t\t...partial.guards,\n\t\t\ttsconfig: { ...defaults.guards.tsconfig, ...partial.guards?.tsconfig },\n\t\t\tbiome: { ...defaults.guards.biome, ...partial.guards?.biome },\n\t\t\tpackageJson: { ...defaults.guards.packageJson, ...partial.guards?.packageJson },\n\t\t},\n\t\tproject: { ...defaults.project, ...partial.project },\n\t};\n\tif (warnings.length > 0) {\n\t\tconfig._configWarnings = warnings;\n\t}\n\treturn config;\n}\n\n/**\n * Extracts repository URL from a package.json repository field.\n * Handles both string and object forms.\n * @internal\n */\nfunction extractRepoUrl(\n\trepo: string | { type?: string; url?: string } | undefined,\n): string | undefined {\n\tif (!repo) return undefined;\n\tconst raw = typeof repo === \"string\" ? repo : repo.url;\n\tif (!raw) return undefined;\n\t// Normalize git+https://... and .git suffix\n\treturn raw.replace(/^git\\+/, \"\").replace(/\\.git$/, \"\");\n}\n\n/**\n * Attempts to load a TypeScript or JavaScript config file via dynamic import.\n *\n * @param filePath - Absolute path to the config file.\n * @returns The default export of the config module, or `null` on failure.\n * @internal\n */\nasync function loadModuleConfig(filePath: string): Promise<Partial<ForgeConfig> | null> {\n\ttry {\n\t\tconst fileUrl = pathToFileURL(filePath).href;\n\t\tconst mod = (await import(fileUrl)) as {\n\t\t\tdefault?: Partial<ForgeConfig>;\n\t\t};\n\t\treturn mod.default ?? null;\n\t} catch (err) {\n\t\t// File exists but failed to import — warn instead of silently falling back.\n\t\t// Common cause: .ts config in a CommonJS project without \"type\": \"module\".\n\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(\n\t\t\t`[forge-ts] warning: failed to load config file \"${filePath}\" — ${msg.split(\"\\n\")[0]}`,\n\t\t);\n\t\treturn null;\n\t}\n}\n\n/**\n * Minimal shape of a `package.json` file relevant to forge-ts config loading.\n * @internal\n */\ninterface PackageJson {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\thomepage?: string;\n\trepository?: string | { type?: string; url?: string };\n\tbin?: string | Record<string, string>;\n\tscripts?: Record<string, string>;\n\tkeywords?: string[];\n\t\"forge-ts\"?: Partial<ForgeConfig>;\n}\n\n/**\n * Attempts to read the `\"forge-ts\"` key from a `package.json` file.\n *\n * @param pkgPath - Absolute path to `package.json`.\n * @returns The value of the `\"forge-ts\"` key, or `null` if absent.\n * @internal\n */\nasync function loadPackageJsonConfig(pkgPath: string): Promise<Partial<ForgeConfig> | null> {\n\ttry {\n\t\tconst raw = await readFile(pkgPath, \"utf8\");\n\t\tconst pkg = JSON.parse(raw) as PackageJson;\n\t\tconst key = pkg[\"forge-ts\"];\n\t\tif (key) {\n\t\t\treturn key;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Loads the forge-ts configuration for a project.\n *\n * Resolution order:\n * 1. `<rootDir>/forge-ts.config.ts`\n * 2. `<rootDir>/forge-ts.config.js`\n * 3. `\"forge-ts\"` key inside `<rootDir>/package.json`\n * 4. Built-in defaults (returned when none of the above is found)\n *\n * @param rootDir - The project root to search for config. Defaults to `process.cwd()`.\n * @returns A fully-resolved {@link ForgeConfig}.\n * @example\n * ```typescript\n * import { loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig(\"/path/to/project\");\n * // config is fully resolved with defaults\n * ```\n * @public\n */\nexport async function loadConfig(rootDir?: string): Promise<ForgeConfig> {\n\tconst root = resolve(rootDir ?? process.cwd());\n\n\tlet config: ForgeConfig;\n\n\tconst candidates = [join(root, \"forge-ts.config.ts\"), join(root, \"forge-ts.config.js\")];\n\tlet found = false;\n\tconst loadWarnings: string[] = [];\n\n\tfor (const candidate of candidates) {\n\t\tif (existsSync(candidate)) {\n\t\t\tconst partial = await loadModuleConfig(candidate);\n\t\t\tif (partial) {\n\t\t\t\tconfig = mergeWithDefaults(root, partial);\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// Config file exists but failed to load — track the warning\n\t\t\tloadWarnings.push(\n\t\t\t\t`Config file \"${candidate}\" exists but could not be loaded. Check that your project has \"type\": \"module\" in package.json or use a .js config file.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (!found) {\n\t\tconst pkgPath = join(root, \"package.json\");\n\t\tif (existsSync(pkgPath)) {\n\t\t\tconst partial = await loadPackageJsonConfig(pkgPath);\n\t\t\tif (partial) {\n\t\t\t\tconfig = mergeWithDefaults(root, partial);\n\t\t\t} else {\n\t\t\t\tconfig = defaultConfig(root);\n\t\t\t}\n\t\t} else {\n\t\t\tconfig = defaultConfig(root);\n\t\t}\n\t} else {\n\t\t// biome-ignore lint: config is always set when found=true\n\t\tconfig = config!;\n\t}\n\n\t// Auto-detect project metadata from package.json if not explicitly set\n\tconst pkgPath = join(root, \"package.json\");\n\tif (existsSync(pkgPath)) {\n\t\ttry {\n\t\t\tconst raw = await readFile(pkgPath, \"utf8\");\n\t\t\tconst pkg = JSON.parse(raw) as PackageJson;\n\t\t\tif (!config.project.repository) {\n\t\t\t\tconfig.project.repository = extractRepoUrl(pkg.repository);\n\t\t\t}\n\t\t\tif (!config.project.homepage) {\n\t\t\t\tconfig.project.homepage = pkg.homepage;\n\t\t\t}\n\t\t\tif (!config.project.packageName) {\n\t\t\t\tconfig.project.packageName = pkg.name;\n\t\t\t}\n\t\t\tif (!config.project.description) {\n\t\t\t\tconfig.project.description = pkg.description;\n\t\t\t}\n\t\t\tif (!config.project.version) {\n\t\t\t\tconfig.project.version = pkg.version;\n\t\t\t}\n\t\t\tif (!config.project.bin) {\n\t\t\t\tif (typeof pkg.bin === \"string\") {\n\t\t\t\t\tconst binName = pkg.name?.replace(/^@[^/]+\\//, \"\") ?? \"cli\";\n\t\t\t\t\tconfig.project.bin = { [binName]: pkg.bin };\n\t\t\t\t} else if (pkg.bin) {\n\t\t\t\t\tconfig.project.bin = pkg.bin;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!config.project.scripts) {\n\t\t\t\tconfig.project.scripts = pkg.scripts;\n\t\t\t}\n\t\t\tif (!config.project.keywords) {\n\t\t\t\tconfig.project.keywords = pkg.keywords;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore parse errors\n\t\t}\n\t}\n\n\t// Attach any config load warnings so CLI commands can surface them\n\tif (loadWarnings.length > 0) {\n\t\tconfig._configWarnings = [...(config._configWarnings ?? []), ...loadWarnings];\n\t}\n\n\treturn config;\n}\n","/**\n * Visibility levels for exported symbols.\n * Derived from TSDoc release tags (@public, @beta, @internal).\n * @public\n */\nexport enum Visibility {\n\tPublic = \"public\",\n\tBeta = \"beta\",\n\tInternal = \"internal\",\n\tPrivate = \"private\",\n}\n\n/**\n * A single extracted and annotated symbol from the TypeScript AST.\n * @public\n */\nexport interface ForgeSymbol {\n\t/** The declared name of the symbol. */\n\tname: string;\n\t/** The syntactic kind of the symbol. */\n\tkind: \"function\" | \"class\" | \"interface\" | \"type\" | \"enum\" | \"variable\" | \"method\" | \"property\";\n\t/** Resolved visibility from TSDoc release tags. */\n\tvisibility: Visibility;\n\t/** Absolute path to the source file. */\n\tfilePath: string;\n\t/** 1-based line number of the declaration. */\n\tline: number;\n\t/** 0-based column of the declaration. */\n\tcolumn: number;\n\t/** Parsed TSDoc documentation, if present. */\n\tdocumentation?: {\n\t\tsummary?: string;\n\t\tparams?: Array<{ name: string; description: string; type?: string }>;\n\t\treturns?: { description: string; type?: string };\n\t\tthrows?: Array<{ type?: string; description: string }>;\n\t\texamples?: Array<{ code: string; language: string; line: number }>;\n\t\ttags?: Record<string, string[]>;\n\t\tdeprecated?: string;\n\t\t/** {@link} cross-references found in this symbol's TSDoc. */\n\t\tlinks?: Array<{ target: string; line: number }>;\n\t\t/** TSDoc parser messages (syntax warnings/errors) from @microsoft/tsdoc. */\n\t\tparseMessages?: Array<{ messageId: string; text: string; line: number }>;\n\t};\n\t/** Human-readable type signature of the symbol. */\n\tsignature?: string;\n\t/** Child symbols (e.g., class members, enum values). */\n\tchildren?: ForgeSymbol[];\n\t/** Whether this symbol is part of the public module exports. */\n\texported: boolean;\n}\n\n/**\n * Severity level for an individual enforcement rule.\n * - `\"error\"` — violation fails the build.\n * - `\"warn\"` — violation is reported but does not fail the build.\n * - `\"off\"` — rule is disabled entirely.\n * @public\n */\nexport type RuleSeverity = \"error\" | \"warn\" | \"off\";\n\n/**\n * Per-rule severity configuration for the TSDoc enforcer.\n * Each key corresponds to one of the E001–E007 rule codes.\n * @public\n */\nexport interface EnforceRules {\n\t/** E001: Exported symbol missing TSDoc summary. */\n\t\"require-summary\": RuleSeverity;\n\t/** E002: Function parameter missing @param tag. */\n\t\"require-param\": RuleSeverity;\n\t/** E003: Non-void function missing @returns tag. */\n\t\"require-returns\": RuleSeverity;\n\t/** E004: Exported function missing @example block. */\n\t\"require-example\": RuleSeverity;\n\t/** E005: Entry point missing @packageDocumentation. */\n\t\"require-package-doc\": RuleSeverity;\n\t/** E006: Class member missing documentation. */\n\t\"require-class-member-doc\": RuleSeverity;\n\t/** E007: Interface/type member missing documentation. */\n\t\"require-interface-member-doc\": RuleSeverity;\n\t/** W006: TSDoc syntax parse error (invalid tag, malformed block, etc.). */\n\t\"require-tsdoc-syntax\": RuleSeverity;\n\t/** E013: Exported function/class is missing a @remarks block. */\n\t\"require-remarks\": RuleSeverity;\n\t/** E014: Optional property with default is missing @defaultValue. */\n\t\"require-default-value\": RuleSeverity;\n\t/** E015: Generic symbol is missing @typeParam for its type parameters. */\n\t\"require-type-param\": RuleSeverity;\n\t/** W005: Symbol references other symbols via {@link} but has no @see tags. */\n\t\"require-see\": RuleSeverity;\n\t/** E016: Exported symbol is missing a release tag (@public, @beta, @internal). */\n\t\"require-release-tag\": RuleSeverity;\n\t/** W007: Guide FORGE:AUTO section references a symbol that no longer exists or has changed. */\n\t\"require-fresh-guides\": RuleSeverity;\n\t/** W008: Exported public symbol is not mentioned in any guide page. */\n\t\"require-guide-coverage\": RuleSeverity;\n\t/** E017: @internal symbol re-exported through public barrel (index.ts). */\n\t\"require-internal-boundary\": RuleSeverity;\n\t/** E018: @route-tagged function missing @response tag. */\n\t\"require-route-response\": RuleSeverity;\n\t/** W009: {@inheritDoc} references a symbol that does not exist. */\n\t\"require-inheritdoc-source\": RuleSeverity;\n\t/** W010: @breaking without @migration path. */\n\t\"require-migration-path\": RuleSeverity;\n\t/** W011: New public export missing @since version tag. */\n\t\"require-since\": RuleSeverity;\n}\n\n/**\n * Full configuration for a forge-ts run.\n * Loaded from forge-ts.config.ts or the \"forge-ts\" key in package.json.\n * @public\n */\nexport interface ForgeConfig {\n\t/** Root directory of the project. */\n\trootDir: string;\n\t/** Path to the tsconfig.json to compile against. */\n\ttsconfig: string;\n\t/** Output directory for generated files. */\n\toutDir: string;\n\t/** Enforce TSDoc on all public exports. */\n\tenforce: {\n\t\tenabled: boolean;\n\t\t/**\n\t\t * Minimum visibility level to enforce documentation on.\n\t\t *\n\t\t * Accepts either a {@link Visibility} enum value or the equivalent\n\t\t * string literal (`\"public\"`, `\"beta\"`, `\"internal\"`, `\"private\"`).\n\t\t * String literals are resolved to enum values internally.\n\t\t */\n\t\tminVisibility: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\";\n\t\t/** Fail on warnings rather than only on errors. */\n\t\tstrict: boolean;\n\t\t/** Per-rule severity overrides. When strict is true, all \"warn\" become \"error\". */\n\t\trules: EnforceRules;\n\t};\n\t/** DocTest configuration. */\n\tdoctest: {\n\t\tenabled: boolean;\n\t\t/** Cache directory for virtual test files. */\n\t\tcacheDir: string;\n\t};\n\t/** API generation configuration. */\n\tapi: {\n\t\tenabled: boolean;\n\t\t/** Generate an OpenAPI spec from exported HTTP handlers. */\n\t\topenapi: boolean;\n\t\t/** Output path for the OpenAPI spec file. */\n\t\topenapiPath: string;\n\t};\n\t/** Output generation configuration. */\n\tgen: {\n\t\tenabled: boolean;\n\t\t/** Output formats to generate. */\n\t\tformats: Array<\"markdown\" | \"mdx\">;\n\t\t/** Generate an llms.txt companion file. */\n\t\tllmsTxt: boolean;\n\t\t/** Synchronise summaries back into README.md. */\n\t\treadmeSync: boolean;\n\t\t/** Static site generator to target for output format. */\n\t\tssgTarget?: \"docusaurus\" | \"mintlify\" | \"nextra\" | \"vitepress\";\n\t};\n\t/**\n\t * Skill package generation settings.\n\t * Custom sections here are merged into the generated SKILL.md,\n\t * allowing projects to inject workflow knowledge, domain gotchas,\n\t * and other context that cannot be derived from symbols alone.\n\t */\n\tskill: {\n\t\t/** When true, generate a SKILL.md package alongside llms.txt. Defaults to following `gen.llmsTxt`. */\n\t\tenabled?: boolean;\n\t\t/**\n\t\t * Custom sections to inject into the generated SKILL.md body.\n\t\t * Each entry becomes a `## heading` section with the provided markdown content.\n\t\t * Sections are inserted after the auto-generated API section and before Gotchas.\n\t\t *\n\t\t * @example\n\t\t * ```typescript\n\t\t * customSections: [\n\t\t * { heading: \"The Flow\", content: \"check → build → docs init → docs dev\" },\n\t\t * { heading: \"SSoT Principle\", content: \"Source code IS documentation.\" },\n\t\t * ]\n\t\t * ```\n\t\t */\n\t\tcustomSections?: Array<{ heading: string; content: string }>;\n\t\t/**\n\t\t * Extra gotcha lines to append to the auto-detected Gotchas section.\n\t\t * Each string becomes a `- ` bullet point.\n\t\t */\n\t\textraGotchas?: string[];\n\t};\n\t/** TSDoc ecosystem configuration. */\n\ttsdoc: {\n\t\t/** Write tsdoc.json to project root during init. Default: true */\n\t\twriteConfig: boolean;\n\t\t/** Custom tag definitions beyond the forge-ts preset. */\n\t\tcustomTags: Array<{\n\t\t\ttagName: string;\n\t\t\tsyntaxKind: \"block\" | \"inline\" | \"modifier\";\n\t\t}>;\n\t\t/** Enforcement level per standardization group. */\n\t\tenforce: {\n\t\t\t/** Core tags (e.g. @param, @returns, @remarks). Default: \"error\" */\n\t\t\tcore: \"error\" | \"warn\" | \"off\";\n\t\t\t/** Extended tags (e.g. @example, @throws, @see). Default: \"warn\" */\n\t\t\textended: \"error\" | \"warn\" | \"off\";\n\t\t\t/** Discretionary tags (@alpha, @beta, @public, @internal). Default: \"off\" */\n\t\t\tdiscretionary: \"error\" | \"warn\" | \"off\";\n\t\t};\n\t};\n\t/** Bypass budget configuration for temporary rule overrides. */\n\tbypass: {\n\t\t/** Maximum number of bypasses allowed per calendar day. Default: 3 */\n\t\tdailyBudget: number;\n\t\t/** Duration in hours before a bypass automatically expires. Default: 24 */\n\t\tdurationHours: number;\n\t};\n\t/** Guide generation configuration. */\n\tguides: {\n\t\t/** Enable intelligent guide generation. Default: true */\n\t\tenabled: boolean;\n\t\t/** Auto-discover guide topics from code analysis. Default: true */\n\t\tautoDiscover: boolean;\n\t\t/** Explicit guide definitions (supplement auto-discovered guides). */\n\t\tcustom: Array<{\n\t\t\t/** URL slug for the guide (e.g., \"authentication\") */\n\t\t\tslug: string;\n\t\t\t/** Human-readable title */\n\t\t\ttitle: string;\n\t\t\t/** Glob patterns for source files to analyze */\n\t\t\tsources: string[];\n\t\t}>;\n\t};\n\t/** Downstream config drift guards. */\n\tguards: {\n\t\t/** tsconfig.json strictness validation. */\n\t\ttsconfig: {\n\t\t\tenabled: boolean;\n\t\t\t/** Required strict-mode flags. Default: [\"strict\", \"strictNullChecks\", \"noImplicitAny\"] */\n\t\t\trequiredFlags: string[];\n\t\t};\n\t\t/** Biome config drift detection. */\n\t\tbiome: {\n\t\t\tenabled: boolean;\n\t\t\t/** Biome rules that must stay at error level. Auto-detected on lock. */\n\t\t\tlockedRules: string[];\n\t\t};\n\t\t/** package.json guards. */\n\t\tpackageJson: {\n\t\t\tenabled: boolean;\n\t\t\t/** Minimum Node.js version in engines field. */\n\t\t\tminNodeVersion: string;\n\t\t\t/** Required fields in package.json. */\n\t\t\trequiredFields: string[];\n\t\t};\n\t};\n\t/**\n\t * Warnings generated during config loading (e.g., unknown keys).\n\t * Populated by loadConfig(). Agents should surface these in output.\n\t * @internal\n\t */\n\t_configWarnings?: string[];\n\t/** Project metadata — auto-detected from package.json if not provided. */\n\tproject: {\n\t\t/** Repository URL (e.g., \"https://github.com/user/repo\"). */\n\t\trepository?: string;\n\t\t/** Project homepage URL. */\n\t\thomepage?: string;\n\t\t/** npm package name for the main/CLI package. */\n\t\tpackageName?: string;\n\t\t/** Short description from package.json. */\n\t\tdescription?: string;\n\t\t/** Package version string. */\n\t\tversion?: string;\n\t\t/** CLI entry points from the `bin` field (e.g., `{ \"my-cli\": \"./dist/cli.js\" }`). */\n\t\tbin?: Record<string, string>;\n\t\t/** npm scripts from package.json (e.g., `{ \"test\": \"vitest\", \"build\": \"tsup\" }`). */\n\t\tscripts?: Record<string, string>;\n\t\t/** npm keywords for the package. */\n\t\tkeywords?: string[];\n\t};\n}\n\n/**\n * The result of a forge-ts compilation pass.\n * @public\n */\nexport interface ForgeResult {\n\t/** Whether the run succeeded without errors. */\n\tsuccess: boolean;\n\t/** All symbols extracted during this run. */\n\tsymbols: ForgeSymbol[];\n\t/** Errors that caused or would cause failure. */\n\terrors: ForgeError[];\n\t/** Non-fatal warnings. */\n\twarnings: ForgeWarning[];\n\t/** Wall-clock duration of the run in milliseconds. */\n\tduration: number;\n\t/** Absolute paths of files written during this run (populated by gen). */\n\twrittenFiles?: string[];\n}\n\n/**\n * A diagnostic error produced during a forge-ts run.\n * @public\n */\nexport interface ForgeError {\n\t/** Machine-readable error code (e.g. \"E001\"). */\n\tcode: string;\n\t/** Human-readable description of the error. */\n\tmessage: string;\n\t/** Absolute path of the file where the error occurred. */\n\tfilePath: string;\n\t/** 1-based line number. */\n\tline: number;\n\t/** 0-based column. */\n\tcolumn: number;\n\t/** Suggested fix for the agent — exact TSDoc block to add. */\n\tsuggestedFix?: string;\n\t/** The symbol name that needs fixing. */\n\tsymbolName?: string;\n\t/** The symbol kind (function, class, interface, etc.). */\n\tsymbolKind?: string;\n}\n\n/**\n * A diagnostic warning produced during a forge-ts run.\n * @public\n */\nexport interface ForgeWarning {\n\t/** Machine-readable warning code (e.g. \"W001\"). */\n\tcode: string;\n\t/** Human-readable description of the warning. */\n\tmessage: string;\n\t/** Absolute path of the file where the warning occurred. */\n\tfilePath: string;\n\t/** 1-based line number. */\n\tline: number;\n\t/** 0-based column. */\n\tcolumn: number;\n}\n","/**\n * Config locking system for forge-ts.\n *\n * Prevents LLM agents from silently weakening project settings by snapshotting\n * the current config state and validating it on every subsequent run.\n *\n * @packageDocumentation\n * @public\n */\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ForgeConfig } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Lock file name\n// ---------------------------------------------------------------------------\n\n/**\n * Default lock file name placed in the project root.\n * @internal\n */\nconst LOCK_FILE_NAME = \".forge-lock.json\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Manifest stored in `.forge-lock.json`.\n * Captures a point-in-time snapshot of the project's forge-ts configuration\n * so that future runs can detect when settings have been weakened.\n *\n * @public\n */\nexport interface ForgeLockManifest {\n\t/** Schema version of the lock manifest. */\n\tversion: string;\n\t/** ISO-8601 timestamp when the lock was created. */\n\tlockedAt: string;\n\t/** Identifier of the user or agent that created the lock. */\n\tlockedBy: string;\n\t/** Snapshot of locked configuration values. */\n\tconfig: {\n\t\t/** Rule name to severity mapping from enforce.rules. */\n\t\trules: Record<string, string>;\n\t\t/** tsconfig guard settings, if readable at lock time. */\n\t\ttsconfig?: Record<string, unknown>;\n\t\t/** Biome guard settings, if readable at lock time. */\n\t\tbiome?: Record<string, unknown>;\n\t};\n}\n\n/**\n * A single violation found when comparing current config against the lock.\n *\n * @public\n */\nexport interface LockViolation {\n\t/** Dot-path of the config field that changed (e.g., \"rules.require-summary\"). */\n\tfield: string;\n\t/** The value stored in the lock file. */\n\tlocked: string;\n\t/** The current value in the live config. */\n\tcurrent: string;\n\t/** Human-readable explanation of the violation. */\n\tmessage: string;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the `.forge-lock.json` file from the given project root.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns The parsed lock manifest, or `null` if no lock file exists or is invalid.\n * @example\n * ```typescript\n * import { readLockFile } from \"@forge-ts/core\";\n * const lock = readLockFile(\"/path/to/project\");\n * if (lock) {\n * console.log(`Locked at ${lock.lockedAt} by ${lock.lockedBy}`);\n * }\n * ```\n * @public\n */\nexport function readLockFile(rootDir: string): ForgeLockManifest | null {\n\tconst lockPath = join(rootDir, LOCK_FILE_NAME);\n\tif (!existsSync(lockPath)) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst raw = readFileSync(lockPath, \"utf8\");\n\t\treturn JSON.parse(raw) as ForgeLockManifest;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Writes a {@link ForgeLockManifest} to `.forge-lock.json` in the project root.\n *\n * @param rootDir - Absolute path to the project root.\n * @param manifest - The lock manifest to write.\n * @example\n * ```typescript\n * import { writeLockFile, createLockManifest, loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig(\"/path/to/project\");\n * const manifest = createLockManifest(config);\n * writeLockFile(\"/path/to/project\", manifest);\n * ```\n * @public\n */\nexport function writeLockFile(rootDir: string, manifest: ForgeLockManifest): void {\n\tconst lockPath = join(rootDir, LOCK_FILE_NAME);\n\twriteFileSync(lockPath, `${JSON.stringify(manifest, null, 2)}\\n`, \"utf8\");\n}\n\n/**\n * Removes the `.forge-lock.json` file from the project root.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns `true` if the file existed and was removed, `false` otherwise.\n * @example\n * ```typescript\n * import { removeLockFile } from \"@forge-ts/core\";\n * const removed = removeLockFile(\"/path/to/project\");\n * console.log(removed ? \"Lock removed\" : \"No lock file found\");\n * ```\n * @public\n */\nexport function removeLockFile(rootDir: string): boolean {\n\tconst lockPath = join(rootDir, LOCK_FILE_NAME);\n\tif (!existsSync(lockPath)) {\n\t\treturn false;\n\t}\n\ttry {\n\t\tunlinkSync(lockPath);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a {@link ForgeLockManifest} from the current project config.\n *\n * Snapshots the enforce rule severities and guard settings so they can\n * be compared on future runs to detect weakening.\n *\n * @param config - The fully-resolved {@link ForgeConfig} to snapshot.\n * @param lockedBy - Identifier of the user or agent creating the lock. Defaults to `\"forge-ts lock\"`.\n * @returns A new lock manifest ready to be written with {@link writeLockFile}.\n * @example\n * ```typescript\n * import { createLockManifest, loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const manifest = createLockManifest(config);\n * console.log(manifest.config.rules); // { \"require-summary\": \"error\", ... }\n * ```\n * @public\n */\nexport function createLockManifest(\n\tconfig: ForgeConfig,\n\tlockedBy: string = \"forge-ts lock\",\n): ForgeLockManifest {\n\tconst rules: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(config.enforce.rules)) {\n\t\trules[key] = value;\n\t}\n\n\tconst lockConfig: ForgeLockManifest[\"config\"] = { rules };\n\n\t// Snapshot tsconfig guard settings if enabled\n\tif (config.guards.tsconfig.enabled) {\n\t\tlockConfig.tsconfig = {\n\t\t\tenabled: config.guards.tsconfig.enabled,\n\t\t\trequiredFlags: config.guards.tsconfig.requiredFlags,\n\t\t};\n\t}\n\n\t// Snapshot biome guard settings if enabled\n\tif (config.guards.biome.enabled) {\n\t\tlockConfig.biome = {\n\t\t\tenabled: config.guards.biome.enabled,\n\t\t\tlockedRules: config.guards.biome.lockedRules,\n\t\t};\n\t}\n\n\treturn {\n\t\tversion: \"1.0.0\",\n\t\tlockedAt: new Date().toISOString(),\n\t\tlockedBy,\n\t\tconfig: lockConfig,\n\t};\n}\n\n/**\n * Validates the current config against a locked manifest.\n *\n * Returns an array of violations where the current config has weakened\n * settings relative to the locked state. Weakening means:\n * - A rule severity changed from `\"error\"` to `\"warn\"` or `\"off\"`\n * - A rule severity changed from `\"warn\"` to `\"off\"`\n * - A tsconfig guard was disabled\n * - A required tsconfig flag was removed\n * - A biome guard was disabled\n * - A locked biome rule was removed\n *\n * @param config - The current fully-resolved {@link ForgeConfig}.\n * @param lock - The lock manifest to validate against.\n * @returns An array of {@link LockViolation} entries. Empty means no weakening detected.\n * @example\n * ```typescript\n * import { validateAgainstLock, readLockFile, loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const lock = readLockFile(config.rootDir);\n * if (lock) {\n * const violations = validateAgainstLock(config, lock);\n * for (const v of violations) {\n * console.error(`LOCK VIOLATION: ${v.message}`);\n * }\n * }\n * ```\n * @public\n */\nexport function validateAgainstLock(config: ForgeConfig, lock: ForgeLockManifest): LockViolation[] {\n\tconst violations: LockViolation[] = [];\n\n\t// Severity ranking: higher number = stricter\n\tconst severityRank: Record<string, number> = {\n\t\toff: 0,\n\t\twarn: 1,\n\t\terror: 2,\n\t};\n\n\t// Check rule severities\n\tfor (const [ruleName, lockedSeverity] of Object.entries(lock.config.rules)) {\n\t\tconst currentSeverity =\n\t\t\t(config.enforce.rules as unknown as Record<string, string>)[ruleName] ?? \"off\";\n\t\tconst lockedRank = severityRank[lockedSeverity] ?? 0;\n\t\tconst currentRank = severityRank[currentSeverity] ?? 0;\n\n\t\tif (currentRank < lockedRank) {\n\t\t\tviolations.push({\n\t\t\t\tfield: `rules.${ruleName}`,\n\t\t\t\tlocked: lockedSeverity,\n\t\t\t\tcurrent: currentSeverity,\n\t\t\t\tmessage: `Rule \"${ruleName}\" was weakened from \"${lockedSeverity}\" to \"${currentSeverity}\". Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check tsconfig guard settings\n\tif (lock.config.tsconfig) {\n\t\tconst lockedTsconfig = lock.config.tsconfig as {\n\t\t\tenabled?: boolean;\n\t\t\trequiredFlags?: string[];\n\t\t};\n\n\t\t// Guard was disabled\n\t\tif (lockedTsconfig.enabled && !config.guards.tsconfig.enabled) {\n\t\t\tviolations.push({\n\t\t\t\tfield: \"guards.tsconfig.enabled\",\n\t\t\t\tlocked: \"true\",\n\t\t\t\tcurrent: \"false\",\n\t\t\t\tmessage:\n\t\t\t\t\t'tsconfig guard was disabled. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".',\n\t\t\t});\n\t\t}\n\n\t\t// Required flags removed\n\t\tif (lockedTsconfig.requiredFlags && config.guards.tsconfig.enabled) {\n\t\t\tconst currentFlags = new Set(config.guards.tsconfig.requiredFlags);\n\t\t\tfor (const flag of lockedTsconfig.requiredFlags) {\n\t\t\t\tif (!currentFlags.has(flag)) {\n\t\t\t\t\tviolations.push({\n\t\t\t\t\t\tfield: `guards.tsconfig.requiredFlags.${flag}`,\n\t\t\t\t\t\tlocked: flag,\n\t\t\t\t\t\tcurrent: \"(removed)\",\n\t\t\t\t\t\tmessage: `tsconfig required flag \"${flag}\" was removed. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check biome guard settings\n\tif (lock.config.biome) {\n\t\tconst lockedBiome = lock.config.biome as {\n\t\t\tenabled?: boolean;\n\t\t\tlockedRules?: string[];\n\t\t};\n\n\t\t// Guard was disabled\n\t\tif (lockedBiome.enabled && !config.guards.biome.enabled) {\n\t\t\tviolations.push({\n\t\t\t\tfield: \"guards.biome.enabled\",\n\t\t\t\tlocked: \"true\",\n\t\t\t\tcurrent: \"false\",\n\t\t\t\tmessage:\n\t\t\t\t\t'Biome guard was disabled. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".',\n\t\t\t});\n\t\t}\n\n\t\t// Locked rules removed\n\t\tif (lockedBiome.lockedRules && config.guards.biome.enabled) {\n\t\t\tconst currentRules = new Set(config.guards.biome.lockedRules);\n\t\t\tfor (const rule of lockedBiome.lockedRules) {\n\t\t\t\tif (!currentRules.has(rule)) {\n\t\t\t\t\tviolations.push({\n\t\t\t\t\t\tfield: `guards.biome.lockedRules.${rule}`,\n\t\t\t\t\t\tlocked: rule,\n\t\t\t\t\t\tcurrent: \"(removed)\",\n\t\t\t\t\t\tmessage: `Biome locked rule \"${rule}\" was removed. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn violations;\n}\n","import { type ForgeSymbol, Visibility } from \"./types.js\";\n\n/**\n * Determines the visibility level of a symbol from its TSDoc release tags.\n *\n * The precedence order is:\n * 1. `@internal` → {@link Visibility.Internal}\n * 2. `@beta` → {@link Visibility.Beta}\n * 3. `@public` → {@link Visibility.Public}\n * 4. (no tag) → {@link Visibility.Public} (default for exports)\n *\n * @param tags - The parsed `tags` map from `ForgeSymbol.documentation`.\n * @returns The resolved {@link Visibility} value.\n * @example\n * ```typescript\n * import { resolveVisibility } from \"@forge-ts/core\";\n * const vis = resolveVisibility({ internal: [] });\n * // vis === Visibility.Internal\n * ```\n * @public\n */\nexport function resolveVisibility(tags: Record<string, string[]> | undefined): Visibility {\n\tif (!tags) return Visibility.Public;\n\n\tif (\"internal\" in tags) return Visibility.Internal;\n\tif (\"beta\" in tags) return Visibility.Beta;\n\tif (\"public\" in tags) return Visibility.Public;\n\n\treturn Visibility.Public;\n}\n\n/**\n * Numeric rank used for visibility comparisons.\n * Lower numbers are more restrictive.\n * @internal\n */\nconst VISIBILITY_RANK: Record<Visibility, number> = {\n\t[Visibility.Public]: 0,\n\t[Visibility.Beta]: 1,\n\t[Visibility.Internal]: 2,\n\t[Visibility.Private]: 3,\n};\n\n/**\n * Returns whether `candidate` meets or exceeds the required minimum visibility.\n *\n * \"Meets\" means the symbol is at least as visible as `minVisibility`.\n * For example, `Public` meets a minimum of `Public`, but `Internal` does not.\n *\n * Both parameters accept either a {@link Visibility} enum value or the\n * equivalent string literal (`\"public\"`, `\"beta\"`, `\"internal\"`, `\"private\"`).\n *\n * @param candidate - The visibility of the symbol being tested.\n * @param minVisibility - The minimum visibility threshold.\n * @returns `true` if `candidate` is at least as visible as `minVisibility`.\n * @example\n * ```typescript\n * import { meetsVisibility, Visibility } from \"@forge-ts/core\";\n * meetsVisibility(Visibility.Public, Visibility.Public); // true\n * meetsVisibility(Visibility.Internal, Visibility.Public); // false\n * meetsVisibility(\"public\", \"beta\"); // true (string literals also accepted)\n * ```\n * @public\n */\nexport function meetsVisibility(\n\tcandidate: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\",\n\tminVisibility: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\",\n): boolean {\n\treturn VISIBILITY_RANK[candidate as Visibility] <= VISIBILITY_RANK[minVisibility as Visibility];\n}\n\n/**\n * Filters an array of {@link ForgeSymbol} objects to only include symbols\n * whose visibility meets or exceeds `minVisibility`.\n *\n * @param symbols - The full list of symbols to filter.\n * @param minVisibility - The minimum visibility threshold to keep.\n * @returns A new array containing only symbols that pass the visibility check.\n * @example\n * ```typescript\n * import { filterByVisibility, Visibility } from \"@forge-ts/core\";\n * const publicOnly = filterByVisibility(symbols, Visibility.Public);\n * ```\n * @public\n */\nexport function filterByVisibility(\n\tsymbols: ForgeSymbol[],\n\tminVisibility: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\",\n): ForgeSymbol[] {\n\treturn symbols.filter((s) => meetsVisibility(s.visibility, minVisibility));\n}\n","import { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, resolve } from \"node:path\";\nimport {\n\ttype DocBlock,\n\ttype DocCodeSpan,\n\ttype DocComment,\n\ttype DocFencedCode,\n\ttype DocLinkTag,\n\ttype DocNode,\n\tDocNodeKind,\n\ttype DocParagraph,\n\ttype DocPlainText,\n\ttype DocSection,\n\tStandardTags,\n\tTSDocConfiguration,\n\tTSDocParser,\n} from \"@microsoft/tsdoc\";\nimport { TSDocConfigFile } from \"@microsoft/tsdoc-config\";\nimport ts from \"typescript\";\nimport type { ForgeConfig, ForgeSymbol } from \"./types.js\";\nimport { resolveVisibility } from \"./visibility.js\";\n\n// ---------------------------------------------------------------------------\n// TSDoc configuration cache (keyed by folder path)\n// ---------------------------------------------------------------------------\n\n/**\n * Per-folder cache of resolved TSDoc configurations. Prevents re-loading\n * `tsdoc.json` for every comment in the same directory.\n * @internal\n */\nconst tsdocConfigCache = new Map<string, TSDocConfiguration>();\n\n/** Clears the TSDoc configuration cache. Intended for use in tests only. @internal */\nexport function clearTSDocConfigCache(): void {\n\ttsdocConfigCache.clear();\n}\n\n/**\n * Resolve the TSDoc configuration to use when parsing comments in\n * files under `folderPath`.\n *\n * If a `tsdoc.json` file exists in or above the folder and can be loaded\n * without errors, its settings are applied to a fresh `TSDocConfiguration`\n * via `TSDocConfigFile.configureParser()`. Otherwise the default\n * `TSDocConfiguration` is returned (backward-compatible behaviour).\n *\n * Results are cached per folder path so the file system is only consulted\n * once per unique directory.\n *\n * @param folderPath - Absolute directory path of the source file being parsed.\n * @returns A configured `TSDocConfiguration` instance.\n * @internal\n */\nexport function loadTSDocConfiguration(folderPath: string): TSDocConfiguration {\n\tconst cached = tsdocConfigCache.get(folderPath);\n\tif (cached) return cached;\n\n\tconst configuration = new TSDocConfiguration();\n\n\ttry {\n\t\tconst configFile = TSDocConfigFile.loadForFolder(folderPath);\n\t\tif (!configFile.fileNotFound && !configFile.hasErrors) {\n\t\t\tconfigFile.configureParser(configuration);\n\t\t} else {\n\t\t\t// No project-level tsdoc.json found — load the bundled preset so\n\t\t\t// forge-ts custom tags (@route, @category, etc.) are still recognised.\n\t\t\tloadBundledPreset(configuration);\n\t\t}\n\t} catch {\n\t\t// If loading fails for any reason, fall back to the bundled preset.\n\t\ttry {\n\t\t\tloadBundledPreset(configuration);\n\t\t} catch {\n\t\t\t// Last resort: bare TSDocConfiguration (no custom tags).\n\t\t}\n\t}\n\n\ttsdocConfigCache.set(folderPath, configuration);\n\treturn configuration;\n}\n\n/**\n * Loads the forge-ts bundled tsdoc.json preset from\n * `@forge-ts/core/tsdoc-preset/tsdoc.json` and applies it to the given\n * configuration via `TSDocConfigFile.configureParser()`.\n *\n * @param configuration - The `TSDocConfiguration` instance to configure.\n * @internal\n */\nfunction loadBundledPreset(configuration: TSDocConfiguration): void {\n\tconst require = createRequire(import.meta.url);\n\tconst presetPath = require.resolve(\"@forge-ts/core/tsdoc-preset/tsdoc.json\");\n\tconst presetFile = TSDocConfigFile.loadFile(presetPath);\n\tif (!presetFile.hasErrors) {\n\t\tpresetFile.configureParser(configuration);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Public API surface\n// ---------------------------------------------------------------------------\n\n/**\n * The return type of {@link createWalker}.\n * @public\n */\nexport interface ASTWalker {\n\t/**\n\t * Walk all source files referenced by the configured tsconfig and return\n\t * one {@link ForgeSymbol} per exported declaration.\n\t */\n\twalk(): ForgeSymbol[];\n}\n\n// ---------------------------------------------------------------------------\n// TSDoc helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Render inline nodes (PlainText, CodeSpan, SoftBreak, LinkTag) to a plain string.\n * `{@link Target}` references are rendered as backtick-wrapped target names\n * (e.g., `` `ForgeConfig` ``) so they appear correctly in generated Markdown.\n * @internal\n */\nfunction renderInlineNodes(nodes: readonly DocNode[]): string {\n\tconst parts: string[] = [];\n\tfor (const node of nodes) {\n\t\tswitch (node.kind) {\n\t\t\tcase DocNodeKind.PlainText:\n\t\t\t\tparts.push((node as DocPlainText).text);\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.CodeSpan:\n\t\t\t\tparts.push(`\\`${(node as DocCodeSpan).code}\\``);\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.SoftBreak:\n\t\t\t\tparts.push(\" \");\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.Paragraph:\n\t\t\t\t// Recurse into paragraph nodes to extract nested text\n\t\t\t\tparts.push(renderInlineNodes((node as DocParagraph).nodes));\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.LinkTag: {\n\t\t\t\t// Render {@link Target} as `Target` in output text.\n\t\t\t\t// Uses the link text if provided, otherwise the code destination.\n\t\t\t\tconst linkTag = node as DocLinkTag;\n\t\t\t\tconst linkText =\n\t\t\t\t\tlinkTag.linkText ??\n\t\t\t\t\tlinkTag.codeDestination?.memberReferences\n\t\t\t\t\t\t.map((ref) => ref.memberIdentifier?.identifier)\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\".\");\n\t\t\t\tif (linkText) {\n\t\t\t\t\tparts.push(`\\`${linkText}\\``);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn parts.join(\"\");\n}\n\n/** Render a `DocSection` (or similar node) to a plain string. @internal */\nfunction renderDocSection(section: DocSection | undefined): string {\n\tif (!section) return \"\";\n\treturn renderInlineNodes(section.nodes).trim();\n}\n\n/** Render a `DocBlock`'s content to a plain string. @internal */\nfunction renderBlock(block: DocBlock): string {\n\treturn renderDocSection(block.content);\n}\n\n/** Extract all `@example` fenced code blocks from a parsed comment. @internal */\nfunction extractExamples(\n\tcomment: DocComment,\n\tstartLine: number,\n): Array<{ code: string; language: string; line: number }> {\n\tconst examples: Array<{ code: string; language: string; line: number }> = [];\n\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() !== \"@example\") continue;\n\n\t\tfor (const node of block.content.nodes) {\n\t\t\tif (node.kind === DocNodeKind.FencedCode) {\n\t\t\t\tconst fenced = node as DocFencedCode;\n\t\t\t\texamples.push({\n\t\t\t\t\tcode: fenced.code,\n\t\t\t\t\tlanguage: fenced.language || \"typescript\",\n\t\t\t\t\tline: startLine,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn examples;\n}\n\n/** Parse a raw JSDoc/TSDoc comment string into a structured documentation object. @internal */\nfunction parseTSDoc(\n\trawComment: string,\n\tstartLine: number,\n\tfolderPath?: string,\n): ForgeSymbol[\"documentation\"] {\n\tconst configuration =\n\t\tfolderPath !== undefined ? loadTSDocConfiguration(folderPath) : new TSDocConfiguration();\n\tconst parser = new TSDocParser(configuration);\n\tconst result = parser.parseString(rawComment);\n\tconst comment = result.docComment;\n\n\tconst tags: Record<string, string[]> = {};\n\n\t// Release tags\n\tif (comment.modifierTagSet.hasTag(StandardTags.public)) {\n\t\ttags.public = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.beta)) {\n\t\ttags.beta = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.internal)) {\n\t\ttags.internal = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.alpha)) {\n\t\ttags.alpha = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.packageDocumentation)) {\n\t\ttags.packageDocumentation = [];\n\t}\n\n\t// @deprecated\n\tlet deprecated: string | undefined;\n\tif (comment.deprecatedBlock) {\n\t\tdeprecated = renderBlock(comment.deprecatedBlock).trim() || \"true\";\n\t}\n\n\t// @param blocks\n\tconst params: Array<{ name: string; description: string; type?: string }> = [];\n\tfor (const paramBlock of comment.params.blocks) {\n\t\tparams.push({\n\t\t\tname: paramBlock.parameterName,\n\t\t\tdescription: renderBlock(paramBlock),\n\t\t});\n\t}\n\n\t// @returns block\n\tlet returns: { description: string; type?: string } | undefined;\n\tif (comment.returnsBlock) {\n\t\tconst desc = renderBlock(comment.returnsBlock);\n\t\tif (desc) returns = { description: desc };\n\t}\n\n\t// @throws blocks\n\tconst throws: Array<{ type?: string; description: string }> = [];\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@throws\") {\n\t\t\tthrows.push({ description: renderBlock(block) });\n\t\t}\n\t}\n\n\t// @route blocks - format: \"METHOD /path\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@route\") {\n\t\t\tconst routeText = renderBlock(block).trim();\n\t\t\tconst match = routeText.match(/^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)\\s+(\\S+)/i);\n\t\t\tif (match) {\n\t\t\t\tif (!tags.route) tags.route = [];\n\t\t\t\ttags.route.push(`${match[1].toUpperCase()} ${match[2]}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @remarks block\n\tif (comment.remarksBlock) {\n\t\tconst remarksText = renderBlock(comment.remarksBlock).trim();\n\t\ttags.remarks = remarksText ? [remarksText] : [];\n\t}\n\n\t// @see blocks\n\tif (comment.seeBlocks.length > 0) {\n\t\ttags.see = comment.seeBlocks.map((block) => renderBlock(block).trim()).filter(Boolean);\n\t}\n\n\t// @typeParam blocks\n\tif (comment.typeParams.count > 0) {\n\t\ttags.typeParam = comment.typeParams.blocks.map(\n\t\t\t(block) => `${block.parameterName} - ${renderBlock(block).trim()}`,\n\t\t);\n\t}\n\n\t// @defaultValue blocks\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@defaultvalue\") {\n\t\t\tconst dvText = renderBlock(block).trim();\n\t\t\tif (!tags.defaultValue) tags.defaultValue = [];\n\t\t\ttags.defaultValue.push(dvText);\n\t\t}\n\t}\n\n\t// @concept blocks — free-text concept labels for grouping/navigation\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@concept\") {\n\t\t\tconst conceptText = renderBlock(block).trim();\n\t\t\tif (conceptText) {\n\t\t\t\tif (!tags.concept) tags.concept = [];\n\t\t\t\ttags.concept.push(conceptText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @guide blocks — guide slugs/labels for cross-referencing documentation\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@guide\") {\n\t\t\tconst guideText = renderBlock(block).trim();\n\t\t\tif (guideText) {\n\t\t\t\tif (!tags.guide) tags.guide = [];\n\t\t\t\ttags.guide.push(guideText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @category blocks — grouping labels for navigation and guide discovery\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@category\") {\n\t\t\tconst categoryText = renderBlock(block).trim();\n\t\t\tif (categoryText) {\n\t\t\t\tif (!tags.category) tags.category = [];\n\t\t\t\ttags.category.push(categoryText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @since blocks — version when the symbol was introduced\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@since\") {\n\t\t\tconst sinceText = renderBlock(block).trim();\n\t\t\tif (sinceText) {\n\t\t\t\tif (!tags.since) tags.since = [];\n\t\t\t\ttags.since.push(sinceText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @response blocks — format: \"STATUS_CODE - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@response\") {\n\t\t\tconst responseText = renderBlock(block).trim();\n\t\t\tif (responseText) {\n\t\t\t\tif (!tags.response) tags.response = [];\n\t\t\t\ttags.response.push(responseText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @query blocks — format: \"paramName - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@query\") {\n\t\t\tconst queryText = renderBlock(block).trim();\n\t\t\tif (queryText) {\n\t\t\t\tif (!tags.query) tags.query = [];\n\t\t\t\ttags.query.push(queryText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @header blocks — format: \"headerName - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@header\") {\n\t\t\tconst headerText = renderBlock(block).trim();\n\t\t\tif (headerText) {\n\t\t\t\tif (!tags.header) tags.header = [];\n\t\t\t\ttags.header.push(headerText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @body blocks — format: \"TypeName - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@body\") {\n\t\t\tconst bodyText = renderBlock(block).trim();\n\t\t\tif (bodyText) {\n\t\t\t\tif (!tags.body) tags.body = [];\n\t\t\t\ttags.body.push(bodyText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @faq blocks — free text\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@faq\") {\n\t\t\tconst faqText = renderBlock(block).trim();\n\t\t\tif (faqText) {\n\t\t\t\tif (!tags.faq) tags.faq = [];\n\t\t\t\ttags.faq.push(faqText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @breaking blocks — free text describing the breaking change\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@breaking\") {\n\t\t\tconst breakingText = renderBlock(block).trim();\n\t\t\tif (breakingText) {\n\t\t\t\tif (!tags.breaking) tags.breaking = [];\n\t\t\t\ttags.breaking.push(breakingText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @migration blocks — free text describing migration path\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@migration\") {\n\t\t\tconst migrationText = renderBlock(block).trim();\n\t\t\tif (migrationText) {\n\t\t\t\tif (!tags.migration) tags.migration = [];\n\t\t\t\ttags.migration.push(migrationText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @complexity blocks — free text (e.g., \"O(n log n)\")\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@complexity\") {\n\t\t\tconst complexityText = renderBlock(block).trim();\n\t\t\tif (complexityText) {\n\t\t\t\tif (!tags.complexity) tags.complexity = [];\n\t\t\t\ttags.complexity.push(complexityText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @quickstart modifier tag — presence-only, no content\n\tfor (const tag of comment.modifierTagSet.nodes) {\n\t\tif (tag.tagName.toLowerCase() === \"@quickstart\") {\n\t\t\ttags.quickstart = [];\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// @example blocks\n\tconst examples = extractExamples(comment, startLine);\n\n\t// Extract {@link} references\n\tconst links: Array<{ target: string; line: number }> = [];\n\tfunction walkForLinks(node: DocNode): void {\n\t\tif (node.kind === DocNodeKind.LinkTag) {\n\t\t\tconst linkTag = node as DocLinkTag;\n\t\t\tif (linkTag.codeDestination) {\n\t\t\t\tconst target = linkTag.codeDestination.memberReferences\n\t\t\t\t\t.map((ref) => ref.memberIdentifier?.identifier ?? \"\")\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(\".\");\n\t\t\t\tif (target) {\n\t\t\t\t\tlinks.push({ target, line: startLine });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const child of node.getChildNodes()) {\n\t\t\twalkForLinks(child);\n\t\t}\n\t}\n\twalkForLinks(comment);\n\n\t// Extract {@inheritDoc} target if present\n\tif (comment.inheritDocTag?.declarationReference) {\n\t\tconst ref = comment.inheritDocTag.declarationReference;\n\t\tconst target = ref.memberReferences\n\t\t\t.map((r) => r.memberIdentifier?.identifier ?? \"\")\n\t\t\t.filter(Boolean)\n\t\t\t.join(\".\");\n\t\tif (target) {\n\t\t\tif (!tags.inheritDoc) tags.inheritDoc = [];\n\t\t\ttags.inheritDoc.push(target);\n\t\t}\n\t}\n\n\t// Collect TSDoc parser messages (syntax warnings/errors)\n\tconst parseMessages: Array<{ messageId: string; text: string; line: number }> = [];\n\tfor (const msg of result.log.messages) {\n\t\tparseMessages.push({\n\t\t\tmessageId: msg.messageId,\n\t\t\ttext: msg.unformattedText,\n\t\t\tline: startLine,\n\t\t});\n\t}\n\n\tconst summary = renderDocSection(comment.summarySection);\n\n\treturn {\n\t\tsummary: summary || undefined,\n\t\tparams: params.length > 0 ? params : undefined,\n\t\treturns,\n\t\tthrows: throws.length > 0 ? throws : undefined,\n\t\texamples: examples.length > 0 ? examples : undefined,\n\t\ttags: Object.keys(tags).length > 0 ? tags : undefined,\n\t\tdeprecated,\n\t\tlinks: links.length > 0 ? links : undefined,\n\t\tparseMessages: parseMessages.length > 0 ? parseMessages : undefined,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// TypeScript AST helpers\n// ---------------------------------------------------------------------------\n\n/** Extract the leading JSDoc comment text for a node. @internal */\nfunction getLeadingComment(node: ts.Node, sourceFile: ts.SourceFile): string | undefined {\n\tconst fullText = sourceFile.getFullText();\n\tconst ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());\n\tif (!ranges || ranges.length === 0) return undefined;\n\n\t// Take the last leading comment (closest to the declaration)\n\tconst range = ranges[ranges.length - 1];\n\tif (\n\t\trange.kind !== ts.SyntaxKind.MultiLineCommentTrivia ||\n\t\t!fullText.slice(range.pos, range.end).startsWith(\"/**\")\n\t) {\n\t\treturn undefined;\n\t}\n\n\treturn fullText.slice(range.pos, range.end);\n}\n\n/** Map a TypeScript `SyntaxKind` to a `ForgeSymbol` kind string. @internal */\nfunction kindToString(kind: ts.SyntaxKind): ForgeSymbol[\"kind\"] | null {\n\tswitch (kind) {\n\t\tcase ts.SyntaxKind.FunctionDeclaration:\n\t\tcase ts.SyntaxKind.ArrowFunction:\n\t\tcase ts.SyntaxKind.FunctionExpression:\n\t\t\treturn \"function\";\n\t\tcase ts.SyntaxKind.ClassDeclaration:\n\t\t\treturn \"class\";\n\t\tcase ts.SyntaxKind.InterfaceDeclaration:\n\t\t\treturn \"interface\";\n\t\tcase ts.SyntaxKind.TypeAliasDeclaration:\n\t\t\treturn \"type\";\n\t\tcase ts.SyntaxKind.EnumDeclaration:\n\t\t\treturn \"enum\";\n\t\tcase ts.SyntaxKind.VariableDeclaration:\n\t\tcase ts.SyntaxKind.VariableStatement:\n\t\t\treturn \"variable\";\n\t\tcase ts.SyntaxKind.MethodDeclaration:\n\t\tcase ts.SyntaxKind.MethodSignature:\n\t\t\treturn \"method\";\n\t\tcase ts.SyntaxKind.PropertyDeclaration:\n\t\tcase ts.SyntaxKind.PropertySignature:\n\t\tcase ts.SyntaxKind.EnumMember:\n\t\t\treturn \"property\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\n/** Build a human-readable type signature string using the type checker. @internal */\nfunction buildSignature(node: ts.Declaration, checker: ts.TypeChecker): string | undefined {\n\ttry {\n\t\tconst symbol = checker.getSymbolAtLocation((node as ts.NamedDeclaration).name ?? node);\n\t\tif (!symbol) return undefined;\n\t\tconst type = checker.getTypeOfSymbolAtLocation(symbol, node);\n\t\treturn checker.typeToString(type);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Walker implementation\n// ---------------------------------------------------------------------------\n\n/** @internal */\nfunction extractSymbolsFromFile(sourceFile: ts.SourceFile, checker: ts.TypeChecker): ForgeSymbol[] {\n\tconst symbols: ForgeSymbol[] = [];\n\tconst filePath = sourceFile.fileName;\n\tconst fileDir = dirname(filePath);\n\n\tfunction visit(node: ts.Node, parentExported: boolean): void {\n\t\tconst isExported =\n\t\t\tparentExported ||\n\t\t\t(ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export) !== 0;\n\n\t\t// Handle export declarations: `export { Foo, Bar }`\n\t\tif (ts.isExportDeclaration(node)) {\n\t\t\tts.forEachChild(node, (child) => visit(child, true));\n\t\t\treturn;\n\t\t}\n\n\t\tconst kind = kindToString(node.kind);\n\n\t\t// Variable statements need special handling: `export const foo = ...`\n\t\tif (ts.isVariableStatement(node)) {\n\t\t\tif (!isExported) {\n\t\t\t\tts.forEachChild(node, (child) => visit(child, false));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (const decl of node.declarationList.declarations) {\n\t\t\t\tconst name = decl.name.getText(sourceFile);\n\t\t\t\tconst pos = sourceFile.getLineAndCharacterOfPosition(decl.getStart());\n\t\t\t\tconst rawComment = getLeadingComment(node, sourceFile);\n\t\t\t\tconst documentation = rawComment\n\t\t\t\t\t? parseTSDoc(rawComment, pos.line + 1, fileDir)\n\t\t\t\t\t: undefined;\n\t\t\t\tconst tags = documentation?.tags;\n\t\t\t\tconst visibility = resolveVisibility(tags);\n\n\t\t\t\tsymbols.push({\n\t\t\t\t\tname,\n\t\t\t\t\tkind: \"variable\",\n\t\t\t\t\tvisibility,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tline: pos.line + 1,\n\t\t\t\t\tcolumn: pos.character,\n\t\t\t\t\tdocumentation,\n\t\t\t\t\tsignature: buildSignature(decl, checker),\n\t\t\t\t\texported: true,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kind === null || !isExported) {\n\t\t\tts.forEachChild(node, (child) => visit(child, isExported));\n\t\t\treturn;\n\t\t}\n\n\t\tconst namedNode = node as ts.NamedDeclaration;\n\t\tconst nameNode = namedNode.name;\n\t\tif (!nameNode) {\n\t\t\tts.forEachChild(node, (child) => visit(child, isExported));\n\t\t\treturn;\n\t\t}\n\n\t\tconst name = nameNode.getText(sourceFile);\n\t\tconst pos = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n\t\tconst rawComment = getLeadingComment(node, sourceFile);\n\t\tconst documentation = rawComment ? parseTSDoc(rawComment, pos.line + 1, fileDir) : undefined;\n\t\tconst tags = documentation?.tags;\n\t\tconst visibility = resolveVisibility(tags);\n\n\t\tconst children: ForgeSymbol[] = [];\n\n\t\t// Walk class members / interface members / enum members\n\t\tif (\n\t\t\tts.isClassDeclaration(node) ||\n\t\t\tts.isInterfaceDeclaration(node) ||\n\t\t\tts.isEnumDeclaration(node)\n\t\t) {\n\t\t\tfor (const member of node.members) {\n\t\t\t\tconst memberKind = kindToString(member.kind);\n\t\t\t\tif (!memberKind) continue;\n\t\t\t\tconst memberName = (member as ts.NamedDeclaration).name?.getText(sourceFile) ?? \"\";\n\t\t\t\tconst memberPos = sourceFile.getLineAndCharacterOfPosition(member.getStart());\n\t\t\t\tconst memberComment = getLeadingComment(member, sourceFile);\n\t\t\t\tconst memberDoc = memberComment\n\t\t\t\t\t? parseTSDoc(memberComment, memberPos.line + 1, fileDir)\n\t\t\t\t\t: undefined;\n\t\t\t\tconst memberTags = memberDoc?.tags;\n\t\t\t\tconst memberVisibility = resolveVisibility(memberTags);\n\n\t\t\t\tchildren.push({\n\t\t\t\t\tname: memberName,\n\t\t\t\t\tkind: memberKind,\n\t\t\t\t\tvisibility: memberVisibility,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tline: memberPos.line + 1,\n\t\t\t\t\tcolumn: memberPos.character,\n\t\t\t\t\tdocumentation: memberDoc,\n\t\t\t\t\tsignature: buildSignature(member as ts.Declaration, checker),\n\t\t\t\t\texported: false,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tsymbols.push({\n\t\t\tname,\n\t\t\tkind,\n\t\t\tvisibility,\n\t\t\tfilePath,\n\t\t\tline: pos.line + 1,\n\t\t\tcolumn: pos.character,\n\t\t\tdocumentation,\n\t\t\tsignature: buildSignature(namedNode as ts.Declaration, checker),\n\t\t\tchildren: children.length > 0 ? children : undefined,\n\t\t\texported: isExported,\n\t\t});\n\t}\n\n\tts.forEachChild(sourceFile, (node) => visit(node, false));\n\treturn symbols;\n}\n\n/**\n * Creates an {@link ASTWalker} configured for the given forge config.\n *\n * The walker uses the TypeScript Compiler API to create a `ts.Program` from\n * the project's tsconfig, then visits every source file to extract exported\n * declarations. TSDoc comments are parsed with `@microsoft/tsdoc` to\n * populate the `documentation` field on each {@link ForgeSymbol}.\n *\n * @param config - The resolved {@link ForgeConfig} for the project.\n * @returns An {@link ASTWalker} instance whose `walk()` method performs the extraction.\n * @example\n * ```typescript\n * import { loadConfig, createWalker } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const walker = createWalker(config);\n * const symbols = walker.walk();\n * console.log(`Found ${symbols.length} symbols`);\n * ```\n * @public\n */\nexport function createWalker(config: ForgeConfig): ASTWalker {\n\treturn {\n\t\twalk(): ForgeSymbol[] {\n\t\t\t// Load tsconfig\n\t\t\tconst tsconfigPath = resolve(config.tsconfig);\n\t\t\tconst configFile = ts.readConfigFile(tsconfigPath, (path) => readFileSync(path, \"utf8\"));\n\n\t\t\tif (configFile.error) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to read tsconfig at ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, \"\\n\")}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst parsedCommandLine = ts.parseJsonConfigFileContent(\n\t\t\t\tconfigFile.config,\n\t\t\t\tts.sys,\n\t\t\t\tresolve(config.rootDir),\n\t\t\t);\n\n\t\t\tconst program = ts.createProgram({\n\t\t\t\trootNames: parsedCommandLine.fileNames,\n\t\t\t\toptions: parsedCommandLine.options,\n\t\t\t});\n\n\t\t\tconst checker = program.getTypeChecker();\n\n\t\t\tconst allSymbols: ForgeSymbol[] = [];\n\n\t\t\tfor (const sourceFile of program.getSourceFiles()) {\n\t\t\t\t// Skip declaration files and node_modules\n\t\t\t\tif (sourceFile.isDeclarationFile || sourceFile.fileName.includes(\"node_modules\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst fileSymbols = extractSymbolsFromFile(sourceFile, checker);\n\t\t\t\tallSymbols.push(...fileSymbols);\n\t\t\t}\n\n\t\t\treturn allSymbols;\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["existsSync","readFileSync","userInfo","join","getCurrentUser","userInfo","join","existsSync","readFileSync","existsSync","join","Visibility","join","existsSync","pkgPath","existsSync","readFileSync","writeFileSync","join","readFileSync","resolve","require","name","pos","rawComment","documentation","tags","visibility","resolve","readFileSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/audit.ts","../src/bypass.ts","../src/config.ts","../src/types.ts","../src/lock.ts","../src/visibility.ts","../src/walker.ts"],"sourcesContent":["/**\n * Append-only audit trail for forge-ts configuration and governance events.\n *\n * Events are stored as JSON Lines in `.forge-audit.jsonl` at the project root.\n * Each line is a single JSON object — the file is never truncated or overwritten.\n *\n * @packageDocumentation\n * @public\n */\n\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport { join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Discriminated event types recorded in the audit trail.\n *\n * @public\n */\nexport type AuditEventType =\n\t| \"config.lock\"\n\t| \"config.unlock\"\n\t| \"config.drift\"\n\t| \"bypass.create\"\n\t| \"bypass.expire\"\n\t| \"rule.change\";\n\n/**\n * A single audit event recorded in the forge-ts audit trail.\n *\n * @public\n */\nexport interface AuditEvent {\n\t/** ISO 8601 timestamp of when the event occurred. */\n\ttimestamp: string;\n\t/** Discriminated event type. */\n\tevent: AuditEventType;\n\t/** OS username of the actor (falls back to \"unknown\"). */\n\tuser: string;\n\t/** Mandatory for lock/unlock/bypass events; optional otherwise. */\n\treason?: string;\n\t/** Event-specific payload. */\n\tdetails: Record<string, unknown>;\n}\n\n/** Default filename for the audit trail. */\nconst AUDIT_FILENAME = \".forge-audit.jsonl\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current OS username, or \"unknown\" if unavailable.\n *\n * @returns The OS username string.\n * @example\n * ```typescript\n * import { getCurrentUser } from \"@forge-ts/core/audit\";\n * const user = getCurrentUser(); // e.g. \"alice\"\n * ```\n * @internal\n */\nexport function getCurrentUser(): string {\n\ttry {\n\t\treturn userInfo().username;\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Appends a single audit event to the `.forge-audit.jsonl` file.\n *\n * Creates the file if it does not exist. The file is strictly append-only —\n * existing content is never modified or truncated.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param event - The audit event to record.\n * @example\n * ```typescript\n * import { appendAuditEvent } from \"@forge-ts/core\";\n * appendAuditEvent(\"/path/to/project\", {\n * timestamp: new Date().toISOString(),\n * event: \"config.lock\",\n * user: \"alice\",\n * reason: \"Stabilize v2 config\",\n * details: { hash: \"abc123\" },\n * });\n * ```\n * @public\n */\nexport function appendAuditEvent(rootDir: string, event: AuditEvent): void {\n\tconst filePath = join(rootDir, AUDIT_FILENAME);\n\tappendFileSync(filePath, `${JSON.stringify(event)}\\n`, \"utf-8\");\n}\n\n/**\n * Options for reading the audit log.\n *\n * @public\n */\nexport interface ReadAuditOptions {\n\t/** Maximum number of events to return. */\n\tlimit?: number;\n\t/** Filter to a single event type. */\n\teventType?: AuditEventType;\n}\n\n/**\n * Reads the `.forge-audit.jsonl` file and returns parsed audit events.\n *\n * Returns newest events first. If the file does not exist, returns an empty\n * array.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param options - Optional limit and event type filter.\n * @returns Array of audit events, newest first.\n * @example\n * ```typescript\n * import { readAuditLog } from \"@forge-ts/core\";\n * const events = readAuditLog(\"/path/to/project\", { limit: 10 });\n * console.log(events.length); // up to 10\n * ```\n * @public\n */\nexport function readAuditLog(rootDir: string, options?: ReadAuditOptions): AuditEvent[] {\n\tconst filePath = join(rootDir, AUDIT_FILENAME);\n\tif (!existsSync(filePath)) {\n\t\treturn [];\n\t}\n\n\tconst raw = readFileSync(filePath, \"utf-8\");\n\tconst lines = raw.split(\"\\n\").filter((line) => line.trim().length > 0);\n\n\tlet events: AuditEvent[] = lines.map((line) => JSON.parse(line) as AuditEvent);\n\n\t// Filter by event type if specified\n\tif (options?.eventType) {\n\t\tevents = events.filter((e) => e.event === options.eventType);\n\t}\n\n\t// Newest first\n\tevents.reverse();\n\n\t// Apply limit\n\tif (options?.limit !== undefined && options.limit >= 0) {\n\t\tevents = events.slice(0, options.limit);\n\t}\n\n\treturn events;\n}\n\n/**\n * Formats a single audit event as a human-readable string.\n *\n * @param event - The audit event to format.\n * @returns A single-line human-readable representation.\n * @example\n * ```typescript\n * import { formatAuditEvent } from \"@forge-ts/core\";\n * const line = formatAuditEvent({\n * timestamp: \"2026-03-21T12:00:00.000Z\",\n * event: \"config.lock\",\n * user: \"alice\",\n * reason: \"Stabilize v2 config\",\n * details: { hash: \"abc123\" },\n * });\n * console.log(line);\n * // \"[2026-03-21T12:00:00.000Z] config.lock by alice — Stabilize v2 config {hash: abc123}\"\n * ```\n * @public\n */\nexport function formatAuditEvent(event: AuditEvent): string {\n\tconst reasonPart = event.reason ? ` — ${event.reason}` : \"\";\n\tconst detailKeys = Object.keys(event.details);\n\tconst detailPart = detailKeys.length > 0 ? ` ${JSON.stringify(event.details)}` : \"\";\n\n\treturn `[${event.timestamp}] ${event.event} by ${event.user}${reasonPart}${detailPart}`;\n}\n","/**\n * Bypass budget system for forge-ts config governance.\n *\n * Allows agents to temporarily bypass locked rules with a limited daily budget.\n * Each bypass requires a mandatory justification and expires after a configurable\n * duration. All bypass events are recorded in the audit trail.\n *\n * Bypass records are stored in `.forge-bypass.json` at the project root as a\n * JSON array of {@link BypassRecord} objects.\n *\n * @packageDocumentation\n * @public\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport { join } from \"node:path\";\nimport { appendAuditEvent } from \"./audit.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration for the bypass budget system.\n *\n * @public\n */\nexport interface BypassConfig {\n\t/** Maximum number of bypasses allowed per calendar day. Default: 3 */\n\tdailyBudget: number;\n\t/** Duration in hours before a bypass automatically expires. Default: 24 */\n\tdurationHours: number;\n}\n\n/**\n * A single bypass record stored in `.forge-bypass.json`.\n *\n * @public\n */\nexport interface BypassRecord {\n\t/** Unique identifier for this bypass. */\n\tid: string;\n\t/** ISO 8601 timestamp when the bypass was created. */\n\tcreatedAt: string;\n\t/** ISO 8601 timestamp when the bypass expires. */\n\texpiresAt: string;\n\t/** Mandatory justification for why the bypass was created. */\n\treason: string;\n\t/** Specific rule code bypassed (e.g., \"E009\"), or \"all\" for a blanket bypass. */\n\trule: string;\n\t/** OS username of the actor who created the bypass. */\n\tuser: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default filename for the bypass records. */\nconst BYPASS_FILENAME = \".forge-bypass.json\";\n\n/** Default bypass configuration values. */\nconst DEFAULT_BYPASS_CONFIG: BypassConfig = {\n\tdailyBudget: 3,\n\tdurationHours: 24,\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the current OS username, or \"unknown\" if unavailable.\n *\n * @returns The OS username string.\n * @internal\n */\nfunction getCurrentUser(): string {\n\ttry {\n\t\treturn userInfo().username;\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n}\n\n/**\n * Reads the raw bypass records from `.forge-bypass.json`.\n * Returns an empty array if the file does not exist or contains invalid JSON.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns Array of all bypass records (including expired).\n * @internal\n */\nfunction readBypassFile(rootDir: string): BypassRecord[] {\n\tconst filePath = join(rootDir, BYPASS_FILENAME);\n\tif (!existsSync(filePath)) {\n\t\treturn [];\n\t}\n\ttry {\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\treturn JSON.parse(raw) as BypassRecord[];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Writes bypass records to `.forge-bypass.json`.\n *\n * @param rootDir - Absolute path to the project root.\n * @param records - Array of bypass records to write.\n * @internal\n */\nfunction writeBypassFile(rootDir: string, records: BypassRecord[]): void {\n\tconst filePath = join(rootDir, BYPASS_FILENAME);\n\twriteFileSync(filePath, `${JSON.stringify(records, null, 2)}\\n`, \"utf-8\");\n}\n\n/**\n * Merges user-provided bypass config with defaults.\n *\n * @param config - Partial bypass configuration.\n * @returns Fully-resolved bypass configuration.\n * @internal\n */\nfunction resolveConfig(config?: Partial<BypassConfig>): BypassConfig {\n\treturn {\n\t\t...DEFAULT_BYPASS_CONFIG,\n\t\t...config,\n\t};\n}\n\n/**\n * Returns the start of the current UTC day as a Date object.\n *\n * @returns Date representing midnight UTC of the current day.\n * @internal\n */\nfunction startOfToday(): Date {\n\tconst now = new Date();\n\treturn new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a new bypass record, writes it to `.forge-bypass.json`, and appends\n * an audit event.\n *\n * Throws an error if the daily budget is exhausted.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param reason - Mandatory justification for the bypass.\n * @param rule - Specific rule code to bypass (e.g., \"E009\"), or \"all\". Defaults to \"all\".\n * @param config - Optional bypass budget configuration overrides.\n * @returns The created bypass record.\n * @throws Error when the daily bypass budget is exhausted.\n * @example\n * ```typescript\n * import { createBypass } from \"@forge-ts/core\";\n * const bypass = createBypass(\"/path/to/project\", \"hotfix for release\", \"E009\");\n * console.log(bypass.id); // unique bypass ID\n * ```\n * @public\n */\nexport function createBypass(\n\trootDir: string,\n\treason: string,\n\trule?: string,\n\tconfig?: Partial<BypassConfig>,\n): BypassRecord {\n\tconst resolved = resolveConfig(config);\n\tconst remaining = getRemainingBudget(rootDir, config);\n\n\tif (remaining <= 0) {\n\t\tthrow new Error(\n\t\t\t`Bypass budget exhausted: ${resolved.dailyBudget}/${resolved.dailyBudget} bypasses used today. ` +\n\t\t\t\t\"Wait until tomorrow or increase bypass.dailyBudget in your forge-ts config.\",\n\t\t);\n\t}\n\n\tconst now = new Date();\n\tconst expiresAt = new Date(now.getTime() + resolved.durationHours * 60 * 60 * 1000);\n\n\tconst record: BypassRecord = {\n\t\tid: randomUUID(),\n\t\tcreatedAt: now.toISOString(),\n\t\texpiresAt: expiresAt.toISOString(),\n\t\treason,\n\t\trule: rule ?? \"all\",\n\t\tuser: getCurrentUser(),\n\t};\n\n\t// Read existing records and append the new one\n\tconst records = readBypassFile(rootDir);\n\trecords.push(record);\n\twriteBypassFile(rootDir, records);\n\n\t// Append audit event\n\tappendAuditEvent(rootDir, {\n\t\ttimestamp: record.createdAt,\n\t\tevent: \"bypass.create\",\n\t\tuser: record.user,\n\t\treason,\n\t\tdetails: {\n\t\t\tbypassId: record.id,\n\t\t\trule: record.rule,\n\t\t\texpiresAt: record.expiresAt,\n\t\t\tdurationHours: resolved.durationHours,\n\t\t},\n\t});\n\n\treturn record;\n}\n\n/**\n * Returns all currently active (non-expired) bypass records.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @returns Array of active bypass records.\n * @example\n * ```typescript\n * import { getActiveBypasses } from \"@forge-ts/core\";\n * const active = getActiveBypasses(\"/path/to/project\");\n * console.log(`${active.length} active bypass(es)`);\n * ```\n * @public\n */\nexport function getActiveBypasses(rootDir: string): BypassRecord[] {\n\tconst records = readBypassFile(rootDir);\n\tconst now = new Date();\n\treturn records.filter((r) => new Date(r.expiresAt) > now);\n}\n\n/**\n * Checks whether a specific rule has an active bypass.\n *\n * A rule is considered bypassed if there is an active bypass with the exact\n * rule code or an \"all\" bypass.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param ruleCode - The rule code to check (e.g., \"E009\", \"E010\").\n * @returns `true` if the rule is currently bypassed.\n * @example\n * ```typescript\n * import { isRuleBypassed } from \"@forge-ts/core\";\n * if (isRuleBypassed(\"/path/to/project\", \"E009\")) {\n * console.log(\"E009 is currently bypassed\");\n * }\n * ```\n * @public\n */\nexport function isRuleBypassed(rootDir: string, ruleCode: string): boolean {\n\tconst active = getActiveBypasses(rootDir);\n\treturn active.some((r) => r.rule === ruleCode || r.rule === \"all\");\n}\n\n/**\n * Returns the number of bypass budget slots remaining for today.\n *\n * Counts bypasses created today (UTC) against the configured daily budget.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @param config - Optional bypass budget configuration overrides.\n * @returns Number of remaining bypass slots for today.\n * @example\n * ```typescript\n * import { getRemainingBudget } from \"@forge-ts/core\";\n * const remaining = getRemainingBudget(\"/path/to/project\");\n * console.log(`${remaining} bypass(es) remaining today`);\n * ```\n * @public\n */\nexport function getRemainingBudget(rootDir: string, config?: Partial<BypassConfig>): number {\n\tconst resolved = resolveConfig(config);\n\tconst records = readBypassFile(rootDir);\n\tconst todayStart = startOfToday();\n\n\tconst todayCount = records.filter((r) => new Date(r.createdAt) >= todayStart).length;\n\n\treturn Math.max(0, resolved.dailyBudget - todayCount);\n}\n\n/**\n * Removes expired bypass records from `.forge-bypass.json`.\n *\n * Also appends a `bypass.expire` audit event for each expired record removed.\n *\n * @param rootDir - Absolute path to the project root directory.\n * @returns The number of expired records removed.\n * @example\n * ```typescript\n * import { expireOldBypasses } from \"@forge-ts/core\";\n * const removed = expireOldBypasses(\"/path/to/project\");\n * console.log(`${removed} expired bypass(es) removed`);\n * ```\n * @public\n */\nexport function expireOldBypasses(rootDir: string): number {\n\tconst records = readBypassFile(rootDir);\n\tconst now = new Date();\n\n\tconst active = records.filter((r) => new Date(r.expiresAt) > now);\n\tconst expired = records.filter((r) => new Date(r.expiresAt) <= now);\n\n\tif (expired.length === 0) {\n\t\treturn 0;\n\t}\n\n\t// Write back only active records\n\twriteBypassFile(rootDir, active);\n\n\t// Append audit events for each expired bypass\n\tfor (const record of expired) {\n\t\tappendAuditEvent(rootDir, {\n\t\t\ttimestamp: now.toISOString(),\n\t\t\tevent: \"bypass.expire\",\n\t\t\tuser: record.user,\n\t\t\tdetails: {\n\t\t\t\tbypassId: record.id,\n\t\t\t\trule: record.rule,\n\t\t\t\tcreatedAt: record.createdAt,\n\t\t\t\texpiresAt: record.expiresAt,\n\t\t\t},\n\t\t});\n\t}\n\n\treturn expired.length;\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { type ForgeConfig, Visibility } from \"./types.js\";\n\n/**\n * Type-safe helper for defining a partial forge-ts configuration.\n *\n * Only include the settings you want to override — everything else\n * inherits sensible defaults via `loadConfig()`.\n *\n * @param config - Partial configuration overrides.\n * @returns The same object (identity function for type checking).\n * @example\n * ```typescript\n * import { defineConfig } from \"@forge-ts/core\";\n *\n * export default defineConfig({\n * outDir: \"docs\",\n * enforce: { strict: true },\n * gen: { ssgTarget: \"mintlify\" },\n * });\n * ```\n * @public\n */\nexport function defineConfig(config: Partial<ForgeConfig>): Partial<ForgeConfig> {\n\treturn config;\n}\n\n/**\n * Constructs a sensible default {@link ForgeConfig} rooted at `rootDir`.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns A fully-populated default configuration.\n * @example\n * ```typescript\n * import { defaultConfig } from \"@forge-ts/core\";\n * const config = defaultConfig(\"/path/to/project\");\n * console.log(config.enforce.enabled); // true\n * ```\n * @public\n */\nexport function defaultConfig(rootDir: string): ForgeConfig {\n\treturn {\n\t\trootDir,\n\t\ttsconfig: join(rootDir, \"tsconfig.json\"),\n\t\toutDir: join(rootDir, \"docs\"),\n\t\tenforce: {\n\t\t\tenabled: true,\n\t\t\tminVisibility: Visibility.Public,\n\t\t\tstrict: false,\n\t\t\trules: {\n\t\t\t\t\"require-summary\": \"error\",\n\t\t\t\t\"require-param\": \"error\",\n\t\t\t\t\"require-returns\": \"error\",\n\t\t\t\t\"require-example\": \"error\",\n\t\t\t\t\"require-package-doc\": \"warn\",\n\t\t\t\t\"require-class-member-doc\": \"error\",\n\t\t\t\t\"require-interface-member-doc\": \"error\",\n\t\t\t\t\"require-tsdoc-syntax\": \"warn\",\n\t\t\t\t\"require-remarks\": \"error\",\n\t\t\t\t\"require-default-value\": \"warn\",\n\t\t\t\t\"require-type-param\": \"error\",\n\t\t\t\t\"require-see\": \"warn\",\n\t\t\t\t\"require-release-tag\": \"error\",\n\t\t\t\t\"require-fresh-guides\": \"warn\",\n\t\t\t\t\"require-guide-coverage\": \"warn\",\n\t\t\t\t\"require-internal-boundary\": \"error\",\n\t\t\t\t\"require-route-response\": \"warn\",\n\t\t\t\t\"require-inheritdoc-source\": \"warn\",\n\t\t\t\t\"require-migration-path\": \"warn\",\n\t\t\t\t\"require-since\": \"warn\",\n\t\t\t\t\"require-fresh-examples\": \"warn\",\n\t\t\t\t\"require-no-ts-ignore\": \"error\",\n\t\t\t\t\"require-no-any-in-api\": \"warn\",\n\t\t\t\t\"require-fresh-link-text\": \"warn\",\n\t\t\t},\n\t\t},\n\t\tdoctest: {\n\t\t\tenabled: true,\n\t\t\tcacheDir: join(rootDir, \".cache\", \"doctest\"),\n\t\t},\n\t\tapi: {\n\t\t\tenabled: false,\n\t\t\topenapi: false,\n\t\t\topenapiPath: join(rootDir, \"docs\", \"openapi.json\"),\n\t\t},\n\t\tgen: {\n\t\t\tenabled: true,\n\t\t\tformats: [\"markdown\"],\n\t\t\tllmsTxt: true,\n\t\t\treadmeSync: false,\n\t\t},\n\t\tskill: {},\n\t\tbypass: {\n\t\t\tdailyBudget: 3,\n\t\t\tdurationHours: 24,\n\t\t},\n\t\ttsdoc: {\n\t\t\twriteConfig: true,\n\t\t\tcustomTags: [],\n\t\t\tenforce: {\n\t\t\t\tcore: \"error\",\n\t\t\t\textended: \"warn\",\n\t\t\t\tdiscretionary: \"off\",\n\t\t\t},\n\t\t},\n\t\tguides: {\n\t\t\tenabled: true,\n\t\t\tautoDiscover: true,\n\t\t\tcustom: [],\n\t\t},\n\t\tguards: {\n\t\t\ttsconfig: {\n\t\t\t\tenabled: true,\n\t\t\t\trequiredFlags: [\"strict\", \"strictNullChecks\", \"noImplicitAny\"],\n\t\t\t},\n\t\t\tbiome: {\n\t\t\t\tenabled: false,\n\t\t\t\tlockedRules: [],\n\t\t\t},\n\t\t\tpackageJson: {\n\t\t\t\tenabled: true,\n\t\t\t\tminNodeVersion: \"22.0.0\",\n\t\t\t\trequiredFields: [\"type\", \"engines\"],\n\t\t\t},\n\t\t},\n\t\tproject: {},\n\t};\n}\n\n/**\n * Known top-level keys in {@link ForgeConfig}.\n * Used to warn about unrecognised config keys that are silently ignored.\n * @internal\n */\nconst KNOWN_TOP_KEYS = new Set([\n\t\"rootDir\",\n\t\"tsconfig\",\n\t\"outDir\",\n\t\"enforce\",\n\t\"doctest\",\n\t\"api\",\n\t\"gen\",\n\t\"skill\",\n\t\"bypass\",\n\t\"tsdoc\",\n\t\"guides\",\n\t\"guards\",\n\t\"project\",\n]);\n\n/**\n * Known keys within `enforce.rules`.\n * @internal\n */\nconst KNOWN_RULE_KEYS = new Set([\n\t\"require-summary\",\n\t\"require-param\",\n\t\"require-returns\",\n\t\"require-example\",\n\t\"require-package-doc\",\n\t\"require-class-member-doc\",\n\t\"require-interface-member-doc\",\n\t\"require-tsdoc-syntax\",\n\t\"require-remarks\",\n\t\"require-default-value\",\n\t\"require-type-param\",\n\t\"require-see\",\n\t\"require-release-tag\",\n\t\"require-fresh-guides\",\n\t\"require-guide-coverage\",\n\t\"require-internal-boundary\",\n\t\"require-route-response\",\n\t\"require-inheritdoc-source\",\n\t\"require-migration-path\",\n\t\"require-since\",\n\t\"require-fresh-examples\",\n\t\"require-no-ts-ignore\",\n\t\"require-no-any-in-api\",\n\t\"require-fresh-link-text\",\n]);\n\n/**\n * Known keys within `tsdoc`.\n * @internal\n */\nconst KNOWN_TSDOC_KEYS = new Set([\"writeConfig\", \"customTags\", \"enforce\"]);\n\n/**\n * Known keys within `tsdoc.enforce`.\n * @internal\n */\nconst KNOWN_TSDOC_ENFORCE_KEYS = new Set([\"core\", \"extended\", \"discretionary\"]);\n\n/**\n * Known keys within `guides`.\n * @internal\n */\nconst KNOWN_GUIDES_KEYS = new Set([\"enabled\", \"autoDiscover\", \"custom\"]);\n\n/**\n * Known keys within `guards`.\n * @internal\n */\nconst KNOWN_GUARDS_KEYS = new Set([\"tsconfig\", \"biome\", \"packageJson\"]);\n\n/**\n * Known keys within `guards.tsconfig`.\n * @internal\n */\nconst KNOWN_GUARDS_TSCONFIG_KEYS = new Set([\"enabled\", \"requiredFlags\"]);\n\n/**\n * Known keys within `guards.biome`.\n * @internal\n */\nconst KNOWN_GUARDS_BIOME_KEYS = new Set([\"enabled\", \"lockedRules\"]);\n\n/**\n * Known keys within `guards.packageJson`.\n * @internal\n */\nconst KNOWN_GUARDS_PACKAGE_JSON_KEYS = new Set([\"enabled\", \"minNodeVersion\", \"requiredFields\"]);\n\n/**\n * Validates an object against a set of known keys and collects warnings.\n * @internal\n */\nfunction validateKnownKeys(\n\tobj: Record<string, unknown>,\n\tknownKeys: Set<string>,\n\tsection: string,\n\twarnings: string[],\n): void {\n\tfor (const key of Object.keys(obj)) {\n\t\tif (!knownKeys.has(key)) {\n\t\t\twarnings.push(`Unknown key \"${key}\" in ${section} — ignored.`);\n\t\t}\n\t}\n}\n\n/**\n * Collects warnings about unknown keys in user config.\n * Also emits to stderr for TTY consumers.\n * Returns the warnings array so it can be attached to the config for\n * agents that only see structured JSON output.\n * @internal\n */\nfunction collectUnknownKeyWarnings(partial: Partial<ForgeConfig>): string[] {\n\tconst warnings: string[] = [];\n\tfor (const key of Object.keys(partial)) {\n\t\tif (!KNOWN_TOP_KEYS.has(key)) {\n\t\t\twarnings.push(`Unknown config key \"${key}\" — ignored.`);\n\t\t}\n\t}\n\tif (partial.enforce?.rules) {\n\t\tfor (const key of Object.keys(partial.enforce.rules)) {\n\t\t\tif (!KNOWN_RULE_KEYS.has(key)) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Unknown enforce rule \"${key}\" — ignored. Valid rules: ${[...KNOWN_RULE_KEYS].join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\tif (partial.tsdoc) {\n\t\tvalidateKnownKeys(\n\t\t\tpartial.tsdoc as unknown as Record<string, unknown>,\n\t\t\tKNOWN_TSDOC_KEYS,\n\t\t\t\"tsdoc\",\n\t\t\twarnings,\n\t\t);\n\t\tif (partial.tsdoc.enforce) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.tsdoc.enforce as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_TSDOC_ENFORCE_KEYS,\n\t\t\t\t\"tsdoc.enforce\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t}\n\tif (partial.guides) {\n\t\tvalidateKnownKeys(\n\t\t\tpartial.guides as unknown as Record<string, unknown>,\n\t\t\tKNOWN_GUIDES_KEYS,\n\t\t\t\"guides\",\n\t\t\twarnings,\n\t\t);\n\t}\n\tif (partial.guards) {\n\t\tvalidateKnownKeys(\n\t\t\tpartial.guards as unknown as Record<string, unknown>,\n\t\t\tKNOWN_GUARDS_KEYS,\n\t\t\t\"guards\",\n\t\t\twarnings,\n\t\t);\n\t\tif (partial.guards.tsconfig) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.guards.tsconfig as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_GUARDS_TSCONFIG_KEYS,\n\t\t\t\t\"guards.tsconfig\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t\tif (partial.guards.biome) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.guards.biome as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_GUARDS_BIOME_KEYS,\n\t\t\t\t\"guards.biome\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t\tif (partial.guards.packageJson) {\n\t\t\tvalidateKnownKeys(\n\t\t\t\tpartial.guards.packageJson as unknown as Record<string, unknown>,\n\t\t\t\tKNOWN_GUARDS_PACKAGE_JSON_KEYS,\n\t\t\t\t\"guards.packageJson\",\n\t\t\t\twarnings,\n\t\t\t);\n\t\t}\n\t}\n\tfor (const w of warnings) {\n\t\tconsole.error(`[forge-ts] warning: ${w}`);\n\t}\n\treturn warnings;\n}\n\n/**\n * Merges a partial user config with the defaults so every field is present.\n *\n * @param rootDir - Absolute path to the project root.\n * @param partial - Partial config from the user's config file.\n * @returns A fully-populated {@link ForgeConfig}.\n * @internal\n */\nfunction mergeWithDefaults(rootDir: string, partial: Partial<ForgeConfig>): ForgeConfig {\n\tconst warnings = collectUnknownKeyWarnings(partial);\n\tconst defaults = defaultConfig(rootDir);\n\tconst config: ForgeConfig = {\n\t\t...defaults,\n\t\t...partial,\n\t\tenforce: {\n\t\t\t...defaults.enforce,\n\t\t\t...partial.enforce,\n\t\t\trules: { ...defaults.enforce.rules, ...partial.enforce?.rules },\n\t\t},\n\t\tdoctest: { ...defaults.doctest, ...partial.doctest },\n\t\tapi: { ...defaults.api, ...partial.api },\n\t\tgen: { ...defaults.gen, ...partial.gen },\n\t\tskill: { ...defaults.skill, ...partial.skill },\n\t\tbypass: { ...defaults.bypass, ...partial.bypass },\n\t\tguides: { ...defaults.guides, ...partial.guides },\n\t\ttsdoc: {\n\t\t\t...defaults.tsdoc,\n\t\t\t...partial.tsdoc,\n\t\t\tenforce: { ...defaults.tsdoc.enforce, ...partial.tsdoc?.enforce },\n\t\t},\n\t\tguards: {\n\t\t\t...defaults.guards,\n\t\t\t...partial.guards,\n\t\t\ttsconfig: { ...defaults.guards.tsconfig, ...partial.guards?.tsconfig },\n\t\t\tbiome: { ...defaults.guards.biome, ...partial.guards?.biome },\n\t\t\tpackageJson: { ...defaults.guards.packageJson, ...partial.guards?.packageJson },\n\t\t},\n\t\tproject: { ...defaults.project, ...partial.project },\n\t};\n\tif (warnings.length > 0) {\n\t\tconfig._configWarnings = warnings;\n\t}\n\treturn config;\n}\n\n/**\n * Extracts repository URL from a package.json repository field.\n * Handles both string and object forms.\n * @internal\n */\nfunction extractRepoUrl(\n\trepo: string | { type?: string; url?: string } | undefined,\n): string | undefined {\n\tif (!repo) return undefined;\n\tconst raw = typeof repo === \"string\" ? repo : repo.url;\n\tif (!raw) return undefined;\n\t// Normalize git+https://... and .git suffix\n\treturn raw.replace(/^git\\+/, \"\").replace(/\\.git$/, \"\");\n}\n\n/**\n * Attempts to load a TypeScript or JavaScript config file via dynamic import.\n *\n * @param filePath - Absolute path to the config file.\n * @returns The default export of the config module, or `null` on failure.\n * @internal\n */\nasync function loadModuleConfig(filePath: string): Promise<Partial<ForgeConfig> | null> {\n\ttry {\n\t\tconst fileUrl = pathToFileURL(filePath).href;\n\t\tconst mod = (await import(fileUrl)) as {\n\t\t\tdefault?: Partial<ForgeConfig>;\n\t\t};\n\t\treturn mod.default ?? null;\n\t} catch (err) {\n\t\t// File exists but failed to import — warn instead of silently falling back.\n\t\t// Common cause: .ts config in a CommonJS project without \"type\": \"module\".\n\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\tconsole.error(\n\t\t\t`[forge-ts] warning: failed to load config file \"${filePath}\" — ${msg.split(\"\\n\")[0]}`,\n\t\t);\n\t\treturn null;\n\t}\n}\n\n/**\n * Minimal shape of a `package.json` file relevant to forge-ts config loading.\n * @internal\n */\ninterface PackageJson {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\thomepage?: string;\n\trepository?: string | { type?: string; url?: string };\n\tbin?: string | Record<string, string>;\n\tscripts?: Record<string, string>;\n\tkeywords?: string[];\n\t\"forge-ts\"?: Partial<ForgeConfig>;\n}\n\n/**\n * Attempts to read the `\"forge-ts\"` key from a `package.json` file.\n *\n * @param pkgPath - Absolute path to `package.json`.\n * @returns The value of the `\"forge-ts\"` key, or `null` if absent.\n * @internal\n */\nasync function loadPackageJsonConfig(pkgPath: string): Promise<Partial<ForgeConfig> | null> {\n\ttry {\n\t\tconst raw = await readFile(pkgPath, \"utf8\");\n\t\tconst pkg = JSON.parse(raw) as PackageJson;\n\t\tconst key = pkg[\"forge-ts\"];\n\t\tif (key) {\n\t\t\treturn key;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Loads the forge-ts configuration for a project.\n *\n * Resolution order:\n * 1. `<rootDir>/forge-ts.config.ts`\n * 2. `<rootDir>/forge-ts.config.js`\n * 3. `\"forge-ts\"` key inside `<rootDir>/package.json`\n * 4. Built-in defaults (returned when none of the above is found)\n *\n * @param rootDir - The project root to search for config. Defaults to `process.cwd()`.\n * @returns A fully-resolved {@link ForgeConfig}.\n * @example\n * ```typescript\n * import { loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig(\"/path/to/project\");\n * // config is fully resolved with defaults\n * ```\n * @public\n */\nexport async function loadConfig(rootDir?: string): Promise<ForgeConfig> {\n\tconst root = resolve(rootDir ?? process.cwd());\n\n\tlet config: ForgeConfig;\n\n\tconst candidates = [join(root, \"forge-ts.config.ts\"), join(root, \"forge-ts.config.js\")];\n\tlet found = false;\n\tconst loadWarnings: string[] = [];\n\n\tfor (const candidate of candidates) {\n\t\tif (existsSync(candidate)) {\n\t\t\tconst partial = await loadModuleConfig(candidate);\n\t\t\tif (partial) {\n\t\t\t\tconfig = mergeWithDefaults(root, partial);\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// Config file exists but failed to load — track the warning\n\t\t\tloadWarnings.push(\n\t\t\t\t`Config file \"${candidate}\" exists but could not be loaded. Check that your project has \"type\": \"module\" in package.json or use a .js config file.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (!found) {\n\t\tconst pkgPath = join(root, \"package.json\");\n\t\tif (existsSync(pkgPath)) {\n\t\t\tconst partial = await loadPackageJsonConfig(pkgPath);\n\t\t\tif (partial) {\n\t\t\t\tconfig = mergeWithDefaults(root, partial);\n\t\t\t} else {\n\t\t\t\tconfig = defaultConfig(root);\n\t\t\t}\n\t\t} else {\n\t\t\tconfig = defaultConfig(root);\n\t\t}\n\t} else {\n\t\t// biome-ignore lint: config is always set when found=true\n\t\tconfig = config!;\n\t}\n\n\t// Auto-detect project metadata from package.json if not explicitly set\n\tconst pkgPath = join(root, \"package.json\");\n\tif (existsSync(pkgPath)) {\n\t\ttry {\n\t\t\tconst raw = await readFile(pkgPath, \"utf8\");\n\t\t\tconst pkg = JSON.parse(raw) as PackageJson;\n\t\t\tif (!config.project.repository) {\n\t\t\t\tconfig.project.repository = extractRepoUrl(pkg.repository);\n\t\t\t}\n\t\t\tif (!config.project.homepage) {\n\t\t\t\tconfig.project.homepage = pkg.homepage;\n\t\t\t}\n\t\t\tif (!config.project.packageName) {\n\t\t\t\tconfig.project.packageName = pkg.name;\n\t\t\t}\n\t\t\tif (!config.project.description) {\n\t\t\t\tconfig.project.description = pkg.description;\n\t\t\t}\n\t\t\tif (!config.project.version) {\n\t\t\t\tconfig.project.version = pkg.version;\n\t\t\t}\n\t\t\tif (!config.project.bin) {\n\t\t\t\tif (typeof pkg.bin === \"string\") {\n\t\t\t\t\tconst binName = pkg.name?.replace(/^@[^/]+\\//, \"\") ?? \"cli\";\n\t\t\t\t\tconfig.project.bin = { [binName]: pkg.bin };\n\t\t\t\t} else if (pkg.bin) {\n\t\t\t\t\tconfig.project.bin = pkg.bin;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!config.project.scripts) {\n\t\t\t\tconfig.project.scripts = pkg.scripts;\n\t\t\t}\n\t\t\tif (!config.project.keywords) {\n\t\t\t\tconfig.project.keywords = pkg.keywords;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore parse errors\n\t\t}\n\t}\n\n\t// Attach any config load warnings so CLI commands can surface them\n\tif (loadWarnings.length > 0) {\n\t\tconfig._configWarnings = [...(config._configWarnings ?? []), ...loadWarnings];\n\t}\n\n\treturn config;\n}\n","/**\n * Visibility levels for exported symbols.\n * Derived from TSDoc release tags (@public, @beta, @internal).\n * @public\n */\nexport enum Visibility {\n\tPublic = \"public\",\n\tBeta = \"beta\",\n\tInternal = \"internal\",\n\tPrivate = \"private\",\n}\n\n/**\n * A single extracted and annotated symbol from the TypeScript AST.\n * @public\n */\nexport interface ForgeSymbol {\n\t/** The declared name of the symbol. */\n\tname: string;\n\t/** The syntactic kind of the symbol. */\n\tkind: \"function\" | \"class\" | \"interface\" | \"type\" | \"enum\" | \"variable\" | \"method\" | \"property\";\n\t/** Resolved visibility from TSDoc release tags. */\n\tvisibility: Visibility;\n\t/** Absolute path to the source file. */\n\tfilePath: string;\n\t/** 1-based line number of the declaration. */\n\tline: number;\n\t/** 0-based column of the declaration. */\n\tcolumn: number;\n\t/** Parsed TSDoc documentation, if present. */\n\tdocumentation?: {\n\t\tsummary?: string;\n\t\tparams?: Array<{ name: string; description: string; type?: string }>;\n\t\treturns?: { description: string; type?: string };\n\t\tthrows?: Array<{ type?: string; description: string }>;\n\t\texamples?: Array<{ code: string; language: string; line: number }>;\n\t\ttags?: Record<string, string[]>;\n\t\tdeprecated?: string;\n\t\t/** {@link} cross-references found in this symbol's TSDoc. */\n\t\tlinks?: Array<{ target: string; line: number; text?: string }>;\n\t\t/** TSDoc parser messages (syntax warnings/errors) from @microsoft/tsdoc. */\n\t\tparseMessages?: Array<{ messageId: string; text: string; line: number }>;\n\t};\n\t/** Human-readable type signature of the symbol. */\n\tsignature?: string;\n\t/** Child symbols (e.g., class members, enum values). */\n\tchildren?: ForgeSymbol[];\n\t/** Whether this symbol is part of the public module exports. */\n\texported: boolean;\n}\n\n/**\n * Severity level for an individual enforcement rule.\n * - `\"error\"` — violation fails the build.\n * - `\"warn\"` — violation is reported but does not fail the build.\n * - `\"off\"` — rule is disabled entirely.\n * @public\n */\nexport type RuleSeverity = \"error\" | \"warn\" | \"off\";\n\n/**\n * Per-rule severity configuration for the TSDoc enforcer.\n * Each key corresponds to one of the E001–E007 rule codes.\n * @public\n */\nexport interface EnforceRules {\n\t/** E001: Exported symbol missing TSDoc summary. */\n\t\"require-summary\": RuleSeverity;\n\t/** E002: Function parameter missing @param tag. */\n\t\"require-param\": RuleSeverity;\n\t/** E003: Non-void function missing @returns tag. */\n\t\"require-returns\": RuleSeverity;\n\t/** E004: Exported function missing @example block. */\n\t\"require-example\": RuleSeverity;\n\t/** E005: Entry point missing @packageDocumentation. */\n\t\"require-package-doc\": RuleSeverity;\n\t/** E006: Class member missing documentation. */\n\t\"require-class-member-doc\": RuleSeverity;\n\t/** E007: Interface/type member missing documentation. */\n\t\"require-interface-member-doc\": RuleSeverity;\n\t/** W006: TSDoc syntax parse error (invalid tag, malformed block, etc.). */\n\t\"require-tsdoc-syntax\": RuleSeverity;\n\t/** E013: Exported function/class is missing a @remarks block. */\n\t\"require-remarks\": RuleSeverity;\n\t/** E014: Optional property with default is missing @defaultValue. */\n\t\"require-default-value\": RuleSeverity;\n\t/** E015: Generic symbol is missing @typeParam for its type parameters. */\n\t\"require-type-param\": RuleSeverity;\n\t/** W005: Symbol references other symbols via {@link} but has no @see tags. */\n\t\"require-see\": RuleSeverity;\n\t/** E016: Exported symbol is missing a release tag (@public, @beta, @internal). */\n\t\"require-release-tag\": RuleSeverity;\n\t/** W007: Guide FORGE:AUTO section references a symbol that no longer exists or has changed. */\n\t\"require-fresh-guides\": RuleSeverity;\n\t/** W008: Exported public symbol is not mentioned in any guide page. */\n\t\"require-guide-coverage\": RuleSeverity;\n\t/** E017: @internal symbol re-exported through public barrel (index.ts). */\n\t\"require-internal-boundary\": RuleSeverity;\n\t/** E018: @route-tagged function missing @response tag. */\n\t\"require-route-response\": RuleSeverity;\n\t/** W009: {@inheritDoc} references a symbol that does not exist. */\n\t\"require-inheritdoc-source\": RuleSeverity;\n\t/** W010: @breaking without @migration path. */\n\t\"require-migration-path\": RuleSeverity;\n\t/** W011: New public export missing @since version tag. */\n\t\"require-since\": RuleSeverity;\n\t/** W013: @example block may be stale — function call arg count mismatches parameter count. */\n\t\"require-fresh-examples\": RuleSeverity;\n\t/** E019: Non-test file contains ts-ignore or ts-expect-error directive. */\n\t\"require-no-ts-ignore\": RuleSeverity;\n\t/** E020: Exported symbol has `any` in its public API signature. */\n\t\"require-no-any-in-api\": RuleSeverity;\n\t/** W012: {@link} display text appears stale relative to target summary. */\n\t\"require-fresh-link-text\": RuleSeverity;\n}\n\n/**\n * Full configuration for a forge-ts run.\n * Loaded from forge-ts.config.ts or the \"forge-ts\" key in package.json.\n * @public\n */\nexport interface ForgeConfig {\n\t/** Root directory of the project. */\n\trootDir: string;\n\t/** Path to the tsconfig.json to compile against. */\n\ttsconfig: string;\n\t/** Output directory for generated files. */\n\toutDir: string;\n\t/** Enforce TSDoc on all public exports. */\n\tenforce: {\n\t\tenabled: boolean;\n\t\t/**\n\t\t * Minimum visibility level to enforce documentation on.\n\t\t *\n\t\t * Accepts either a {@link Visibility} enum value or the equivalent\n\t\t * string literal (`\"public\"`, `\"beta\"`, `\"internal\"`, `\"private\"`).\n\t\t * String literals are resolved to enum values internally.\n\t\t */\n\t\tminVisibility: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\";\n\t\t/** Fail on warnings rather than only on errors. */\n\t\tstrict: boolean;\n\t\t/** Per-rule severity overrides. When strict is true, all \"warn\" become \"error\". */\n\t\trules: EnforceRules;\n\t\t/**\n\t\t * Path to a file listing symbol names to skip enforcement on (one per line).\n\t\t * Lines starting with `#` are treated as comments.\n\t\t * Typically generated by Knip: `npx knip --reporter json | jq -r '.exports[].name' > .forge-ignore`\n\t\t */\n\t\tignoreFile?: string;\n\t};\n\t/** DocTest configuration. */\n\tdoctest: {\n\t\tenabled: boolean;\n\t\t/** Cache directory for virtual test files. */\n\t\tcacheDir: string;\n\t};\n\t/** API generation configuration. */\n\tapi: {\n\t\tenabled: boolean;\n\t\t/** Generate an OpenAPI spec from exported HTTP handlers. */\n\t\topenapi: boolean;\n\t\t/** Output path for the OpenAPI spec file. */\n\t\topenapiPath: string;\n\t};\n\t/** Output generation configuration. */\n\tgen: {\n\t\tenabled: boolean;\n\t\t/** Output formats to generate. */\n\t\tformats: Array<\"markdown\" | \"mdx\">;\n\t\t/** Generate an llms.txt companion file. */\n\t\tllmsTxt: boolean;\n\t\t/** Synchronise summaries back into README.md. */\n\t\treadmeSync: boolean;\n\t\t/** Static site generator to target for output format. */\n\t\tssgTarget?: \"docusaurus\" | \"mintlify\" | \"nextra\" | \"vitepress\";\n\t};\n\t/**\n\t * Skill package generation settings.\n\t * Custom sections here are merged into the generated SKILL.md,\n\t * allowing projects to inject workflow knowledge, domain gotchas,\n\t * and other context that cannot be derived from symbols alone.\n\t */\n\tskill: {\n\t\t/** When true, generate a SKILL.md package alongside llms.txt. Defaults to following `gen.llmsTxt`. */\n\t\tenabled?: boolean;\n\t\t/**\n\t\t * Custom sections to inject into the generated SKILL.md body.\n\t\t * Each entry becomes a `## heading` section with the provided markdown content.\n\t\t * Sections are inserted after the auto-generated API section and before Gotchas.\n\t\t *\n\t\t * @example\n\t\t * ```typescript\n\t\t * customSections: [\n\t\t * { heading: \"The Flow\", content: \"check → build → docs init → docs dev\" },\n\t\t * { heading: \"SSoT Principle\", content: \"Source code IS documentation.\" },\n\t\t * ]\n\t\t * ```\n\t\t */\n\t\tcustomSections?: Array<{ heading: string; content: string }>;\n\t\t/**\n\t\t * Extra gotcha lines to append to the auto-detected Gotchas section.\n\t\t * Each string becomes a `- ` bullet point.\n\t\t */\n\t\textraGotchas?: string[];\n\t};\n\t/** TSDoc ecosystem configuration. */\n\ttsdoc: {\n\t\t/** Write tsdoc.json to project root during init. Default: true */\n\t\twriteConfig: boolean;\n\t\t/** Custom tag definitions beyond the forge-ts preset. */\n\t\tcustomTags: Array<{\n\t\t\ttagName: string;\n\t\t\tsyntaxKind: \"block\" | \"inline\" | \"modifier\";\n\t\t}>;\n\t\t/** Enforcement level per standardization group. */\n\t\tenforce: {\n\t\t\t/** Core tags (e.g. @param, @returns, @remarks). Default: \"error\" */\n\t\t\tcore: \"error\" | \"warn\" | \"off\";\n\t\t\t/** Extended tags (e.g. @example, @throws, @see). Default: \"warn\" */\n\t\t\textended: \"error\" | \"warn\" | \"off\";\n\t\t\t/** Discretionary tags (@alpha, @beta, @public, @internal). Default: \"off\" */\n\t\t\tdiscretionary: \"error\" | \"warn\" | \"off\";\n\t\t};\n\t};\n\t/** Bypass budget configuration for temporary rule overrides. */\n\tbypass: {\n\t\t/** Maximum number of bypasses allowed per calendar day. Default: 3 */\n\t\tdailyBudget: number;\n\t\t/** Duration in hours before a bypass automatically expires. Default: 24 */\n\t\tdurationHours: number;\n\t};\n\t/** Guide generation configuration. */\n\tguides: {\n\t\t/** Enable intelligent guide generation. Default: true */\n\t\tenabled: boolean;\n\t\t/** Auto-discover guide topics from code analysis. Default: true */\n\t\tautoDiscover: boolean;\n\t\t/** Explicit guide definitions (supplement auto-discovered guides). */\n\t\tcustom: Array<{\n\t\t\t/** URL slug for the guide (e.g., \"authentication\") */\n\t\t\tslug: string;\n\t\t\t/** Human-readable title */\n\t\t\ttitle: string;\n\t\t\t/** Glob patterns for source files to analyze */\n\t\t\tsources: string[];\n\t\t}>;\n\t};\n\t/** Downstream config drift guards. */\n\tguards: {\n\t\t/** tsconfig.json strictness validation. */\n\t\ttsconfig: {\n\t\t\tenabled: boolean;\n\t\t\t/** Required strict-mode flags. Default: [\"strict\", \"strictNullChecks\", \"noImplicitAny\"] */\n\t\t\trequiredFlags: string[];\n\t\t};\n\t\t/** Biome config drift detection. */\n\t\tbiome: {\n\t\t\tenabled: boolean;\n\t\t\t/** Biome rules that must stay at error level. Auto-detected on lock. */\n\t\t\tlockedRules: string[];\n\t\t};\n\t\t/** package.json guards. */\n\t\tpackageJson: {\n\t\t\tenabled: boolean;\n\t\t\t/** Minimum Node.js version in engines field. */\n\t\t\tminNodeVersion: string;\n\t\t\t/** Required fields in package.json. */\n\t\t\trequiredFields: string[];\n\t\t};\n\t};\n\t/**\n\t * Warnings generated during config loading (e.g., unknown keys).\n\t * Populated by loadConfig(). Agents should surface these in output.\n\t * @internal\n\t */\n\t_configWarnings?: string[];\n\t/** Project metadata — auto-detected from package.json if not provided. */\n\tproject: {\n\t\t/** Repository URL (e.g., \"https://github.com/user/repo\"). */\n\t\trepository?: string;\n\t\t/** Project homepage URL. */\n\t\thomepage?: string;\n\t\t/** npm package name for the main/CLI package. */\n\t\tpackageName?: string;\n\t\t/** Short description from package.json. */\n\t\tdescription?: string;\n\t\t/** Package version string. */\n\t\tversion?: string;\n\t\t/** CLI entry points from the `bin` field (e.g., `{ \"my-cli\": \"./dist/cli.js\" }`). */\n\t\tbin?: Record<string, string>;\n\t\t/** npm scripts from package.json (e.g., `{ \"test\": \"vitest\", \"build\": \"tsup\" }`). */\n\t\tscripts?: Record<string, string>;\n\t\t/** npm keywords for the package. */\n\t\tkeywords?: string[];\n\t};\n}\n\n/**\n * The result of a forge-ts compilation pass.\n * @public\n */\nexport interface ForgeResult {\n\t/** Whether the run succeeded without errors. */\n\tsuccess: boolean;\n\t/** All symbols extracted during this run. */\n\tsymbols: ForgeSymbol[];\n\t/** Errors that caused or would cause failure. */\n\terrors: ForgeError[];\n\t/** Non-fatal warnings. */\n\twarnings: ForgeWarning[];\n\t/** Wall-clock duration of the run in milliseconds. */\n\tduration: number;\n\t/** Absolute paths of files written during this run (populated by gen). */\n\twrittenFiles?: string[];\n}\n\n/**\n * A diagnostic error produced during a forge-ts run.\n * @public\n */\nexport interface ForgeError {\n\t/** Machine-readable error code (e.g. \"E001\"). */\n\tcode: string;\n\t/** Human-readable description of the error. */\n\tmessage: string;\n\t/** Absolute path of the file where the error occurred. */\n\tfilePath: string;\n\t/** 1-based line number. */\n\tline: number;\n\t/** 0-based column. */\n\tcolumn: number;\n\t/** Suggested fix for the agent — exact TSDoc block to add. */\n\tsuggestedFix?: string;\n\t/** The symbol name that needs fixing. */\n\tsymbolName?: string;\n\t/** The symbol kind (function, class, interface, etc.). */\n\tsymbolKind?: string;\n}\n\n/**\n * A diagnostic warning produced during a forge-ts run.\n * @public\n */\nexport interface ForgeWarning {\n\t/** Machine-readable warning code (e.g. \"W001\"). */\n\tcode: string;\n\t/** Human-readable description of the warning. */\n\tmessage: string;\n\t/** Absolute path of the file where the warning occurred. */\n\tfilePath: string;\n\t/** 1-based line number. */\n\tline: number;\n\t/** 0-based column. */\n\tcolumn: number;\n}\n","/**\n * Config locking system for forge-ts.\n *\n * Prevents LLM agents from silently weakening project settings by snapshotting\n * the current config state and validating it on every subsequent run.\n *\n * @packageDocumentation\n * @public\n */\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ForgeConfig } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Lock file name\n// ---------------------------------------------------------------------------\n\n/**\n * Default lock file name placed in the project root.\n * @internal\n */\nconst LOCK_FILE_NAME = \".forge-lock.json\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Manifest stored in `.forge-lock.json`.\n * Captures a point-in-time snapshot of the project's forge-ts configuration\n * so that future runs can detect when settings have been weakened.\n *\n * @public\n */\nexport interface ForgeLockManifest {\n\t/** Schema version of the lock manifest. */\n\tversion: string;\n\t/** ISO-8601 timestamp when the lock was created. */\n\tlockedAt: string;\n\t/** Identifier of the user or agent that created the lock. */\n\tlockedBy: string;\n\t/** Snapshot of locked configuration values. */\n\tconfig: {\n\t\t/** Rule name to severity mapping from enforce.rules. */\n\t\trules: Record<string, string>;\n\t\t/** tsconfig guard settings, if readable at lock time. */\n\t\ttsconfig?: Record<string, unknown>;\n\t\t/** Biome guard settings, if readable at lock time. */\n\t\tbiome?: Record<string, unknown>;\n\t};\n}\n\n/**\n * A single violation found when comparing current config against the lock.\n *\n * @public\n */\nexport interface LockViolation {\n\t/** Dot-path of the config field that changed (e.g., \"rules.require-summary\"). */\n\tfield: string;\n\t/** The value stored in the lock file. */\n\tlocked: string;\n\t/** The current value in the live config. */\n\tcurrent: string;\n\t/** Human-readable explanation of the violation. */\n\tmessage: string;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Reads the `.forge-lock.json` file from the given project root.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns The parsed lock manifest, or `null` if no lock file exists or is invalid.\n * @example\n * ```typescript\n * import { readLockFile } from \"@forge-ts/core\";\n * const lock = readLockFile(\"/path/to/project\");\n * if (lock) {\n * console.log(`Locked at ${lock.lockedAt} by ${lock.lockedBy}`);\n * }\n * ```\n * @public\n */\nexport function readLockFile(rootDir: string): ForgeLockManifest | null {\n\tconst lockPath = join(rootDir, LOCK_FILE_NAME);\n\tif (!existsSync(lockPath)) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst raw = readFileSync(lockPath, \"utf8\");\n\t\treturn JSON.parse(raw) as ForgeLockManifest;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Writes a {@link ForgeLockManifest} to `.forge-lock.json` in the project root.\n *\n * @param rootDir - Absolute path to the project root.\n * @param manifest - The lock manifest to write.\n * @example\n * ```typescript\n * import { writeLockFile, createLockManifest, loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig(\"/path/to/project\");\n * const manifest = createLockManifest(config);\n * writeLockFile(\"/path/to/project\", manifest);\n * ```\n * @public\n */\nexport function writeLockFile(rootDir: string, manifest: ForgeLockManifest): void {\n\tconst lockPath = join(rootDir, LOCK_FILE_NAME);\n\twriteFileSync(lockPath, `${JSON.stringify(manifest, null, 2)}\\n`, \"utf8\");\n}\n\n/**\n * Removes the `.forge-lock.json` file from the project root.\n *\n * @param rootDir - Absolute path to the project root.\n * @returns `true` if the file existed and was removed, `false` otherwise.\n * @example\n * ```typescript\n * import { removeLockFile } from \"@forge-ts/core\";\n * const removed = removeLockFile(\"/path/to/project\");\n * console.log(removed ? \"Lock removed\" : \"No lock file found\");\n * ```\n * @public\n */\nexport function removeLockFile(rootDir: string): boolean {\n\tconst lockPath = join(rootDir, LOCK_FILE_NAME);\n\tif (!existsSync(lockPath)) {\n\t\treturn false;\n\t}\n\ttry {\n\t\tunlinkSync(lockPath);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a {@link ForgeLockManifest} from the current project config.\n *\n * Snapshots the enforce rule severities and guard settings so they can\n * be compared on future runs to detect weakening.\n *\n * @param config - The fully-resolved {@link ForgeConfig} to snapshot.\n * @param lockedBy - Identifier of the user or agent creating the lock. Defaults to `\"forge-ts lock\"`.\n * @returns A new lock manifest ready to be written with {@link writeLockFile}.\n * @example\n * ```typescript\n * import { createLockManifest, loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const manifest = createLockManifest(config);\n * console.log(manifest.config.rules); // { \"require-summary\": \"error\", ... }\n * ```\n * @public\n */\nexport function createLockManifest(\n\tconfig: ForgeConfig,\n\tlockedBy: string = \"forge-ts lock\",\n): ForgeLockManifest {\n\tconst rules: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(config.enforce.rules)) {\n\t\trules[key] = value;\n\t}\n\n\tconst lockConfig: ForgeLockManifest[\"config\"] = { rules };\n\n\t// Snapshot tsconfig guard settings if enabled\n\tif (config.guards.tsconfig.enabled) {\n\t\tlockConfig.tsconfig = {\n\t\t\tenabled: config.guards.tsconfig.enabled,\n\t\t\trequiredFlags: config.guards.tsconfig.requiredFlags,\n\t\t};\n\t}\n\n\t// Snapshot biome guard settings if enabled\n\tif (config.guards.biome.enabled) {\n\t\tlockConfig.biome = {\n\t\t\tenabled: config.guards.biome.enabled,\n\t\t\tlockedRules: config.guards.biome.lockedRules,\n\t\t};\n\t}\n\n\treturn {\n\t\tversion: \"1.0.0\",\n\t\tlockedAt: new Date().toISOString(),\n\t\tlockedBy,\n\t\tconfig: lockConfig,\n\t};\n}\n\n/**\n * Validates the current config against a locked manifest.\n *\n * Returns an array of violations where the current config has weakened\n * settings relative to the locked state. Weakening means:\n * - A rule severity changed from `\"error\"` to `\"warn\"` or `\"off\"`\n * - A rule severity changed from `\"warn\"` to `\"off\"`\n * - A tsconfig guard was disabled\n * - A required tsconfig flag was removed\n * - A biome guard was disabled\n * - A locked biome rule was removed\n *\n * @param config - The current fully-resolved {@link ForgeConfig}.\n * @param lock - The lock manifest to validate against.\n * @returns An array of {@link LockViolation} entries. Empty means no weakening detected.\n * @example\n * ```typescript\n * import { validateAgainstLock, readLockFile, loadConfig } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const lock = readLockFile(config.rootDir);\n * if (lock) {\n * const violations = validateAgainstLock(config, lock);\n * for (const v of violations) {\n * console.error(`LOCK VIOLATION: ${v.message}`);\n * }\n * }\n * ```\n * @public\n */\nexport function validateAgainstLock(config: ForgeConfig, lock: ForgeLockManifest): LockViolation[] {\n\tconst violations: LockViolation[] = [];\n\n\t// Severity ranking: higher number = stricter\n\tconst severityRank: Record<string, number> = {\n\t\toff: 0,\n\t\twarn: 1,\n\t\terror: 2,\n\t};\n\n\t// Check rule severities\n\tfor (const [ruleName, lockedSeverity] of Object.entries(lock.config.rules)) {\n\t\tconst currentSeverity =\n\t\t\t(config.enforce.rules as unknown as Record<string, string>)[ruleName] ?? \"off\";\n\t\tconst lockedRank = severityRank[lockedSeverity] ?? 0;\n\t\tconst currentRank = severityRank[currentSeverity] ?? 0;\n\n\t\tif (currentRank < lockedRank) {\n\t\t\tviolations.push({\n\t\t\t\tfield: `rules.${ruleName}`,\n\t\t\t\tlocked: lockedSeverity,\n\t\t\t\tcurrent: currentSeverity,\n\t\t\t\tmessage: `Rule \"${ruleName}\" was weakened from \"${lockedSeverity}\" to \"${currentSeverity}\". Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".`,\n\t\t\t});\n\t\t}\n\t}\n\n\t// Check tsconfig guard settings\n\tif (lock.config.tsconfig) {\n\t\tconst lockedTsconfig = lock.config.tsconfig as {\n\t\t\tenabled?: boolean;\n\t\t\trequiredFlags?: string[];\n\t\t};\n\n\t\t// Guard was disabled\n\t\tif (lockedTsconfig.enabled && !config.guards.tsconfig.enabled) {\n\t\t\tviolations.push({\n\t\t\t\tfield: \"guards.tsconfig.enabled\",\n\t\t\t\tlocked: \"true\",\n\t\t\t\tcurrent: \"false\",\n\t\t\t\tmessage:\n\t\t\t\t\t'tsconfig guard was disabled. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".',\n\t\t\t});\n\t\t}\n\n\t\t// Required flags removed\n\t\tif (lockedTsconfig.requiredFlags && config.guards.tsconfig.enabled) {\n\t\t\tconst currentFlags = new Set(config.guards.tsconfig.requiredFlags);\n\t\t\tfor (const flag of lockedTsconfig.requiredFlags) {\n\t\t\t\tif (!currentFlags.has(flag)) {\n\t\t\t\t\tviolations.push({\n\t\t\t\t\t\tfield: `guards.tsconfig.requiredFlags.${flag}`,\n\t\t\t\t\t\tlocked: flag,\n\t\t\t\t\t\tcurrent: \"(removed)\",\n\t\t\t\t\t\tmessage: `tsconfig required flag \"${flag}\" was removed. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check biome guard settings\n\tif (lock.config.biome) {\n\t\tconst lockedBiome = lock.config.biome as {\n\t\t\tenabled?: boolean;\n\t\t\tlockedRules?: string[];\n\t\t};\n\n\t\t// Guard was disabled\n\t\tif (lockedBiome.enabled && !config.guards.biome.enabled) {\n\t\t\tviolations.push({\n\t\t\t\tfield: \"guards.biome.enabled\",\n\t\t\t\tlocked: \"true\",\n\t\t\t\tcurrent: \"false\",\n\t\t\t\tmessage:\n\t\t\t\t\t'Biome guard was disabled. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".',\n\t\t\t});\n\t\t}\n\n\t\t// Locked rules removed\n\t\tif (lockedBiome.lockedRules && config.guards.biome.enabled) {\n\t\t\tconst currentRules = new Set(config.guards.biome.lockedRules);\n\t\t\tfor (const rule of lockedBiome.lockedRules) {\n\t\t\t\tif (!currentRules.has(rule)) {\n\t\t\t\t\tviolations.push({\n\t\t\t\t\t\tfield: `guards.biome.lockedRules.${rule}`,\n\t\t\t\t\t\tlocked: rule,\n\t\t\t\t\t\tcurrent: \"(removed)\",\n\t\t\t\t\t\tmessage: `Biome locked rule \"${rule}\" was removed. Locked settings cannot be weakened without running \"forge-ts unlock --reason=...\".`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn violations;\n}\n","import { type ForgeSymbol, Visibility } from \"./types.js\";\n\n/**\n * Determines the visibility level of a symbol from its TSDoc release tags.\n *\n * The precedence order is:\n * 1. `@internal` → {@link Visibility.Internal}\n * 2. `@beta` → {@link Visibility.Beta}\n * 3. `@public` → {@link Visibility.Public}\n * 4. (no tag) → {@link Visibility.Public} (default for exports)\n *\n * @param tags - The parsed `tags` map from `ForgeSymbol.documentation`.\n * @returns The resolved {@link Visibility} value.\n * @example\n * ```typescript\n * import { resolveVisibility } from \"@forge-ts/core\";\n * const vis = resolveVisibility({ internal: [] });\n * // vis === Visibility.Internal\n * ```\n * @public\n */\nexport function resolveVisibility(tags: Record<string, string[]> | undefined): Visibility {\n\tif (!tags) return Visibility.Public;\n\n\tif (\"internal\" in tags) return Visibility.Internal;\n\tif (\"beta\" in tags) return Visibility.Beta;\n\tif (\"public\" in tags) return Visibility.Public;\n\n\treturn Visibility.Public;\n}\n\n/**\n * Numeric rank used for visibility comparisons.\n * Lower numbers are more restrictive.\n * @internal\n */\nconst VISIBILITY_RANK: Record<Visibility, number> = {\n\t[Visibility.Public]: 0,\n\t[Visibility.Beta]: 1,\n\t[Visibility.Internal]: 2,\n\t[Visibility.Private]: 3,\n};\n\n/**\n * Returns whether `candidate` meets or exceeds the required minimum visibility.\n *\n * \"Meets\" means the symbol is at least as visible as `minVisibility`.\n * For example, `Public` meets a minimum of `Public`, but `Internal` does not.\n *\n * Both parameters accept either a {@link Visibility} enum value or the\n * equivalent string literal (`\"public\"`, `\"beta\"`, `\"internal\"`, `\"private\"`).\n *\n * @param candidate - The visibility of the symbol being tested.\n * @param minVisibility - The minimum visibility threshold.\n * @returns `true` if `candidate` is at least as visible as `minVisibility`.\n * @example\n * ```typescript\n * import { meetsVisibility, Visibility } from \"@forge-ts/core\";\n * meetsVisibility(Visibility.Public, Visibility.Public); // true\n * meetsVisibility(Visibility.Internal, Visibility.Public); // false\n * meetsVisibility(\"public\", \"beta\"); // true (string literals also accepted)\n * ```\n * @public\n */\nexport function meetsVisibility(\n\tcandidate: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\",\n\tminVisibility: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\",\n): boolean {\n\treturn VISIBILITY_RANK[candidate as Visibility] <= VISIBILITY_RANK[minVisibility as Visibility];\n}\n\n/**\n * Filters an array of {@link ForgeSymbol} objects to only include symbols\n * whose visibility meets or exceeds `minVisibility`.\n *\n * @param symbols - The full list of symbols to filter.\n * @param minVisibility - The minimum visibility threshold to keep.\n * @returns A new array containing only symbols that pass the visibility check.\n * @example\n * ```typescript\n * import { filterByVisibility, Visibility } from \"@forge-ts/core\";\n * const publicOnly = filterByVisibility(symbols, Visibility.Public);\n * ```\n * @public\n */\nexport function filterByVisibility(\n\tsymbols: ForgeSymbol[],\n\tminVisibility: Visibility | \"public\" | \"beta\" | \"internal\" | \"private\",\n): ForgeSymbol[] {\n\treturn symbols.filter((s) => meetsVisibility(s.visibility, minVisibility));\n}\n","import { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, resolve } from \"node:path\";\nimport {\n\ttype DocBlock,\n\ttype DocCodeSpan,\n\ttype DocComment,\n\ttype DocFencedCode,\n\ttype DocLinkTag,\n\ttype DocNode,\n\tDocNodeKind,\n\ttype DocParagraph,\n\ttype DocPlainText,\n\ttype DocSection,\n\tStandardTags,\n\tTSDocConfiguration,\n\tTSDocParser,\n} from \"@microsoft/tsdoc\";\nimport { TSDocConfigFile } from \"@microsoft/tsdoc-config\";\nimport ts from \"typescript\";\nimport type { ForgeConfig, ForgeSymbol } from \"./types.js\";\nimport { resolveVisibility } from \"./visibility.js\";\n\n// ---------------------------------------------------------------------------\n// TSDoc configuration cache (keyed by folder path)\n// ---------------------------------------------------------------------------\n\n/**\n * Per-folder cache of resolved TSDoc configurations. Prevents re-loading\n * `tsdoc.json` for every comment in the same directory.\n * @internal\n */\nconst tsdocConfigCache = new Map<string, TSDocConfiguration>();\n\n/** Clears the TSDoc configuration cache. Intended for use in tests only. @internal */\nexport function clearTSDocConfigCache(): void {\n\ttsdocConfigCache.clear();\n}\n\n/**\n * Resolve the TSDoc configuration to use when parsing comments in\n * files under `folderPath`.\n *\n * If a `tsdoc.json` file exists in or above the folder and can be loaded\n * without errors, its settings are applied to a fresh `TSDocConfiguration`\n * via `TSDocConfigFile.configureParser()`. Otherwise the default\n * `TSDocConfiguration` is returned (backward-compatible behaviour).\n *\n * Results are cached per folder path so the file system is only consulted\n * once per unique directory.\n *\n * @param folderPath - Absolute directory path of the source file being parsed.\n * @returns A configured `TSDocConfiguration` instance.\n * @internal\n */\nexport function loadTSDocConfiguration(folderPath: string): TSDocConfiguration {\n\tconst cached = tsdocConfigCache.get(folderPath);\n\tif (cached) return cached;\n\n\tconst configuration = new TSDocConfiguration();\n\n\ttry {\n\t\tconst configFile = TSDocConfigFile.loadForFolder(folderPath);\n\t\tif (!configFile.fileNotFound && !configFile.hasErrors) {\n\t\t\tconfigFile.configureParser(configuration);\n\t\t} else {\n\t\t\t// No project-level tsdoc.json found — load the bundled preset so\n\t\t\t// forge-ts custom tags (@route, @category, etc.) are still recognised.\n\t\t\tloadBundledPreset(configuration);\n\t\t}\n\t} catch {\n\t\t// If loading fails for any reason, fall back to the bundled preset.\n\t\ttry {\n\t\t\tloadBundledPreset(configuration);\n\t\t} catch {\n\t\t\t// Last resort: bare TSDocConfiguration (no custom tags).\n\t\t}\n\t}\n\n\ttsdocConfigCache.set(folderPath, configuration);\n\treturn configuration;\n}\n\n/**\n * Loads the forge-ts bundled tsdoc.json preset from\n * `@forge-ts/core/tsdoc-preset/tsdoc.json` and applies it to the given\n * configuration via `TSDocConfigFile.configureParser()`.\n *\n * @param configuration - The `TSDocConfiguration` instance to configure.\n * @internal\n */\nfunction loadBundledPreset(configuration: TSDocConfiguration): void {\n\tconst require = createRequire(import.meta.url);\n\tconst presetPath = require.resolve(\"@forge-ts/core/tsdoc-preset/tsdoc.json\");\n\tconst presetFile = TSDocConfigFile.loadFile(presetPath);\n\tif (!presetFile.hasErrors) {\n\t\tpresetFile.configureParser(configuration);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Public API surface\n// ---------------------------------------------------------------------------\n\n/**\n * The return type of {@link createWalker}.\n * @public\n */\nexport interface ASTWalker {\n\t/**\n\t * Walk all source files referenced by the configured tsconfig and return\n\t * one {@link ForgeSymbol} per exported declaration.\n\t */\n\twalk(): ForgeSymbol[];\n}\n\n// ---------------------------------------------------------------------------\n// TSDoc helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Render inline nodes (PlainText, CodeSpan, SoftBreak, LinkTag) to a plain string.\n * `{@link Target}` references are rendered as backtick-wrapped target names\n * (e.g., `` `ForgeConfig` ``) so they appear correctly in generated Markdown.\n * @internal\n */\nfunction renderInlineNodes(nodes: readonly DocNode[]): string {\n\tconst parts: string[] = [];\n\tfor (const node of nodes) {\n\t\tswitch (node.kind) {\n\t\t\tcase DocNodeKind.PlainText:\n\t\t\t\tparts.push((node as DocPlainText).text);\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.CodeSpan:\n\t\t\t\tparts.push(`\\`${(node as DocCodeSpan).code}\\``);\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.SoftBreak:\n\t\t\t\tparts.push(\" \");\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.Paragraph:\n\t\t\t\t// Recurse into paragraph nodes to extract nested text\n\t\t\t\tparts.push(renderInlineNodes((node as DocParagraph).nodes));\n\t\t\t\tbreak;\n\t\t\tcase DocNodeKind.LinkTag: {\n\t\t\t\t// Render {@link Target} as `Target` in output text.\n\t\t\t\t// Uses the link text if provided, otherwise the code destination.\n\t\t\t\tconst linkTag = node as DocLinkTag;\n\t\t\t\tconst linkText =\n\t\t\t\t\tlinkTag.linkText ??\n\t\t\t\t\tlinkTag.codeDestination?.memberReferences\n\t\t\t\t\t\t.map((ref) => ref.memberIdentifier?.identifier)\n\t\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t\t.join(\".\");\n\t\t\t\tif (linkText) {\n\t\t\t\t\tparts.push(`\\`${linkText}\\``);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn parts.join(\"\");\n}\n\n/** Render a `DocSection` (or similar node) to a plain string. @internal */\nfunction renderDocSection(section: DocSection | undefined): string {\n\tif (!section) return \"\";\n\treturn renderInlineNodes(section.nodes).trim();\n}\n\n/** Render a `DocBlock`'s content to a plain string. @internal */\nfunction renderBlock(block: DocBlock): string {\n\treturn renderDocSection(block.content);\n}\n\n/** Extract all `@example` fenced code blocks from a parsed comment. @internal */\nfunction extractExamples(\n\tcomment: DocComment,\n\tstartLine: number,\n): Array<{ code: string; language: string; line: number }> {\n\tconst examples: Array<{ code: string; language: string; line: number }> = [];\n\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() !== \"@example\") continue;\n\n\t\tfor (const node of block.content.nodes) {\n\t\t\tif (node.kind === DocNodeKind.FencedCode) {\n\t\t\t\tconst fenced = node as DocFencedCode;\n\t\t\t\texamples.push({\n\t\t\t\t\tcode: fenced.code,\n\t\t\t\t\tlanguage: fenced.language || \"typescript\",\n\t\t\t\t\tline: startLine,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn examples;\n}\n\n/** Parse a raw JSDoc/TSDoc comment string into a structured documentation object. @internal */\nfunction parseTSDoc(\n\trawComment: string,\n\tstartLine: number,\n\tfolderPath?: string,\n): ForgeSymbol[\"documentation\"] {\n\tconst configuration =\n\t\tfolderPath !== undefined ? loadTSDocConfiguration(folderPath) : new TSDocConfiguration();\n\tconst parser = new TSDocParser(configuration);\n\tconst result = parser.parseString(rawComment);\n\tconst comment = result.docComment;\n\n\tconst tags: Record<string, string[]> = {};\n\n\t// Release tags\n\tif (comment.modifierTagSet.hasTag(StandardTags.public)) {\n\t\ttags.public = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.beta)) {\n\t\ttags.beta = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.internal)) {\n\t\ttags.internal = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.alpha)) {\n\t\ttags.alpha = [];\n\t}\n\tif (comment.modifierTagSet.hasTag(StandardTags.packageDocumentation)) {\n\t\ttags.packageDocumentation = [];\n\t}\n\n\t// @deprecated\n\tlet deprecated: string | undefined;\n\tif (comment.deprecatedBlock) {\n\t\tdeprecated = renderBlock(comment.deprecatedBlock).trim() || \"true\";\n\t}\n\n\t// @param blocks\n\tconst params: Array<{ name: string; description: string; type?: string }> = [];\n\tfor (const paramBlock of comment.params.blocks) {\n\t\tparams.push({\n\t\t\tname: paramBlock.parameterName,\n\t\t\tdescription: renderBlock(paramBlock),\n\t\t});\n\t}\n\n\t// @returns block\n\tlet returns: { description: string; type?: string } | undefined;\n\tif (comment.returnsBlock) {\n\t\tconst desc = renderBlock(comment.returnsBlock);\n\t\tif (desc) returns = { description: desc };\n\t}\n\n\t// @throws blocks\n\tconst throws: Array<{ type?: string; description: string }> = [];\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@throws\") {\n\t\t\tthrows.push({ description: renderBlock(block) });\n\t\t}\n\t}\n\n\t// @route blocks - format: \"METHOD /path\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@route\") {\n\t\t\tconst routeText = renderBlock(block).trim();\n\t\t\tconst match = routeText.match(/^(GET|POST|PUT|DELETE|PATCH|OPTIONS|HEAD)\\s+(\\S+)/i);\n\t\t\tif (match) {\n\t\t\t\tif (!tags.route) tags.route = [];\n\t\t\t\ttags.route.push(`${match[1].toUpperCase()} ${match[2]}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @remarks block\n\tif (comment.remarksBlock) {\n\t\tconst remarksText = renderBlock(comment.remarksBlock).trim();\n\t\ttags.remarks = remarksText ? [remarksText] : [];\n\t}\n\n\t// @see blocks\n\tif (comment.seeBlocks.length > 0) {\n\t\ttags.see = comment.seeBlocks.map((block) => renderBlock(block).trim()).filter(Boolean);\n\t}\n\n\t// @typeParam blocks\n\tif (comment.typeParams.count > 0) {\n\t\ttags.typeParam = comment.typeParams.blocks.map(\n\t\t\t(block) => `${block.parameterName} - ${renderBlock(block).trim()}`,\n\t\t);\n\t}\n\n\t// @defaultValue blocks\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@defaultvalue\") {\n\t\t\tconst dvText = renderBlock(block).trim();\n\t\t\tif (!tags.defaultValue) tags.defaultValue = [];\n\t\t\ttags.defaultValue.push(dvText);\n\t\t}\n\t}\n\n\t// @concept blocks — free-text concept labels for grouping/navigation\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@concept\") {\n\t\t\tconst conceptText = renderBlock(block).trim();\n\t\t\tif (conceptText) {\n\t\t\t\tif (!tags.concept) tags.concept = [];\n\t\t\t\ttags.concept.push(conceptText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @guide blocks — guide slugs/labels for cross-referencing documentation\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@guide\") {\n\t\t\tconst guideText = renderBlock(block).trim();\n\t\t\tif (guideText) {\n\t\t\t\tif (!tags.guide) tags.guide = [];\n\t\t\t\ttags.guide.push(guideText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @category blocks — grouping labels for navigation and guide discovery\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@category\") {\n\t\t\tconst categoryText = renderBlock(block).trim();\n\t\t\tif (categoryText) {\n\t\t\t\tif (!tags.category) tags.category = [];\n\t\t\t\ttags.category.push(categoryText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @since blocks — version when the symbol was introduced\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@since\") {\n\t\t\tconst sinceText = renderBlock(block).trim();\n\t\t\tif (sinceText) {\n\t\t\t\tif (!tags.since) tags.since = [];\n\t\t\t\ttags.since.push(sinceText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @response blocks — format: \"STATUS_CODE - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@response\") {\n\t\t\tconst responseText = renderBlock(block).trim();\n\t\t\tif (responseText) {\n\t\t\t\tif (!tags.response) tags.response = [];\n\t\t\t\ttags.response.push(responseText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @query blocks — format: \"paramName - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@query\") {\n\t\t\tconst queryText = renderBlock(block).trim();\n\t\t\tif (queryText) {\n\t\t\t\tif (!tags.query) tags.query = [];\n\t\t\t\ttags.query.push(queryText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @header blocks — format: \"headerName - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@header\") {\n\t\t\tconst headerText = renderBlock(block).trim();\n\t\t\tif (headerText) {\n\t\t\t\tif (!tags.header) tags.header = [];\n\t\t\t\ttags.header.push(headerText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @body blocks — format: \"TypeName - Description\"\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@body\") {\n\t\t\tconst bodyText = renderBlock(block).trim();\n\t\t\tif (bodyText) {\n\t\t\t\tif (!tags.body) tags.body = [];\n\t\t\t\ttags.body.push(bodyText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @faq blocks — free text\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@faq\") {\n\t\t\tconst faqText = renderBlock(block).trim();\n\t\t\tif (faqText) {\n\t\t\t\tif (!tags.faq) tags.faq = [];\n\t\t\t\ttags.faq.push(faqText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @breaking blocks — free text describing the breaking change\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@breaking\") {\n\t\t\tconst breakingText = renderBlock(block).trim();\n\t\t\tif (breakingText) {\n\t\t\t\tif (!tags.breaking) tags.breaking = [];\n\t\t\t\ttags.breaking.push(breakingText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @migration blocks — free text describing migration path\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@migration\") {\n\t\t\tconst migrationText = renderBlock(block).trim();\n\t\t\tif (migrationText) {\n\t\t\t\tif (!tags.migration) tags.migration = [];\n\t\t\t\ttags.migration.push(migrationText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @complexity blocks — free text (e.g., \"O(n log n)\")\n\tfor (const block of comment.customBlocks) {\n\t\tif (block.blockTag.tagName.toLowerCase() === \"@complexity\") {\n\t\t\tconst complexityText = renderBlock(block).trim();\n\t\t\tif (complexityText) {\n\t\t\t\tif (!tags.complexity) tags.complexity = [];\n\t\t\t\ttags.complexity.push(complexityText);\n\t\t\t}\n\t\t}\n\t}\n\n\t// @quickstart modifier tag — presence-only, no content\n\tfor (const tag of comment.modifierTagSet.nodes) {\n\t\tif (tag.tagName.toLowerCase() === \"@quickstart\") {\n\t\t\ttags.quickstart = [];\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// @example blocks\n\tconst examples = extractExamples(comment, startLine);\n\n\t// Extract {@link} references\n\tconst links: Array<{ target: string; line: number; text?: string }> = [];\n\tfunction walkForLinks(node: DocNode): void {\n\t\tif (node.kind === DocNodeKind.LinkTag) {\n\t\t\tconst linkTag = node as DocLinkTag;\n\t\t\tif (linkTag.codeDestination) {\n\t\t\t\tconst target = linkTag.codeDestination.memberReferences\n\t\t\t\t\t.map((ref) => ref.memberIdentifier?.identifier ?? \"\")\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.join(\".\");\n\t\t\t\tif (target) {\n\t\t\t\t\tconst text = linkTag.linkText?.trim() || undefined;\n\t\t\t\t\tlinks.push({ target, line: startLine, text });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const child of node.getChildNodes()) {\n\t\t\twalkForLinks(child);\n\t\t}\n\t}\n\twalkForLinks(comment);\n\n\t// Extract {@inheritDoc} target if present\n\tif (comment.inheritDocTag?.declarationReference) {\n\t\tconst ref = comment.inheritDocTag.declarationReference;\n\t\tconst target = ref.memberReferences\n\t\t\t.map((r) => r.memberIdentifier?.identifier ?? \"\")\n\t\t\t.filter(Boolean)\n\t\t\t.join(\".\");\n\t\tif (target) {\n\t\t\tif (!tags.inheritDoc) tags.inheritDoc = [];\n\t\t\ttags.inheritDoc.push(target);\n\t\t}\n\t}\n\n\t// Collect TSDoc parser messages (syntax warnings/errors)\n\tconst parseMessages: Array<{ messageId: string; text: string; line: number }> = [];\n\tfor (const msg of result.log.messages) {\n\t\tparseMessages.push({\n\t\t\tmessageId: msg.messageId,\n\t\t\ttext: msg.unformattedText,\n\t\t\tline: startLine,\n\t\t});\n\t}\n\n\tconst summary = renderDocSection(comment.summarySection);\n\n\treturn {\n\t\tsummary: summary || undefined,\n\t\tparams: params.length > 0 ? params : undefined,\n\t\treturns,\n\t\tthrows: throws.length > 0 ? throws : undefined,\n\t\texamples: examples.length > 0 ? examples : undefined,\n\t\ttags: Object.keys(tags).length > 0 ? tags : undefined,\n\t\tdeprecated,\n\t\tlinks: links.length > 0 ? links : undefined,\n\t\tparseMessages: parseMessages.length > 0 ? parseMessages : undefined,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// TypeScript AST helpers\n// ---------------------------------------------------------------------------\n\n/** Extract the leading JSDoc comment text for a node. @internal */\nfunction getLeadingComment(node: ts.Node, sourceFile: ts.SourceFile): string | undefined {\n\tconst fullText = sourceFile.getFullText();\n\tconst ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());\n\tif (!ranges || ranges.length === 0) return undefined;\n\n\t// Take the last leading comment (closest to the declaration)\n\tconst range = ranges[ranges.length - 1];\n\tif (\n\t\trange.kind !== ts.SyntaxKind.MultiLineCommentTrivia ||\n\t\t!fullText.slice(range.pos, range.end).startsWith(\"/**\")\n\t) {\n\t\treturn undefined;\n\t}\n\n\treturn fullText.slice(range.pos, range.end);\n}\n\n/** Map a TypeScript `SyntaxKind` to a `ForgeSymbol` kind string. @internal */\nfunction kindToString(kind: ts.SyntaxKind): ForgeSymbol[\"kind\"] | null {\n\tswitch (kind) {\n\t\tcase ts.SyntaxKind.FunctionDeclaration:\n\t\tcase ts.SyntaxKind.ArrowFunction:\n\t\tcase ts.SyntaxKind.FunctionExpression:\n\t\t\treturn \"function\";\n\t\tcase ts.SyntaxKind.ClassDeclaration:\n\t\t\treturn \"class\";\n\t\tcase ts.SyntaxKind.InterfaceDeclaration:\n\t\t\treturn \"interface\";\n\t\tcase ts.SyntaxKind.TypeAliasDeclaration:\n\t\t\treturn \"type\";\n\t\tcase ts.SyntaxKind.EnumDeclaration:\n\t\t\treturn \"enum\";\n\t\tcase ts.SyntaxKind.VariableDeclaration:\n\t\tcase ts.SyntaxKind.VariableStatement:\n\t\t\treturn \"variable\";\n\t\tcase ts.SyntaxKind.MethodDeclaration:\n\t\tcase ts.SyntaxKind.MethodSignature:\n\t\t\treturn \"method\";\n\t\tcase ts.SyntaxKind.PropertyDeclaration:\n\t\tcase ts.SyntaxKind.PropertySignature:\n\t\tcase ts.SyntaxKind.EnumMember:\n\t\t\treturn \"property\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\n/** Build a human-readable type signature string using the type checker. @internal */\nfunction buildSignature(node: ts.Declaration, checker: ts.TypeChecker): string | undefined {\n\ttry {\n\t\tconst symbol = checker.getSymbolAtLocation((node as ts.NamedDeclaration).name ?? node);\n\t\tif (!symbol) return undefined;\n\t\tconst type = checker.getTypeOfSymbolAtLocation(symbol, node);\n\t\treturn checker.typeToString(type);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Walker implementation\n// ---------------------------------------------------------------------------\n\n/** @internal */\nfunction extractSymbolsFromFile(sourceFile: ts.SourceFile, checker: ts.TypeChecker): ForgeSymbol[] {\n\tconst symbols: ForgeSymbol[] = [];\n\tconst filePath = sourceFile.fileName;\n\tconst fileDir = dirname(filePath);\n\n\tfunction visit(node: ts.Node, parentExported: boolean): void {\n\t\tconst isExported =\n\t\t\tparentExported ||\n\t\t\t(ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export) !== 0;\n\n\t\t// Handle export declarations: `export { Foo, Bar }`\n\t\tif (ts.isExportDeclaration(node)) {\n\t\t\tts.forEachChild(node, (child) => visit(child, true));\n\t\t\treturn;\n\t\t}\n\n\t\tconst kind = kindToString(node.kind);\n\n\t\t// Variable statements need special handling: `export const foo = ...`\n\t\tif (ts.isVariableStatement(node)) {\n\t\t\tif (!isExported) {\n\t\t\t\tts.forEachChild(node, (child) => visit(child, false));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (const decl of node.declarationList.declarations) {\n\t\t\t\tconst name = decl.name.getText(sourceFile);\n\t\t\t\tconst pos = sourceFile.getLineAndCharacterOfPosition(decl.getStart());\n\t\t\t\tconst rawComment = getLeadingComment(node, sourceFile);\n\t\t\t\tconst documentation = rawComment\n\t\t\t\t\t? parseTSDoc(rawComment, pos.line + 1, fileDir)\n\t\t\t\t\t: undefined;\n\t\t\t\tconst tags = documentation?.tags;\n\t\t\t\tconst visibility = resolveVisibility(tags);\n\n\t\t\t\tsymbols.push({\n\t\t\t\t\tname,\n\t\t\t\t\tkind: \"variable\",\n\t\t\t\t\tvisibility,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tline: pos.line + 1,\n\t\t\t\t\tcolumn: pos.character,\n\t\t\t\t\tdocumentation,\n\t\t\t\t\tsignature: buildSignature(decl, checker),\n\t\t\t\t\texported: true,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kind === null || !isExported) {\n\t\t\tts.forEachChild(node, (child) => visit(child, isExported));\n\t\t\treturn;\n\t\t}\n\n\t\tconst namedNode = node as ts.NamedDeclaration;\n\t\tconst nameNode = namedNode.name;\n\t\tif (!nameNode) {\n\t\t\tts.forEachChild(node, (child) => visit(child, isExported));\n\t\t\treturn;\n\t\t}\n\n\t\tconst name = nameNode.getText(sourceFile);\n\t\tconst pos = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n\t\tconst rawComment = getLeadingComment(node, sourceFile);\n\t\tconst documentation = rawComment ? parseTSDoc(rawComment, pos.line + 1, fileDir) : undefined;\n\t\tconst tags = documentation?.tags;\n\t\tconst visibility = resolveVisibility(tags);\n\n\t\tconst children: ForgeSymbol[] = [];\n\n\t\t// Walk class members / interface members / enum members\n\t\tif (\n\t\t\tts.isClassDeclaration(node) ||\n\t\t\tts.isInterfaceDeclaration(node) ||\n\t\t\tts.isEnumDeclaration(node)\n\t\t) {\n\t\t\tfor (const member of node.members) {\n\t\t\t\tconst memberKind = kindToString(member.kind);\n\t\t\t\tif (!memberKind) continue;\n\t\t\t\tconst memberName = (member as ts.NamedDeclaration).name?.getText(sourceFile) ?? \"\";\n\t\t\t\tconst memberPos = sourceFile.getLineAndCharacterOfPosition(member.getStart());\n\t\t\t\tconst memberComment = getLeadingComment(member, sourceFile);\n\t\t\t\tconst memberDoc = memberComment\n\t\t\t\t\t? parseTSDoc(memberComment, memberPos.line + 1, fileDir)\n\t\t\t\t\t: undefined;\n\t\t\t\tconst memberTags = memberDoc?.tags;\n\t\t\t\tconst memberVisibility = resolveVisibility(memberTags);\n\n\t\t\t\tchildren.push({\n\t\t\t\t\tname: memberName,\n\t\t\t\t\tkind: memberKind,\n\t\t\t\t\tvisibility: memberVisibility,\n\t\t\t\t\tfilePath,\n\t\t\t\t\tline: memberPos.line + 1,\n\t\t\t\t\tcolumn: memberPos.character,\n\t\t\t\t\tdocumentation: memberDoc,\n\t\t\t\t\tsignature: buildSignature(member as ts.Declaration, checker),\n\t\t\t\t\texported: false,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tsymbols.push({\n\t\t\tname,\n\t\t\tkind,\n\t\t\tvisibility,\n\t\t\tfilePath,\n\t\t\tline: pos.line + 1,\n\t\t\tcolumn: pos.character,\n\t\t\tdocumentation,\n\t\t\tsignature: buildSignature(namedNode as ts.Declaration, checker),\n\t\t\tchildren: children.length > 0 ? children : undefined,\n\t\t\texported: isExported,\n\t\t});\n\t}\n\n\tts.forEachChild(sourceFile, (node) => visit(node, false));\n\treturn symbols;\n}\n\n/**\n * Creates an {@link ASTWalker} configured for the given forge config.\n *\n * The walker uses the TypeScript Compiler API to create a `ts.Program` from\n * the project's tsconfig, then visits every source file to extract exported\n * declarations. TSDoc comments are parsed with `@microsoft/tsdoc` to\n * populate the `documentation` field on each {@link ForgeSymbol}.\n *\n * @param config - The resolved {@link ForgeConfig} for the project.\n * @returns An {@link ASTWalker} instance whose `walk()` method performs the extraction.\n * @example\n * ```typescript\n * import { loadConfig, createWalker } from \"@forge-ts/core\";\n * const config = await loadConfig();\n * const walker = createWalker(config);\n * const symbols = walker.walk();\n * console.log(`Found ${symbols.length} symbols`);\n * ```\n * @public\n */\nexport function createWalker(config: ForgeConfig): ASTWalker {\n\treturn {\n\t\twalk(): ForgeSymbol[] {\n\t\t\t// Load tsconfig\n\t\t\tconst tsconfigPath = resolve(config.tsconfig);\n\t\t\tconst configFile = ts.readConfigFile(tsconfigPath, (path) => readFileSync(path, \"utf8\"));\n\n\t\t\tif (configFile.error) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to read tsconfig at ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, \"\\n\")}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst parsedCommandLine = ts.parseJsonConfigFileContent(\n\t\t\t\tconfigFile.config,\n\t\t\t\tts.sys,\n\t\t\t\tresolve(config.rootDir),\n\t\t\t);\n\n\t\t\tconst program = ts.createProgram({\n\t\t\t\trootNames: parsedCommandLine.fileNames,\n\t\t\t\toptions: parsedCommandLine.options,\n\t\t\t});\n\n\t\t\tconst checker = program.getTypeChecker();\n\n\t\t\tconst allSymbols: ForgeSymbol[] = [];\n\n\t\t\tfor (const sourceFile of program.getSourceFiles()) {\n\t\t\t\t// Skip declaration files and node_modules\n\t\t\t\tif (sourceFile.isDeclarationFile || sourceFile.fileName.includes(\"node_modules\")) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst fileSymbols = extractSymbolsFromFile(sourceFile, checker);\n\t\t\t\tallSymbols.push(...fileSymbols);\n\t\t\t}\n\n\t\t\treturn allSymbols;\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["existsSync","readFileSync","userInfo","join","getCurrentUser","userInfo","join","existsSync","readFileSync","existsSync","join","Visibility","join","existsSync","pkgPath","existsSync","readFileSync","writeFileSync","join","readFileSync","resolve","require","name","pos","rawComment","documentation","tags","visibility","resolve","readFileSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forge-ts/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared types, interfaces, and core AST walker for forge-ts",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"tsup": "^8.3.5",
|
|
36
|
-
"@forge-ts/
|
|
37
|
-
"@forge-ts/
|
|
38
|
-
"@forge-ts/gen": "0.
|
|
36
|
+
"@forge-ts/api": "0.19.0",
|
|
37
|
+
"@forge-ts/enforcer": "0.19.0",
|
|
38
|
+
"@forge-ts/gen": "0.19.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup",
|
package/tsdoc-preset/tsdoc.json
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
{ "tagName": "@faq", "syntaxKind": "block" },
|
|
16
16
|
{ "tagName": "@breaking", "syntaxKind": "block" },
|
|
17
17
|
{ "tagName": "@migration", "syntaxKind": "block" },
|
|
18
|
-
{ "tagName": "@complexity", "syntaxKind": "block" }
|
|
18
|
+
{ "tagName": "@complexity", "syntaxKind": "block" },
|
|
19
|
+
{ "tagName": "@forge-ignore", "syntaxKind": "modifier" }
|
|
19
20
|
],
|
|
20
21
|
"supportForTags": {
|
|
21
22
|
"@alpha": true,
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"@faq": true,
|
|
56
57
|
"@breaking": true,
|
|
57
58
|
"@migration": true,
|
|
58
|
-
"@complexity": true
|
|
59
|
+
"@complexity": true,
|
|
60
|
+
"@forge-ignore": true
|
|
59
61
|
}
|
|
60
62
|
}
|