@forge-ts/core 0.8.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,168 @@
1
+ // src/audit.ts
2
+ import { appendFileSync, existsSync, readFileSync } from "fs";
3
+ import { userInfo } from "os";
4
+ import { join } from "path";
5
+ var AUDIT_FILENAME = ".forge-audit.jsonl";
6
+ function getCurrentUser() {
7
+ try {
8
+ return userInfo().username;
9
+ } catch {
10
+ return "unknown";
11
+ }
12
+ }
13
+ function appendAuditEvent(rootDir, event) {
14
+ const filePath = join(rootDir, AUDIT_FILENAME);
15
+ appendFileSync(filePath, `${JSON.stringify(event)}
16
+ `, "utf-8");
17
+ }
18
+ function readAuditLog(rootDir, options) {
19
+ const filePath = join(rootDir, AUDIT_FILENAME);
20
+ if (!existsSync(filePath)) {
21
+ return [];
22
+ }
23
+ const raw = readFileSync(filePath, "utf-8");
24
+ const lines = raw.split("\n").filter((line) => line.trim().length > 0);
25
+ let events = lines.map((line) => JSON.parse(line));
26
+ if (options?.eventType) {
27
+ events = events.filter((e) => e.event === options.eventType);
28
+ }
29
+ events.reverse();
30
+ if (options?.limit !== void 0 && options.limit >= 0) {
31
+ events = events.slice(0, options.limit);
32
+ }
33
+ return events;
34
+ }
35
+ function formatAuditEvent(event) {
36
+ const reasonPart = event.reason ? ` \u2014 ${event.reason}` : "";
37
+ const detailKeys = Object.keys(event.details);
38
+ const detailPart = detailKeys.length > 0 ? ` ${JSON.stringify(event.details)}` : "";
39
+ return `[${event.timestamp}] ${event.event} by ${event.user}${reasonPart}${detailPart}`;
40
+ }
41
+
42
+ // src/bypass.ts
43
+ import { randomUUID } from "crypto";
44
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
45
+ import { userInfo as userInfo2 } from "os";
46
+ import { join as join2 } from "path";
47
+ var BYPASS_FILENAME = ".forge-bypass.json";
48
+ var DEFAULT_BYPASS_CONFIG = {
49
+ dailyBudget: 3,
50
+ durationHours: 24
51
+ };
52
+ function getCurrentUser2() {
53
+ try {
54
+ return userInfo2().username;
55
+ } catch {
56
+ return "unknown";
57
+ }
58
+ }
59
+ function readBypassFile(rootDir) {
60
+ const filePath = join2(rootDir, BYPASS_FILENAME);
61
+ if (!existsSync2(filePath)) {
62
+ return [];
63
+ }
64
+ try {
65
+ const raw = readFileSync2(filePath, "utf-8");
66
+ return JSON.parse(raw);
67
+ } catch {
68
+ return [];
69
+ }
70
+ }
71
+ function writeBypassFile(rootDir, records) {
72
+ const filePath = join2(rootDir, BYPASS_FILENAME);
73
+ writeFileSync(filePath, `${JSON.stringify(records, null, 2)}
74
+ `, "utf-8");
75
+ }
76
+ function resolveConfig(config) {
77
+ return {
78
+ ...DEFAULT_BYPASS_CONFIG,
79
+ ...config
80
+ };
81
+ }
82
+ function startOfToday() {
83
+ const now = /* @__PURE__ */ new Date();
84
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
85
+ }
86
+ function createBypass(rootDir, reason, rule, config) {
87
+ const resolved = resolveConfig(config);
88
+ const remaining = getRemainingBudget(rootDir, config);
89
+ if (remaining <= 0) {
90
+ throw new Error(
91
+ `Bypass budget exhausted: ${resolved.dailyBudget}/${resolved.dailyBudget} bypasses used today. Wait until tomorrow or increase bypass.dailyBudget in your forge-ts config.`
92
+ );
93
+ }
94
+ const now = /* @__PURE__ */ new Date();
95
+ const expiresAt = new Date(now.getTime() + resolved.durationHours * 60 * 60 * 1e3);
96
+ const record = {
97
+ id: randomUUID(),
98
+ createdAt: now.toISOString(),
99
+ expiresAt: expiresAt.toISOString(),
100
+ reason,
101
+ rule: rule ?? "all",
102
+ user: getCurrentUser2()
103
+ };
104
+ const records = readBypassFile(rootDir);
105
+ records.push(record);
106
+ writeBypassFile(rootDir, records);
107
+ appendAuditEvent(rootDir, {
108
+ timestamp: record.createdAt,
109
+ event: "bypass.create",
110
+ user: record.user,
111
+ reason,
112
+ details: {
113
+ bypassId: record.id,
114
+ rule: record.rule,
115
+ expiresAt: record.expiresAt,
116
+ durationHours: resolved.durationHours
117
+ }
118
+ });
119
+ return record;
120
+ }
121
+ function getActiveBypasses(rootDir) {
122
+ const records = readBypassFile(rootDir);
123
+ const now = /* @__PURE__ */ new Date();
124
+ return records.filter((r) => new Date(r.expiresAt) > now);
125
+ }
126
+ function isRuleBypassed(rootDir, ruleCode) {
127
+ const active = getActiveBypasses(rootDir);
128
+ return active.some((r) => r.rule === ruleCode || r.rule === "all");
129
+ }
130
+ function getRemainingBudget(rootDir, config) {
131
+ const resolved = resolveConfig(config);
132
+ const records = readBypassFile(rootDir);
133
+ const todayStart = startOfToday();
134
+ const todayCount = records.filter((r) => new Date(r.createdAt) >= todayStart).length;
135
+ return Math.max(0, resolved.dailyBudget - todayCount);
136
+ }
137
+ function expireOldBypasses(rootDir) {
138
+ const records = readBypassFile(rootDir);
139
+ const now = /* @__PURE__ */ new Date();
140
+ const active = records.filter((r) => new Date(r.expiresAt) > now);
141
+ const expired = records.filter((r) => new Date(r.expiresAt) <= now);
142
+ if (expired.length === 0) {
143
+ return 0;
144
+ }
145
+ writeBypassFile(rootDir, active);
146
+ for (const record of expired) {
147
+ appendAuditEvent(rootDir, {
148
+ timestamp: now.toISOString(),
149
+ event: "bypass.expire",
150
+ user: record.user,
151
+ details: {
152
+ bypassId: record.id,
153
+ rule: record.rule,
154
+ createdAt: record.createdAt,
155
+ expiresAt: record.expiresAt
156
+ }
157
+ });
158
+ }
159
+ return expired.length;
160
+ }
161
+
1
162
  // src/config.ts
2
- import { existsSync } from "fs";
163
+ import { existsSync as existsSync3 } from "fs";
3
164
  import { readFile } from "fs/promises";
4
- import { join, resolve } from "path";
165
+ import { join as join3, resolve } from "path";
5
166
  import { pathToFileURL } from "url";
6
167
 
7
168
  // src/types.ts
@@ -17,8 +178,8 @@ var Visibility = /* @__PURE__ */ ((Visibility2) => {
17
178
  function defaultConfig(rootDir) {
18
179
  return {
19
180
  rootDir,
20
- tsconfig: join(rootDir, "tsconfig.json"),
21
- outDir: join(rootDir, "docs"),
181
+ tsconfig: join3(rootDir, "tsconfig.json"),
182
+ outDir: join3(rootDir, "docs"),
22
183
  enforce: {
23
184
  enabled: true,
24
185
  minVisibility: "public" /* Public */,
@@ -30,17 +191,25 @@ function defaultConfig(rootDir) {
30
191
  "require-example": "error",
31
192
  "require-package-doc": "warn",
32
193
  "require-class-member-doc": "error",
33
- "require-interface-member-doc": "error"
194
+ "require-interface-member-doc": "error",
195
+ "require-tsdoc-syntax": "warn",
196
+ "require-remarks": "error",
197
+ "require-default-value": "warn",
198
+ "require-type-param": "error",
199
+ "require-see": "warn",
200
+ "require-release-tag": "error",
201
+ "require-fresh-guides": "warn",
202
+ "require-guide-coverage": "warn"
34
203
  }
35
204
  },
36
205
  doctest: {
37
206
  enabled: true,
38
- cacheDir: join(rootDir, ".cache", "doctest")
207
+ cacheDir: join3(rootDir, ".cache", "doctest")
39
208
  },
40
209
  api: {
41
210
  enabled: false,
42
211
  openapi: false,
43
- openapiPath: join(rootDir, "docs", "openapi.json")
212
+ openapiPath: join3(rootDir, "docs", "openapi.json")
44
213
  },
45
214
  gen: {
46
215
  enabled: true,
@@ -49,6 +218,39 @@ function defaultConfig(rootDir) {
49
218
  readmeSync: false
50
219
  },
51
220
  skill: {},
221
+ bypass: {
222
+ dailyBudget: 3,
223
+ durationHours: 24
224
+ },
225
+ tsdoc: {
226
+ writeConfig: true,
227
+ customTags: [],
228
+ enforce: {
229
+ core: "error",
230
+ extended: "warn",
231
+ discretionary: "off"
232
+ }
233
+ },
234
+ guides: {
235
+ enabled: true,
236
+ autoDiscover: true,
237
+ custom: []
238
+ },
239
+ guards: {
240
+ tsconfig: {
241
+ enabled: true,
242
+ requiredFlags: ["strict", "strictNullChecks", "noImplicitAny"]
243
+ },
244
+ biome: {
245
+ enabled: false,
246
+ lockedRules: []
247
+ },
248
+ packageJson: {
249
+ enabled: true,
250
+ minNodeVersion: "22.0.0",
251
+ requiredFields: ["type", "engines"]
252
+ }
253
+ },
52
254
  project: {}
53
255
  };
54
256
  }
@@ -61,6 +263,10 @@ var KNOWN_TOP_KEYS = /* @__PURE__ */ new Set([
61
263
  "api",
62
264
  "gen",
63
265
  "skill",
266
+ "bypass",
267
+ "tsdoc",
268
+ "guides",
269
+ "guards",
64
270
  "project"
65
271
  ]);
66
272
  var KNOWN_RULE_KEYS = /* @__PURE__ */ new Set([
@@ -70,8 +276,30 @@ var KNOWN_RULE_KEYS = /* @__PURE__ */ new Set([
70
276
  "require-example",
71
277
  "require-package-doc",
72
278
  "require-class-member-doc",
73
- "require-interface-member-doc"
279
+ "require-interface-member-doc",
280
+ "require-tsdoc-syntax",
281
+ "require-remarks",
282
+ "require-default-value",
283
+ "require-type-param",
284
+ "require-see",
285
+ "require-release-tag",
286
+ "require-fresh-guides",
287
+ "require-guide-coverage"
74
288
  ]);
289
+ var KNOWN_TSDOC_KEYS = /* @__PURE__ */ new Set(["writeConfig", "customTags", "enforce"]);
290
+ var KNOWN_TSDOC_ENFORCE_KEYS = /* @__PURE__ */ new Set(["core", "extended", "discretionary"]);
291
+ var KNOWN_GUIDES_KEYS = /* @__PURE__ */ new Set(["enabled", "autoDiscover", "custom"]);
292
+ var KNOWN_GUARDS_KEYS = /* @__PURE__ */ new Set(["tsconfig", "biome", "packageJson"]);
293
+ var KNOWN_GUARDS_TSCONFIG_KEYS = /* @__PURE__ */ new Set(["enabled", "requiredFlags"]);
294
+ var KNOWN_GUARDS_BIOME_KEYS = /* @__PURE__ */ new Set(["enabled", "lockedRules"]);
295
+ var KNOWN_GUARDS_PACKAGE_JSON_KEYS = /* @__PURE__ */ new Set(["enabled", "minNodeVersion", "requiredFields"]);
296
+ function validateKnownKeys(obj, knownKeys, section, warnings) {
297
+ for (const key of Object.keys(obj)) {
298
+ if (!knownKeys.has(key)) {
299
+ warnings.push(`Unknown key "${key}" in ${section} \u2014 ignored.`);
300
+ }
301
+ }
302
+ }
75
303
  function collectUnknownKeyWarnings(partial) {
76
304
  const warnings = [];
77
305
  for (const key of Object.keys(partial)) {
@@ -88,6 +316,62 @@ function collectUnknownKeyWarnings(partial) {
88
316
  }
89
317
  }
90
318
  }
319
+ if (partial.tsdoc) {
320
+ validateKnownKeys(
321
+ partial.tsdoc,
322
+ KNOWN_TSDOC_KEYS,
323
+ "tsdoc",
324
+ warnings
325
+ );
326
+ if (partial.tsdoc.enforce) {
327
+ validateKnownKeys(
328
+ partial.tsdoc.enforce,
329
+ KNOWN_TSDOC_ENFORCE_KEYS,
330
+ "tsdoc.enforce",
331
+ warnings
332
+ );
333
+ }
334
+ }
335
+ if (partial.guides) {
336
+ validateKnownKeys(
337
+ partial.guides,
338
+ KNOWN_GUIDES_KEYS,
339
+ "guides",
340
+ warnings
341
+ );
342
+ }
343
+ if (partial.guards) {
344
+ validateKnownKeys(
345
+ partial.guards,
346
+ KNOWN_GUARDS_KEYS,
347
+ "guards",
348
+ warnings
349
+ );
350
+ if (partial.guards.tsconfig) {
351
+ validateKnownKeys(
352
+ partial.guards.tsconfig,
353
+ KNOWN_GUARDS_TSCONFIG_KEYS,
354
+ "guards.tsconfig",
355
+ warnings
356
+ );
357
+ }
358
+ if (partial.guards.biome) {
359
+ validateKnownKeys(
360
+ partial.guards.biome,
361
+ KNOWN_GUARDS_BIOME_KEYS,
362
+ "guards.biome",
363
+ warnings
364
+ );
365
+ }
366
+ if (partial.guards.packageJson) {
367
+ validateKnownKeys(
368
+ partial.guards.packageJson,
369
+ KNOWN_GUARDS_PACKAGE_JSON_KEYS,
370
+ "guards.packageJson",
371
+ warnings
372
+ );
373
+ }
374
+ }
91
375
  for (const w of warnings) {
92
376
  console.error(`[forge-ts] warning: ${w}`);
93
377
  }
@@ -108,6 +392,20 @@ function mergeWithDefaults(rootDir, partial) {
108
392
  api: { ...defaults.api, ...partial.api },
109
393
  gen: { ...defaults.gen, ...partial.gen },
110
394
  skill: { ...defaults.skill, ...partial.skill },
395
+ bypass: { ...defaults.bypass, ...partial.bypass },
396
+ guides: { ...defaults.guides, ...partial.guides },
397
+ tsdoc: {
398
+ ...defaults.tsdoc,
399
+ ...partial.tsdoc,
400
+ enforce: { ...defaults.tsdoc.enforce, ...partial.tsdoc?.enforce }
401
+ },
402
+ guards: {
403
+ ...defaults.guards,
404
+ ...partial.guards,
405
+ tsconfig: { ...defaults.guards.tsconfig, ...partial.guards?.tsconfig },
406
+ biome: { ...defaults.guards.biome, ...partial.guards?.biome },
407
+ packageJson: { ...defaults.guards.packageJson, ...partial.guards?.packageJson }
408
+ },
111
409
  project: { ...defaults.project, ...partial.project }
112
410
  };
113
411
  if (warnings.length > 0) {
@@ -150,11 +448,11 @@ async function loadPackageJsonConfig(pkgPath) {
150
448
  async function loadConfig(rootDir) {
151
449
  const root = resolve(rootDir ?? process.cwd());
152
450
  let config;
153
- const candidates = [join(root, "forge-ts.config.ts"), join(root, "forge-ts.config.js")];
451
+ const candidates = [join3(root, "forge-ts.config.ts"), join3(root, "forge-ts.config.js")];
154
452
  let found = false;
155
453
  const loadWarnings = [];
156
454
  for (const candidate of candidates) {
157
- if (existsSync(candidate)) {
455
+ if (existsSync3(candidate)) {
158
456
  const partial = await loadModuleConfig(candidate);
159
457
  if (partial) {
160
458
  config = mergeWithDefaults(root, partial);
@@ -167,8 +465,8 @@ async function loadConfig(rootDir) {
167
465
  }
168
466
  }
169
467
  if (!found) {
170
- const pkgPath2 = join(root, "package.json");
171
- if (existsSync(pkgPath2)) {
468
+ const pkgPath2 = join3(root, "package.json");
469
+ if (existsSync3(pkgPath2)) {
172
470
  const partial = await loadPackageJsonConfig(pkgPath2);
173
471
  if (partial) {
174
472
  config = mergeWithDefaults(root, partial);
@@ -181,8 +479,8 @@ async function loadConfig(rootDir) {
181
479
  } else {
182
480
  config = config;
183
481
  }
184
- const pkgPath = join(root, "package.json");
185
- if (existsSync(pkgPath)) {
482
+ const pkgPath = join3(root, "package.json");
483
+ if (existsSync3(pkgPath)) {
186
484
  try {
187
485
  const raw = await readFile(pkgPath, "utf8");
188
486
  const pkg = JSON.parse(raw);
@@ -224,6 +522,135 @@ async function loadConfig(rootDir) {
224
522
  return config;
225
523
  }
226
524
 
525
+ // src/lock.ts
526
+ import { existsSync as existsSync4, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
527
+ import { join as join4 } from "path";
528
+ var LOCK_FILE_NAME = ".forge-lock.json";
529
+ function readLockFile(rootDir) {
530
+ const lockPath = join4(rootDir, LOCK_FILE_NAME);
531
+ if (!existsSync4(lockPath)) {
532
+ return null;
533
+ }
534
+ try {
535
+ const raw = readFileSync3(lockPath, "utf8");
536
+ return JSON.parse(raw);
537
+ } catch {
538
+ return null;
539
+ }
540
+ }
541
+ function writeLockFile(rootDir, manifest) {
542
+ const lockPath = join4(rootDir, LOCK_FILE_NAME);
543
+ writeFileSync2(lockPath, `${JSON.stringify(manifest, null, 2)}
544
+ `, "utf8");
545
+ }
546
+ function removeLockFile(rootDir) {
547
+ const lockPath = join4(rootDir, LOCK_FILE_NAME);
548
+ if (!existsSync4(lockPath)) {
549
+ return false;
550
+ }
551
+ try {
552
+ unlinkSync(lockPath);
553
+ return true;
554
+ } catch {
555
+ return false;
556
+ }
557
+ }
558
+ function createLockManifest(config, lockedBy = "forge-ts lock") {
559
+ const rules = {};
560
+ for (const [key, value] of Object.entries(config.enforce.rules)) {
561
+ rules[key] = value;
562
+ }
563
+ const lockConfig = { rules };
564
+ if (config.guards.tsconfig.enabled) {
565
+ lockConfig.tsconfig = {
566
+ enabled: config.guards.tsconfig.enabled,
567
+ requiredFlags: config.guards.tsconfig.requiredFlags
568
+ };
569
+ }
570
+ if (config.guards.biome.enabled) {
571
+ lockConfig.biome = {
572
+ enabled: config.guards.biome.enabled,
573
+ lockedRules: config.guards.biome.lockedRules
574
+ };
575
+ }
576
+ return {
577
+ version: "1.0.0",
578
+ lockedAt: (/* @__PURE__ */ new Date()).toISOString(),
579
+ lockedBy,
580
+ config: lockConfig
581
+ };
582
+ }
583
+ function validateAgainstLock(config, lock) {
584
+ const violations = [];
585
+ const severityRank = {
586
+ off: 0,
587
+ warn: 1,
588
+ error: 2
589
+ };
590
+ for (const [ruleName, lockedSeverity] of Object.entries(lock.config.rules)) {
591
+ const currentSeverity = config.enforce.rules[ruleName] ?? "off";
592
+ const lockedRank = severityRank[lockedSeverity] ?? 0;
593
+ const currentRank = severityRank[currentSeverity] ?? 0;
594
+ if (currentRank < lockedRank) {
595
+ violations.push({
596
+ field: `rules.${ruleName}`,
597
+ locked: lockedSeverity,
598
+ current: currentSeverity,
599
+ message: `Rule "${ruleName}" was weakened from "${lockedSeverity}" to "${currentSeverity}". Locked settings cannot be weakened without running "forge-ts unlock --reason=...".`
600
+ });
601
+ }
602
+ }
603
+ if (lock.config.tsconfig) {
604
+ const lockedTsconfig = lock.config.tsconfig;
605
+ if (lockedTsconfig.enabled && !config.guards.tsconfig.enabled) {
606
+ violations.push({
607
+ field: "guards.tsconfig.enabled",
608
+ locked: "true",
609
+ current: "false",
610
+ message: 'tsconfig guard was disabled. Locked settings cannot be weakened without running "forge-ts unlock --reason=...".'
611
+ });
612
+ }
613
+ if (lockedTsconfig.requiredFlags && config.guards.tsconfig.enabled) {
614
+ const currentFlags = new Set(config.guards.tsconfig.requiredFlags);
615
+ for (const flag of lockedTsconfig.requiredFlags) {
616
+ if (!currentFlags.has(flag)) {
617
+ violations.push({
618
+ field: `guards.tsconfig.requiredFlags.${flag}`,
619
+ locked: flag,
620
+ current: "(removed)",
621
+ message: `tsconfig required flag "${flag}" was removed. Locked settings cannot be weakened without running "forge-ts unlock --reason=...".`
622
+ });
623
+ }
624
+ }
625
+ }
626
+ }
627
+ if (lock.config.biome) {
628
+ const lockedBiome = lock.config.biome;
629
+ if (lockedBiome.enabled && !config.guards.biome.enabled) {
630
+ violations.push({
631
+ field: "guards.biome.enabled",
632
+ locked: "true",
633
+ current: "false",
634
+ message: 'Biome guard was disabled. Locked settings cannot be weakened without running "forge-ts unlock --reason=...".'
635
+ });
636
+ }
637
+ if (lockedBiome.lockedRules && config.guards.biome.enabled) {
638
+ const currentRules = new Set(config.guards.biome.lockedRules);
639
+ for (const rule of lockedBiome.lockedRules) {
640
+ if (!currentRules.has(rule)) {
641
+ violations.push({
642
+ field: `guards.biome.lockedRules.${rule}`,
643
+ locked: rule,
644
+ current: "(removed)",
645
+ message: `Biome locked rule "${rule}" was removed. Locked settings cannot be weakened without running "forge-ts unlock --reason=...".`
646
+ });
647
+ }
648
+ }
649
+ }
650
+ }
651
+ return violations;
652
+ }
653
+
227
654
  // src/visibility.ts
228
655
  function resolveVisibility(tags) {
229
656
  if (!tags) return "public" /* Public */;
@@ -246,15 +673,34 @@ function filterByVisibility(symbols, minVisibility) {
246
673
  }
247
674
 
248
675
  // src/walker.ts
249
- import { readFileSync } from "fs";
250
- import { resolve as resolve2 } from "path";
676
+ import { readFileSync as readFileSync4 } from "fs";
677
+ import { dirname, resolve as resolve2 } from "path";
251
678
  import {
252
679
  DocNodeKind,
253
680
  StandardTags,
254
681
  TSDocConfiguration,
255
682
  TSDocParser
256
683
  } from "@microsoft/tsdoc";
684
+ import { TSDocConfigFile } from "@microsoft/tsdoc-config";
257
685
  import ts from "typescript";
686
+ var tsdocConfigCache = /* @__PURE__ */ new Map();
687
+ function clearTSDocConfigCache() {
688
+ tsdocConfigCache.clear();
689
+ }
690
+ function loadTSDocConfiguration(folderPath) {
691
+ const cached = tsdocConfigCache.get(folderPath);
692
+ if (cached) return cached;
693
+ const configuration = new TSDocConfiguration();
694
+ try {
695
+ const configFile = TSDocConfigFile.loadForFolder(folderPath);
696
+ if (!configFile.fileNotFound && !configFile.hasErrors) {
697
+ configFile.configureParser(configuration);
698
+ }
699
+ } catch {
700
+ }
701
+ tsdocConfigCache.set(folderPath, configuration);
702
+ return configuration;
703
+ }
258
704
  function renderInlineNodes(nodes) {
259
705
  const parts = [];
260
706
  for (const node of nodes) {
@@ -309,8 +755,8 @@ function extractExamples(comment, startLine) {
309
755
  }
310
756
  return examples;
311
757
  }
312
- function parseTSDoc(rawComment, startLine) {
313
- const configuration = new TSDocConfiguration();
758
+ function parseTSDoc(rawComment, startLine, folderPath) {
759
+ const configuration = folderPath !== void 0 ? loadTSDocConfiguration(folderPath) : new TSDocConfiguration();
314
760
  const parser = new TSDocParser(configuration);
315
761
  const result = parser.parseString(rawComment);
316
762
  const comment = result.docComment;
@@ -362,6 +808,43 @@ function parseTSDoc(rawComment, startLine) {
362
808
  }
363
809
  }
364
810
  }
811
+ if (comment.remarksBlock) {
812
+ const remarksText = renderBlock(comment.remarksBlock).trim();
813
+ tags.remarks = remarksText ? [remarksText] : [];
814
+ }
815
+ if (comment.seeBlocks.length > 0) {
816
+ tags.see = comment.seeBlocks.map((block) => renderBlock(block).trim()).filter(Boolean);
817
+ }
818
+ if (comment.typeParams.count > 0) {
819
+ tags.typeParam = comment.typeParams.blocks.map(
820
+ (block) => `${block.parameterName} - ${renderBlock(block).trim()}`
821
+ );
822
+ }
823
+ for (const block of comment.customBlocks) {
824
+ if (block.blockTag.tagName.toLowerCase() === "@defaultvalue") {
825
+ const dvText = renderBlock(block).trim();
826
+ if (!tags.defaultValue) tags.defaultValue = [];
827
+ tags.defaultValue.push(dvText);
828
+ }
829
+ }
830
+ for (const block of comment.customBlocks) {
831
+ if (block.blockTag.tagName.toLowerCase() === "@concept") {
832
+ const conceptText = renderBlock(block).trim();
833
+ if (conceptText) {
834
+ if (!tags.concept) tags.concept = [];
835
+ tags.concept.push(conceptText);
836
+ }
837
+ }
838
+ }
839
+ for (const block of comment.customBlocks) {
840
+ if (block.blockTag.tagName.toLowerCase() === "@guide") {
841
+ const guideText = renderBlock(block).trim();
842
+ if (guideText) {
843
+ if (!tags.guide) tags.guide = [];
844
+ tags.guide.push(guideText);
845
+ }
846
+ }
847
+ }
365
848
  const examples = extractExamples(comment, startLine);
366
849
  const links = [];
367
850
  function walkForLinks(node) {
@@ -379,6 +862,14 @@ function parseTSDoc(rawComment, startLine) {
379
862
  }
380
863
  }
381
864
  walkForLinks(comment);
865
+ const parseMessages = [];
866
+ for (const msg of result.log.messages) {
867
+ parseMessages.push({
868
+ messageId: msg.messageId,
869
+ text: msg.unformattedText,
870
+ line: startLine
871
+ });
872
+ }
382
873
  const summary = renderDocSection(comment.summarySection);
383
874
  return {
384
875
  summary: summary || void 0,
@@ -388,7 +879,8 @@ function parseTSDoc(rawComment, startLine) {
388
879
  examples: examples.length > 0 ? examples : void 0,
389
880
  tags: Object.keys(tags).length > 0 ? tags : void 0,
390
881
  deprecated,
391
- links: links.length > 0 ? links : void 0
882
+ links: links.length > 0 ? links : void 0,
883
+ parseMessages: parseMessages.length > 0 ? parseMessages : void 0
392
884
  };
393
885
  }
394
886
  function getLeadingComment(node, sourceFile) {
@@ -439,9 +931,10 @@ function buildSignature(node, checker) {
439
931
  return void 0;
440
932
  }
441
933
  }
442
- function extractSymbolsFromFile(sourceFile, checker, _tsdocParser) {
934
+ function extractSymbolsFromFile(sourceFile, checker) {
443
935
  const symbols = [];
444
936
  const filePath = sourceFile.fileName;
937
+ const fileDir = dirname(filePath);
445
938
  function visit(node, parentExported) {
446
939
  const isExported = parentExported || (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0;
447
940
  if (ts.isExportDeclaration(node)) {
@@ -458,7 +951,7 @@ function extractSymbolsFromFile(sourceFile, checker, _tsdocParser) {
458
951
  const name2 = decl.name.getText(sourceFile);
459
952
  const pos2 = sourceFile.getLineAndCharacterOfPosition(decl.getStart());
460
953
  const rawComment2 = getLeadingComment(node, sourceFile);
461
- const documentation2 = rawComment2 ? parseTSDoc(rawComment2, pos2.line + 1) : void 0;
954
+ const documentation2 = rawComment2 ? parseTSDoc(rawComment2, pos2.line + 1, fileDir) : void 0;
462
955
  const tags2 = documentation2?.tags;
463
956
  const visibility2 = resolveVisibility(tags2);
464
957
  symbols.push({
@@ -488,7 +981,7 @@ function extractSymbolsFromFile(sourceFile, checker, _tsdocParser) {
488
981
  const name = nameNode.getText(sourceFile);
489
982
  const pos = sourceFile.getLineAndCharacterOfPosition(node.getStart());
490
983
  const rawComment = getLeadingComment(node, sourceFile);
491
- const documentation = rawComment ? parseTSDoc(rawComment, pos.line + 1) : void 0;
984
+ const documentation = rawComment ? parseTSDoc(rawComment, pos.line + 1, fileDir) : void 0;
492
985
  const tags = documentation?.tags;
493
986
  const visibility = resolveVisibility(tags);
494
987
  const children = [];
@@ -499,7 +992,7 @@ function extractSymbolsFromFile(sourceFile, checker, _tsdocParser) {
499
992
  const memberName = member.name?.getText(sourceFile) ?? "";
500
993
  const memberPos = sourceFile.getLineAndCharacterOfPosition(member.getStart());
501
994
  const memberComment = getLeadingComment(member, sourceFile);
502
- const memberDoc = memberComment ? parseTSDoc(memberComment, memberPos.line + 1) : void 0;
995
+ const memberDoc = memberComment ? parseTSDoc(memberComment, memberPos.line + 1, fileDir) : void 0;
503
996
  const memberTags = memberDoc?.tags;
504
997
  const memberVisibility = resolveVisibility(memberTags);
505
998
  children.push({
@@ -535,7 +1028,7 @@ function createWalker(config) {
535
1028
  return {
536
1029
  walk() {
537
1030
  const tsconfigPath = resolve2(config.tsconfig);
538
- const configFile = ts.readConfigFile(tsconfigPath, (path) => readFileSync(path, "utf8"));
1031
+ const configFile = ts.readConfigFile(tsconfigPath, (path) => readFileSync4(path, "utf8"));
539
1032
  if (configFile.error) {
540
1033
  throw new Error(
541
1034
  `Failed to read tsconfig at ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
@@ -551,14 +1044,12 @@ function createWalker(config) {
551
1044
  options: parsedCommandLine.options
552
1045
  });
553
1046
  const checker = program.getTypeChecker();
554
- const tsdocConfiguration = new TSDocConfiguration();
555
- const tsdocParser = new TSDocParser(tsdocConfiguration);
556
1047
  const allSymbols = [];
557
1048
  for (const sourceFile of program.getSourceFiles()) {
558
1049
  if (sourceFile.isDeclarationFile || sourceFile.fileName.includes("node_modules")) {
559
1050
  continue;
560
1051
  }
561
- const fileSymbols = extractSymbolsFromFile(sourceFile, checker, tsdocParser);
1052
+ const fileSymbols = extractSymbolsFromFile(sourceFile, checker);
562
1053
  allSymbols.push(...fileSymbols);
563
1054
  }
564
1055
  return allSymbols;
@@ -567,11 +1058,27 @@ function createWalker(config) {
567
1058
  }
568
1059
  export {
569
1060
  Visibility,
1061
+ appendAuditEvent,
1062
+ clearTSDocConfigCache,
1063
+ createBypass,
1064
+ createLockManifest,
570
1065
  createWalker,
571
1066
  defaultConfig,
1067
+ expireOldBypasses,
572
1068
  filterByVisibility,
1069
+ formatAuditEvent,
1070
+ getActiveBypasses,
1071
+ getCurrentUser,
1072
+ getRemainingBudget,
1073
+ isRuleBypassed,
573
1074
  loadConfig,
1075
+ loadTSDocConfiguration,
574
1076
  meetsVisibility,
575
- resolveVisibility
1077
+ readAuditLog,
1078
+ readLockFile,
1079
+ removeLockFile,
1080
+ resolveVisibility,
1081
+ validateAgainstLock,
1082
+ writeLockFile
576
1083
  };
577
1084
  //# sourceMappingURL=index.js.map