@zudojs/docs 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +32 -9
  2. package/dist/docsTypes/index.d.ts +1 -1
  3. package/dist/document/documentBuilder.core.d.ts +15 -5
  4. package/dist/document/documentBuilder.core.js +27 -15
  5. package/dist/document/index.d.ts +1 -1
  6. package/dist/errors/index.d.ts +16 -0
  7. package/dist/errors/index.js +18 -0
  8. package/dist/examples/examples.core.d.ts +4 -0
  9. package/dist/examples/examples.core.js +16 -7
  10. package/dist/frontmatter/frontmatter.core.d.ts +1 -1
  11. package/dist/frontmatter/frontmatter.parser.d.ts +15 -2
  12. package/dist/frontmatter/frontmatter.parser.js +202 -39
  13. package/dist/frontmatter/frontmatter.serializer.d.ts +13 -1
  14. package/dist/frontmatter/frontmatter.serializer.js +89 -4
  15. package/dist/frontmatter/frontmatter.types.d.ts +10 -1
  16. package/dist/frontmatter/index.d.ts +1 -1
  17. package/dist/generator/generator.types.d.ts +18 -0
  18. package/dist/generator/generatorJson.core.d.ts +8 -3
  19. package/dist/generator/generatorJson.core.js +16 -4
  20. package/dist/generator/generatorMarkdown.core.d.ts +4 -0
  21. package/dist/generator/generatorMarkdown.core.js +32 -13
  22. package/dist/generator/generatorMarkdownNodes.d.ts +20 -0
  23. package/dist/generator/generatorMarkdownNodes.js +87 -17
  24. package/dist/generator/index.d.ts +2 -2
  25. package/dist/generator/index.js +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +2 -0
  28. package/dist/navigation/index.d.ts +1 -1
  29. package/dist/navigation/index.js +1 -1
  30. package/dist/navigation/navigation.core.d.ts +25 -6
  31. package/dist/navigation/navigation.core.js +93 -25
  32. package/dist/registry/index.d.ts +2 -1
  33. package/dist/registry/index.js +1 -1
  34. package/dist/registry/registry.core.d.ts +29 -3
  35. package/dist/registry/registry.core.js +32 -6
  36. package/dist/utils/index.d.ts +2 -1
  37. package/dist/utils/index.js +2 -1
  38. package/dist/utils/utils.freeze.d.ts +16 -0
  39. package/dist/utils/utils.freeze.js +53 -0
  40. package/dist/utils/utils.helper.d.ts +27 -4
  41. package/dist/utils/utils.helper.js +108 -21
  42. package/dist/validator/index.d.ts +4 -1
  43. package/dist/validator/index.js +2 -1
  44. package/dist/validator/validator.types.d.ts +8 -0
  45. package/dist/validator/validator.types.js +10 -1
  46. package/dist/validator/validatorAll/validatorAll.core.d.ts +11 -2
  47. package/dist/validator/validatorAll/validatorAll.core.js +19 -7
  48. package/dist/validator/validatorAll/validatorNavigation.core.d.ts +8 -1
  49. package/dist/validator/validatorAll/validatorNavigation.core.js +64 -10
  50. package/dist/validator/validatorDocument.core.d.ts +3 -2
  51. package/dist/validator/validatorDocument.core.js +197 -15
  52. package/dist/validator/validatorDuplicates.core.d.ts +1 -1
  53. package/dist/validator/validatorDuplicates.core.js +4 -6
  54. package/dist/validator/validatorLinks.core.d.ts +22 -4
  55. package/dist/validator/validatorLinks.core.js +80 -44
  56. package/package.json +18 -9
  57. package/dist/docsTypes/documentationContent.d.ts.map +0 -1
  58. package/dist/docsTypes/documentationContent.js.map +0 -1
  59. package/dist/docsTypes/documentationDocument.d.ts.map +0 -1
  60. package/dist/docsTypes/documentationDocument.js.map +0 -1
  61. package/dist/docsTypes/documentationMetadata.d.ts.map +0 -1
  62. package/dist/docsTypes/documentationMetadata.js.map +0 -1
  63. package/dist/docsTypes/documentationNavigation.d.ts.map +0 -1
  64. package/dist/docsTypes/documentationNavigation.js.map +0 -1
  65. package/dist/docsTypes/documentationProvider.d.ts.map +0 -1
  66. package/dist/docsTypes/documentationProvider.js.map +0 -1
  67. package/dist/docsTypes/index.d.ts.map +0 -1
  68. package/dist/docsTypes/index.js.map +0 -1
  69. package/dist/document/documentBuilder.core.d.ts.map +0 -1
  70. package/dist/document/documentBuilder.core.js.map +0 -1
  71. package/dist/document/index.d.ts.map +0 -1
  72. package/dist/document/index.js.map +0 -1
  73. package/dist/examples/examples.core.d.ts.map +0 -1
  74. package/dist/examples/examples.core.js.map +0 -1
  75. package/dist/examples/index.d.ts.map +0 -1
  76. package/dist/examples/index.js.map +0 -1
  77. package/dist/frontmatter/frontmatter.core.d.ts.map +0 -1
  78. package/dist/frontmatter/frontmatter.core.js.map +0 -1
  79. package/dist/frontmatter/frontmatter.parser.d.ts.map +0 -1
  80. package/dist/frontmatter/frontmatter.parser.js.map +0 -1
  81. package/dist/frontmatter/frontmatter.serializer.d.ts.map +0 -1
  82. package/dist/frontmatter/frontmatter.serializer.js.map +0 -1
  83. package/dist/frontmatter/frontmatter.types.d.ts.map +0 -1
  84. package/dist/frontmatter/frontmatter.types.js.map +0 -1
  85. package/dist/frontmatter/index.d.ts.map +0 -1
  86. package/dist/frontmatter/index.js.map +0 -1
  87. package/dist/generator/generator.types.d.ts.map +0 -1
  88. package/dist/generator/generator.types.js.map +0 -1
  89. package/dist/generator/generatorJson.core.d.ts.map +0 -1
  90. package/dist/generator/generatorJson.core.js.map +0 -1
  91. package/dist/generator/generatorMarkdown.core.d.ts.map +0 -1
  92. package/dist/generator/generatorMarkdown.core.js.map +0 -1
  93. package/dist/generator/generatorMarkdownNodes.d.ts.map +0 -1
  94. package/dist/generator/generatorMarkdownNodes.js.map +0 -1
  95. package/dist/generator/index.d.ts.map +0 -1
  96. package/dist/generator/index.js.map +0 -1
  97. package/dist/index.d.ts.map +0 -1
  98. package/dist/index.js.map +0 -1
  99. package/dist/navigation/index.d.ts.map +0 -1
  100. package/dist/navigation/index.js.map +0 -1
  101. package/dist/navigation/navigation.core.d.ts.map +0 -1
  102. package/dist/navigation/navigation.core.js.map +0 -1
  103. package/dist/registry/index.d.ts.map +0 -1
  104. package/dist/registry/index.js.map +0 -1
  105. package/dist/registry/registry.core.d.ts.map +0 -1
  106. package/dist/registry/registry.core.js.map +0 -1
  107. package/dist/utils/index.d.ts.map +0 -1
  108. package/dist/utils/index.js.map +0 -1
  109. package/dist/utils/utils.helper.d.ts.map +0 -1
  110. package/dist/utils/utils.helper.js.map +0 -1
  111. package/dist/validator/index.d.ts.map +0 -1
  112. package/dist/validator/index.js.map +0 -1
  113. package/dist/validator/validator.types.d.ts.map +0 -1
  114. package/dist/validator/validator.types.js.map +0 -1
  115. package/dist/validator/validatorAll/index.d.ts.map +0 -1
  116. package/dist/validator/validatorAll/index.js.map +0 -1
  117. package/dist/validator/validatorAll/validatorAll.core.d.ts.map +0 -1
  118. package/dist/validator/validatorAll/validatorAll.core.js.map +0 -1
  119. package/dist/validator/validatorAll/validatorNavigation.core.d.ts.map +0 -1
  120. package/dist/validator/validatorAll/validatorNavigation.core.js.map +0 -1
  121. package/dist/validator/validatorDocument.core.d.ts.map +0 -1
  122. package/dist/validator/validatorDocument.core.js.map +0 -1
  123. package/dist/validator/validatorDuplicates.core.d.ts.map +0 -1
  124. package/dist/validator/validatorDuplicates.core.js.map +0 -1
  125. package/dist/validator/validatorLinks.core.d.ts.map +0 -1
  126. package/dist/validator/validatorLinks.core.js.map +0 -1
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { validateDocument } from "./validatorDocument.core.js";
7
7
  export { validateNoDuplicateIds } from "./validatorDuplicates.core.js";
8
- export { validateLinks } from "./validatorLinks.core.js";
8
+ export { validateLinks, DEFAULT_MAX_LINK_SCAN_LENGTH, } from "./validatorLinks.core.js";
9
9
  export { validateAll, validateNavigation } from "./validatorAll/index.js";
10
+ export { toValidationResult } from "./validator.types.js";
10
11
  //# sourceMappingURL=index.js.map
@@ -12,9 +12,17 @@ export interface ValidationIssue {
12
12
  }
13
13
  /**
14
14
  * Result of validating documentation.
15
+ *
16
+ * `valid` is `false` only when at least one issue has severity
17
+ * `"error"`; warnings never make a result invalid.
15
18
  */
16
19
  export interface ValidationResult {
17
20
  readonly valid: boolean;
18
21
  readonly issues: readonly ValidationIssue[];
19
22
  }
23
+ /**
24
+ * Builds a `ValidationResult` from a list of issues using the single
25
+ * rule shared by every validator in this package.
26
+ */
27
+ export declare function toValidationResult(issues: readonly ValidationIssue[]): ValidationResult;
20
28
  //# sourceMappingURL=validator.types.d.ts.map
@@ -1,5 +1,14 @@
1
1
  /**
2
2
  * Type definitions for document validation.
3
3
  */
4
- export {};
4
+ /**
5
+ * Builds a `ValidationResult` from a list of issues using the single
6
+ * rule shared by every validator in this package.
7
+ */
8
+ export function toValidationResult(issues) {
9
+ return {
10
+ valid: !issues.some((issue) => issue.severity === "error"),
11
+ issues,
12
+ };
13
+ }
5
14
  //# sourceMappingURL=validator.types.js.map
@@ -2,9 +2,18 @@
2
2
  * Validates an entire documentation set.
3
3
  */
4
4
  import type { DocumentationDocument, DocumentationNavigationItem } from "../../docsTypes/index.js";
5
- import type { ValidationResult } from "../validator.types.js";
5
+ import { type ValidationResult } from "../validator.types.js";
6
+ import { type ValidateLinksOptions } from "../validatorLinks.core.js";
7
+ /** Options for `validateAll`. */
8
+ export interface ValidateAllOptions extends ValidateLinksOptions {
9
+ /**
10
+ * When a navigation tree is supplied, report documents that do not
11
+ * appear in it as `NAVIGATION_ORPHAN_DOCUMENT` warnings. Default true.
12
+ */
13
+ readonly reportOrphans?: boolean;
14
+ }
6
15
  /**
7
16
  * Validates all documents and optionally a navigation tree.
8
17
  */
9
- export declare function validateAll(documents: readonly DocumentationDocument[], navigation?: readonly DocumentationNavigationItem[]): ValidationResult;
18
+ export declare function validateAll(documents: readonly DocumentationDocument[], navigation?: readonly DocumentationNavigationItem[], options?: ValidateAllOptions): ValidationResult;
10
19
  //# sourceMappingURL=validatorAll.core.d.ts.map
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * Validates an entire documentation set.
3
3
  */
4
+ import { flattenNavigation } from "../../navigation/navigation.core.js";
5
+ import { toValidationResult, } from "../validator.types.js";
4
6
  import { validateDocument } from "../validatorDocument.core.js";
5
7
  import { validateNoDuplicateIds } from "../validatorDuplicates.core.js";
6
- import { validateLinks } from "../validatorLinks.core.js";
8
+ import { validateLinks, } from "../validatorLinks.core.js";
7
9
  import { validateNavigation } from "./validatorNavigation.core.js";
8
10
  /**
9
11
  * Validates all documents and optionally a navigation tree.
10
12
  */
11
- export function validateAll(documents, navigation) {
13
+ export function validateAll(documents, navigation, options = {}) {
12
14
  const allIssues = [];
13
15
  const idResult = validateNoDuplicateIds(documents);
14
16
  allIssues.push(...idResult.issues);
@@ -16,16 +18,26 @@ export function validateAll(documents, navigation) {
16
18
  for (const doc of documents) {
17
19
  const docResult = validateDocument(doc);
18
20
  allIssues.push(...docResult.issues);
19
- const linkResult = validateLinks(doc, registeredIds);
21
+ const linkResult = validateLinks(doc, registeredIds, options);
20
22
  allIssues.push(...linkResult.issues);
21
23
  }
22
24
  if (navigation) {
23
25
  const navResult = validateNavigation(navigation, registeredIds);
24
26
  allIssues.push(...navResult.issues);
27
+ if (options.reportOrphans ?? true) {
28
+ const inNavigation = new Set(flattenNavigation(navigation));
29
+ for (const id of registeredIds) {
30
+ if (!inNavigation.has(id)) {
31
+ allIssues.push({
32
+ severity: "warning",
33
+ code: "NAVIGATION_ORPHAN_DOCUMENT",
34
+ message: `Document "${id}" is not reachable from the navigation.`,
35
+ documentId: id,
36
+ });
37
+ }
38
+ }
39
+ }
25
40
  }
26
- return {
27
- valid: allIssues.filter((i) => i.severity === "error").length === 0,
28
- issues: allIssues,
29
- };
41
+ return toValidationResult(allIssues);
30
42
  }
31
43
  //# sourceMappingURL=validatorAll.core.js.map
@@ -2,9 +2,16 @@
2
2
  * Validates navigation tree references against registered documents.
3
3
  */
4
4
  import type { DocumentationNavigationItem } from "../../docsTypes/index.js";
5
- import type { ValidationResult } from "../validator.types.js";
5
+ import { type ValidationResult } from "../validator.types.js";
6
6
  /**
7
7
  * Validates navigation tree references.
8
+ *
9
+ * Reports `NAVIGATION_UNKNOWN_DOCUMENT` (error) for references to
10
+ * unregistered documents, `NAVIGATION_CYCLE` (error) when a node is
11
+ * its own ancestor or the tree is deeper than `MAX_NAVIGATION_DEPTH`,
12
+ * `NAVIGATION_DUPLICATE_DOCUMENT` (warning) when a document is
13
+ * referenced more than once, and `NAVIGATION_EMPTY_ITEM` (warning)
14
+ * for items with neither `documentId` nor `children`.
8
15
  */
9
16
  export declare function validateNavigation(items: readonly DocumentationNavigationItem[], registeredIds: ReadonlySet<string>): ValidationResult;
10
17
  //# sourceMappingURL=validatorNavigation.core.d.ts.map
@@ -1,29 +1,83 @@
1
1
  /**
2
2
  * Validates navigation tree references against registered documents.
3
3
  */
4
+ import { MAX_NAVIGATION_DEPTH } from "../../navigation/navigation.core.js";
5
+ import { toValidationResult, } from "../validator.types.js";
4
6
  /**
5
7
  * Validates navigation tree references.
8
+ *
9
+ * Reports `NAVIGATION_UNKNOWN_DOCUMENT` (error) for references to
10
+ * unregistered documents, `NAVIGATION_CYCLE` (error) when a node is
11
+ * its own ancestor or the tree is deeper than `MAX_NAVIGATION_DEPTH`,
12
+ * `NAVIGATION_DUPLICATE_DOCUMENT` (warning) when a document is
13
+ * referenced more than once, and `NAVIGATION_EMPTY_ITEM` (warning)
14
+ * for items with neither `documentId` nor `children`.
6
15
  */
7
16
  export function validateNavigation(items, registeredIds) {
8
17
  const issues = [];
9
- function walk(nodes) {
18
+ const seenIds = new Set();
19
+ const visited = new WeakSet();
20
+ function walk(nodes, ancestors) {
21
+ if (ancestors.length > MAX_NAVIGATION_DEPTH) {
22
+ issues.push({
23
+ severity: "error",
24
+ code: "NAVIGATION_CYCLE",
25
+ message: `Navigation tree exceeds the maximum depth of ${MAX_NAVIGATION_DEPTH}.`,
26
+ });
27
+ return;
28
+ }
10
29
  for (const node of nodes) {
11
- if (node.documentId && !registeredIds.has(node.documentId)) {
30
+ if (!node || typeof node !== "object") {
31
+ issues.push({
32
+ severity: "error",
33
+ code: "NAVIGATION_EMPTY_ITEM",
34
+ message: "Navigation item is not an object.",
35
+ });
36
+ continue;
37
+ }
38
+ if (ancestors.includes(node)) {
12
39
  issues.push({
13
40
  severity: "error",
14
- code: "NAVIGATION_UNKNOWN_DOCUMENT",
15
- message: `Navigation item "${node.title}" references unknown document "${node.documentId}".`,
41
+ code: "NAVIGATION_CYCLE",
42
+ message: `Navigation item "${node.title}" is its own ancestor.`,
43
+ });
44
+ continue;
45
+ }
46
+ if (visited.has(node))
47
+ continue;
48
+ visited.add(node);
49
+ if (node.documentId) {
50
+ if (!registeredIds.has(node.documentId)) {
51
+ issues.push({
52
+ severity: "error",
53
+ code: "NAVIGATION_UNKNOWN_DOCUMENT",
54
+ message: `Navigation item "${node.title}" references unknown document "${node.documentId}".`,
55
+ documentId: node.documentId,
56
+ });
57
+ }
58
+ if (seenIds.has(node.documentId)) {
59
+ issues.push({
60
+ severity: "warning",
61
+ code: "NAVIGATION_DUPLICATE_DOCUMENT",
62
+ message: `Document "${node.documentId}" is referenced more than once in the navigation.`,
63
+ documentId: node.documentId,
64
+ });
65
+ }
66
+ seenIds.add(node.documentId);
67
+ }
68
+ else if (!node.children || node.children.length === 0) {
69
+ issues.push({
70
+ severity: "warning",
71
+ code: "NAVIGATION_EMPTY_ITEM",
72
+ message: `Navigation item "${node.title}" has neither a documentId nor children.`,
16
73
  });
17
74
  }
18
75
  if (node.children) {
19
- walk(node.children);
76
+ walk(node.children, [...ancestors, node]);
20
77
  }
21
78
  }
22
79
  }
23
- walk(items);
24
- return {
25
- valid: issues.length === 0,
26
- issues,
27
- };
80
+ walk(items, []);
81
+ return toValidationResult(issues);
28
82
  }
29
83
  //# sourceMappingURL=validatorNavigation.core.js.map
@@ -2,9 +2,10 @@
2
2
  * Validates a single document for structural correctness.
3
3
  */
4
4
  import type { DocumentationDocument } from "../docsTypes/index.js";
5
- import type { ValidationResult } from "./validator.types.js";
5
+ import { type ValidationResult } from "./validator.types.js";
6
6
  /**
7
- * Validates a single document.
7
+ * Validates a single document: ID syntax, required fields, content
8
+ * shape (including structured nodes), and the metadata enums.
8
9
  */
9
10
  export declare function validateDocument(document: DocumentationDocument): ValidationResult;
10
11
  //# sourceMappingURL=validatorDocument.core.d.ts.map
@@ -1,45 +1,227 @@
1
1
  /**
2
2
  * Validates a single document for structural correctness.
3
3
  */
4
+ import { isValidDocumentId } from "../utils/utils.helper.js";
5
+ import { toValidationResult, } from "./validator.types.js";
6
+ const CONTENT_TYPES = new Set(["markdown", "mdx", "html", "structured"]);
7
+ const STATUSES = new Set([
8
+ "stable",
9
+ "experimental",
10
+ "beta",
11
+ "deprecated",
12
+ "internal",
13
+ ]);
14
+ const CATEGORIES = new Set([
15
+ "introduction",
16
+ "guide",
17
+ "tutorial",
18
+ "reference",
19
+ "api",
20
+ "architecture",
21
+ "configuration",
22
+ "deployment",
23
+ "security",
24
+ "migration",
25
+ "examples",
26
+ ]);
27
+ const VISIBILITIES = new Set(["SERVER", "CLIENT"]);
28
+ const NODE_TYPES = new Set([
29
+ "heading",
30
+ "paragraph",
31
+ "code",
32
+ "list",
33
+ "link",
34
+ "table",
35
+ "quote",
36
+ "callout",
37
+ ]);
38
+ const CALLOUT_KINDS = new Set(["note", "warning", "tip", "danger"]);
4
39
  /**
5
- * Validates a single document.
40
+ * Validates a single document: ID syntax, required fields, content
41
+ * shape (including structured nodes), and the metadata enums.
6
42
  */
7
43
  export function validateDocument(document) {
8
44
  const issues = [];
9
- if (!document.id || document.id.trim().length === 0) {
45
+ const id = document.id;
46
+ if (typeof id !== "string" || id.trim().length === 0) {
10
47
  issues.push({
11
48
  severity: "error",
12
49
  code: "MISSING_ID",
13
50
  message: "Document ID is required.",
14
51
  });
15
52
  }
16
- if (!document.title || document.title.trim().length === 0) {
53
+ else if (!isValidDocumentId(id)) {
54
+ issues.push({
55
+ severity: "error",
56
+ code: "INVALID_ID",
57
+ message: `Document ID "${id}" is invalid. Use dot-separated segments of letters, digits, "_" and "-".`,
58
+ documentId: id,
59
+ });
60
+ }
61
+ if (typeof document.title !== "string" || document.title.trim().length === 0) {
17
62
  issues.push({
18
63
  severity: "error",
19
64
  code: "MISSING_TITLE",
20
- message: `Document "${document.id}" is missing a title.`,
21
- documentId: document.id,
65
+ message: `Document "${id}" is missing a title.`,
66
+ documentId: id,
22
67
  });
23
68
  }
24
- if (!document.content) {
69
+ validateContent(document, issues);
70
+ if (document.status !== undefined && !STATUSES.has(document.status)) {
25
71
  issues.push({
26
72
  severity: "error",
27
- code: "MISSING_CONTENT",
28
- message: `Document "${document.id}" is missing content.`,
29
- documentId: document.id,
73
+ code: "INVALID_STATUS",
74
+ message: `Document "${id}" has unknown status "${String(document.status)}".`,
75
+ documentId: id,
76
+ });
77
+ }
78
+ if (document.category !== undefined && !CATEGORIES.has(document.category)) {
79
+ issues.push({
80
+ severity: "error",
81
+ code: "INVALID_CATEGORY",
82
+ message: `Document "${id}" has unknown category "${String(document.category)}".`,
83
+ documentId: id,
84
+ });
85
+ }
86
+ if (document.visibility !== undefined &&
87
+ !VISIBILITIES.has(document.visibility)) {
88
+ issues.push({
89
+ severity: "error",
90
+ code: "INVALID_VISIBILITY",
91
+ message: `Document "${id}" has unknown visibility "${String(document.visibility)}".`,
92
+ documentId: id,
93
+ });
94
+ }
95
+ if (document.tags !== undefined &&
96
+ (!Array.isArray(document.tags) ||
97
+ document.tags.some((tag) => typeof tag !== "string" || tag.trim() === ""))) {
98
+ issues.push({
99
+ severity: "error",
100
+ code: "INVALID_TAGS",
101
+ message: `Document "${id}" has tags that are not non-empty strings.`,
102
+ documentId: id,
30
103
  });
31
104
  }
32
105
  if (document.deprecated && !document.deprecatedMessage) {
33
106
  issues.push({
34
107
  severity: "warning",
35
108
  code: "DEPRECATED_WITHOUT_MESSAGE",
36
- message: `Document "${document.id}" is deprecated but has no deprecation message.`,
37
- documentId: document.id,
109
+ message: `Document "${id}" is deprecated but has no deprecation message.`,
110
+ documentId: id,
111
+ });
112
+ }
113
+ if ((document.status === "deprecated") !== Boolean(document.deprecated) &&
114
+ (document.status === "deprecated" || document.deprecated)) {
115
+ issues.push({
116
+ severity: "warning",
117
+ code: "DEPRECATION_MISMATCH",
118
+ message: `Document "${id}" has status "${document.status ?? "unset"}" but deprecated=${String(Boolean(document.deprecated))}.`,
119
+ documentId: id,
120
+ });
121
+ }
122
+ return toValidationResult(issues);
123
+ }
124
+ function validateContent(document, issues) {
125
+ const id = document.id;
126
+ const content = document.content;
127
+ if (!content || typeof content !== "object") {
128
+ issues.push({
129
+ severity: "error",
130
+ code: "MISSING_CONTENT",
131
+ message: `Document "${id}" is missing content.`,
132
+ documentId: id,
133
+ });
134
+ return;
135
+ }
136
+ const type = content.type;
137
+ if (typeof type !== "string" || !CONTENT_TYPES.has(type)) {
138
+ issues.push({
139
+ severity: "error",
140
+ code: "INVALID_CONTENT_TYPE",
141
+ message: `Document "${id}" has unknown content type "${String(type)}".`,
142
+ documentId: id,
143
+ });
144
+ return;
145
+ }
146
+ if (type === "structured") {
147
+ const nodes = content.nodes;
148
+ if (!Array.isArray(nodes)) {
149
+ issues.push({
150
+ severity: "error",
151
+ code: "INVALID_NODE",
152
+ message: `Document "${id}" structured content must have a "nodes" array.`,
153
+ documentId: id,
154
+ });
155
+ return;
156
+ }
157
+ nodes.forEach((node, index) => {
158
+ const problem = describeNodeProblem(node);
159
+ if (problem) {
160
+ issues.push({
161
+ severity: "error",
162
+ code: "INVALID_NODE",
163
+ message: `Document "${id}" node ${index}: ${problem}`,
164
+ documentId: id,
165
+ });
166
+ }
167
+ });
168
+ return;
169
+ }
170
+ if (typeof content.value !== "string") {
171
+ issues.push({
172
+ severity: "error",
173
+ code: "INVALID_CONTENT_TYPE",
174
+ message: `Document "${id}" ${type} content must have a string "value".`,
175
+ documentId: id,
38
176
  });
39
177
  }
40
- return {
41
- valid: issues.filter((i) => i.severity === "error").length === 0,
42
- issues,
43
- };
178
+ }
179
+ /** Returns a description of what is wrong with a node, or undefined. */
180
+ function describeNodeProblem(node) {
181
+ if (!node || typeof node !== "object")
182
+ return "node is not an object.";
183
+ const n = node;
184
+ if (typeof n.type !== "string" || !NODE_TYPES.has(n.type)) {
185
+ return `unknown node type "${String(n.type)}".`;
186
+ }
187
+ const isString = (v) => typeof v === "string";
188
+ const isStringArray = (v) => Array.isArray(v) && v.every(isString);
189
+ switch (n.type) {
190
+ case "heading":
191
+ if (!Number.isInteger(n.level) || n.level < 1 || n.level > 6) {
192
+ return "heading level must be an integer from 1 to 6.";
193
+ }
194
+ return isString(n.value) ? undefined : "heading value must be a string.";
195
+ case "paragraph":
196
+ case "quote":
197
+ return isString(n.value) ? undefined : `${n.type} value must be a string.`;
198
+ case "code":
199
+ if (n.language !== undefined && !isString(n.language)) {
200
+ return "code language must be a string.";
201
+ }
202
+ return isString(n.value) ? undefined : "code value must be a string.";
203
+ case "list":
204
+ if (typeof n.ordered !== "boolean")
205
+ return "list ordered must be a boolean.";
206
+ return isStringArray(n.items) ? undefined : "list items must be strings.";
207
+ case "link":
208
+ if (!isString(n.href))
209
+ return "link href must be a string.";
210
+ return isString(n.value) ? undefined : "link value must be a string.";
211
+ case "table":
212
+ if (!isStringArray(n.headers))
213
+ return "table headers must be strings.";
214
+ if (!Array.isArray(n.rows) || !n.rows.every(isStringArray)) {
215
+ return "table rows must be arrays of strings.";
216
+ }
217
+ return undefined;
218
+ case "callout":
219
+ if (!isString(n.kind) || !CALLOUT_KINDS.has(n.kind)) {
220
+ return `unknown callout kind "${String(n.kind)}".`;
221
+ }
222
+ return isString(n.value) ? undefined : "callout value must be a string.";
223
+ default:
224
+ return undefined;
225
+ }
44
226
  }
45
227
  //# sourceMappingURL=validatorDocument.core.js.map
@@ -2,7 +2,7 @@
2
2
  * Validates that no documents share the same ID.
3
3
  */
4
4
  import type { DocumentationDocument } from "../docsTypes/index.js";
5
- import type { ValidationResult } from "./validator.types.js";
5
+ import { type ValidationResult } from "./validator.types.js";
6
6
  /**
7
7
  * Validates no duplicate IDs exist in a collection of documents.
8
8
  */
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Validates that no documents share the same ID.
3
3
  */
4
+ import { toValidationResult, } from "./validator.types.js";
4
5
  /**
5
6
  * Validates no duplicate IDs exist in a collection of documents.
6
7
  */
7
8
  export function validateNoDuplicateIds(documents) {
8
- const seen = new Map();
9
+ const seen = new Set();
9
10
  const issues = [];
10
11
  for (const doc of documents) {
11
12
  if (seen.has(doc.id)) {
@@ -17,12 +18,9 @@ export function validateNoDuplicateIds(documents) {
17
18
  });
18
19
  }
19
20
  else {
20
- seen.set(doc.id, doc.title);
21
+ seen.add(doc.id);
21
22
  }
22
23
  }
23
- return {
24
- valid: issues.length === 0,
25
- issues,
26
- };
24
+ return toValidationResult(issues);
27
25
  }
28
26
  //# sourceMappingURL=validatorDuplicates.core.js.map
@@ -1,10 +1,28 @@
1
1
  /**
2
- * Validates internal links within markdown documentation.
2
+ * Validates internal links within markdown and structured documentation.
3
3
  */
4
4
  import type { DocumentationDocument } from "../docsTypes/index.js";
5
- import type { ValidationResult } from "./validator.types.js";
5
+ import { type ValidationResult } from "./validator.types.js";
6
+ /** Default maximum markdown length that is scanned for links. */
7
+ export declare const DEFAULT_MAX_LINK_SCAN_LENGTH = 100000;
8
+ /** Options for `validateLinks`. */
9
+ export interface ValidateLinksOptions {
10
+ /**
11
+ * Markdown longer than this (in characters) is not scanned; a
12
+ * `LINK_VALIDATION_SKIPPED` warning is reported instead.
13
+ * Defaults to {@link DEFAULT_MAX_LINK_SCAN_LENGTH}.
14
+ */
15
+ readonly maxContentLength?: number;
16
+ }
6
17
  /**
7
- * Validates internal links in a document's markdown content.
18
+ * Validates internal links in a document's markdown content and in
19
+ * structured `link` nodes.
20
+ *
21
+ * Skipped (never reported): images, anchors (`#…`), any target with a
22
+ * URL scheme or `//` prefix, links inside fenced or inline code.
23
+ * Relative targets (`./x`, `../x`, `/x`) are resolved against the
24
+ * document ID with `resolveDocumentLink`; bare targets are looked up
25
+ * both as-is and resolved.
8
26
  */
9
- export declare function validateLinks(document: DocumentationDocument, registeredIds: ReadonlySet<string>): ValidationResult;
27
+ export declare function validateLinks(document: DocumentationDocument, registeredIds: ReadonlySet<string>, options?: ValidateLinksOptions): ValidationResult;
10
28
  //# sourceMappingURL=validatorLinks.core.d.ts.map