@vibe-agent-toolkit/resources 0.1.3 → 0.1.4

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 (87) hide show
  1. package/README.md +0 -17
  2. package/dist/collection-matcher.d.ts +63 -0
  3. package/dist/collection-matcher.d.ts.map +1 -0
  4. package/dist/collection-matcher.js +127 -0
  5. package/dist/collection-matcher.js.map +1 -0
  6. package/dist/config-parser.d.ts +63 -0
  7. package/dist/config-parser.d.ts.map +1 -0
  8. package/dist/config-parser.js +113 -0
  9. package/dist/config-parser.js.map +1 -0
  10. package/dist/frontmatter-validator.d.ts +12 -2
  11. package/dist/frontmatter-validator.d.ts.map +1 -1
  12. package/dist/frontmatter-validator.js +174 -18
  13. package/dist/frontmatter-validator.js.map +1 -1
  14. package/dist/index.d.ts +3 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/link-validator.d.ts +25 -3
  19. package/dist/link-validator.d.ts.map +1 -1
  20. package/dist/link-validator.js +52 -40
  21. package/dist/link-validator.js.map +1 -1
  22. package/dist/multi-schema-validator.d.ts +42 -0
  23. package/dist/multi-schema-validator.d.ts.map +1 -0
  24. package/dist/multi-schema-validator.js +107 -0
  25. package/dist/multi-schema-validator.js.map +1 -0
  26. package/dist/pattern-expander.d.ts +63 -0
  27. package/dist/pattern-expander.d.ts.map +1 -0
  28. package/dist/pattern-expander.js +93 -0
  29. package/dist/pattern-expander.js.map +1 -0
  30. package/dist/resource-registry.d.ts +87 -6
  31. package/dist/resource-registry.d.ts.map +1 -1
  32. package/dist/resource-registry.js +215 -46
  33. package/dist/resource-registry.js.map +1 -1
  34. package/dist/schema-assignment.d.ts +49 -0
  35. package/dist/schema-assignment.d.ts.map +1 -0
  36. package/dist/schema-assignment.js +95 -0
  37. package/dist/schema-assignment.js.map +1 -0
  38. package/dist/schemas/project-config.d.ts +254 -0
  39. package/dist/schemas/project-config.d.ts.map +1 -0
  40. package/dist/schemas/project-config.js +57 -0
  41. package/dist/schemas/project-config.js.map +1 -0
  42. package/dist/schemas/resource-metadata.d.ts +3 -0
  43. package/dist/schemas/resource-metadata.d.ts.map +1 -1
  44. package/dist/schemas/resource-metadata.js +2 -0
  45. package/dist/schemas/resource-metadata.js.map +1 -1
  46. package/dist/schemas/validation-result.d.ts +2 -26
  47. package/dist/schemas/validation-result.d.ts.map +1 -1
  48. package/dist/schemas/validation-result.js +4 -20
  49. package/dist/schemas/validation-result.js.map +1 -1
  50. package/dist/types/resource-parser.d.ts +53 -0
  51. package/dist/types/resource-parser.d.ts.map +1 -0
  52. package/dist/types/resource-parser.js +233 -0
  53. package/dist/types/resource-parser.js.map +1 -0
  54. package/dist/types/resource-path-utils.d.ts +43 -0
  55. package/dist/types/resource-path-utils.d.ts.map +1 -0
  56. package/dist/types/resource-path-utils.js +89 -0
  57. package/dist/types/resource-path-utils.js.map +1 -0
  58. package/dist/types/resources.d.ts +140 -0
  59. package/dist/types/resources.d.ts.map +1 -0
  60. package/dist/types/resources.js +58 -0
  61. package/dist/types/resources.js.map +1 -0
  62. package/dist/types.d.ts +14 -2
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js +17 -0
  65. package/dist/types.js.map +1 -1
  66. package/dist/utils.d.ts +18 -0
  67. package/dist/utils.d.ts.map +1 -1
  68. package/dist/utils.js +39 -0
  69. package/dist/utils.js.map +1 -1
  70. package/package.json +2 -2
  71. package/src/collection-matcher.ts +148 -0
  72. package/src/config-parser.ts +125 -0
  73. package/src/frontmatter-validator.ts +202 -18
  74. package/src/index.ts +7 -2
  75. package/src/link-validator.ts +70 -43
  76. package/src/multi-schema-validator.ts +128 -0
  77. package/src/pattern-expander.ts +100 -0
  78. package/src/resource-registry.ts +322 -54
  79. package/src/schema-assignment.ts +119 -0
  80. package/src/schemas/project-config.ts +71 -0
  81. package/src/schemas/resource-metadata.ts +2 -0
  82. package/src/schemas/validation-result.ts +4 -23
  83. package/src/types/resource-parser.ts +302 -0
  84. package/src/types/resource-path-utils.ts +102 -0
  85. package/src/types/resources.ts +211 -0
  86. package/src/types.ts +81 -1
  87. package/src/utils.ts +43 -0
package/README.md CHANGED
@@ -63,9 +63,6 @@ Main class for managing collections of markdown resources.
63
63
  new ResourceRegistry(options?: ResourceRegistryOptions)
64
64
  ```
65
65
 
66
- **Options:**
67
- - `validateOnAdd?: boolean` - Validate resources immediately when added (default: `false`)
68
-
69
66
  #### Methods
70
67
 
71
68
  ##### addResource(filePath: string): Promise<ResourceMetadata>
@@ -906,20 +903,6 @@ for (const [path, issues] of issuesByResource) {
906
903
  }
907
904
  ```
908
905
 
909
- ### Validate on Add
910
-
911
- Enable strict validation mode to fail fast on broken links:
912
-
913
- ```typescript
914
- const registry = new ResourceRegistry({ validateOnAdd: true });
915
-
916
- try {
917
- await registry.addResource('./docs/broken.md');
918
- } catch (error) {
919
- console.error('Validation failed:', error.message);
920
- }
921
- ```
922
-
923
906
  ## Examples
924
907
 
925
908
  ### Validate Project Documentation
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Collection matching utilities for determining which collections a file belongs to.
3
+ *
4
+ * Applies include/exclude pattern rules with precedence (exclude wins).
5
+ */
6
+ import type { CollectionConfig } from './schemas/project-config.js';
7
+ /**
8
+ * Check if a file path matches a collection's include/exclude rules.
9
+ *
10
+ * Rules:
11
+ * - File must match at least one include pattern
12
+ * - File must NOT match any exclude pattern
13
+ * - Exclude always wins over include
14
+ * - Pattern order does not matter
15
+ *
16
+ * Special handling for root-level patterns (*.md, *.json):
17
+ * - These match against the basename only, not the full path
18
+ * - Allows matching root-level files regardless of their absolute path
19
+ *
20
+ * Paths are normalized to forward slashes before matching for cross-platform consistency.
21
+ *
22
+ * @param filePath - Absolute file path to check
23
+ * @param collection - Collection configuration with include/exclude patterns
24
+ * @returns True if file belongs to collection
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const collection = {
29
+ * include: ['docs'],
30
+ * exclude: ['**\/README.md']
31
+ * };
32
+ *
33
+ * matchesCollection('/project/docs/guide.md', collection) // true
34
+ * matchesCollection('/project/docs/README.md', collection) // false (excluded)
35
+ * matchesCollection('/project/src/index.ts', collection) // false (not included)
36
+ * ```
37
+ */
38
+ export declare function matchesCollection(filePath: string, collection: CollectionConfig): boolean;
39
+ /**
40
+ * Find all collections that a file belongs to.
41
+ *
42
+ * A file can belong to multiple collections if it matches their rules.
43
+ *
44
+ * @param filePath - Absolute file path to check
45
+ * @param collections - Map of collection name to collection config
46
+ * @returns Array of collection names the file belongs to
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const collections = {
51
+ * 'rag-kb': { include: ['docs'], exclude: ['**\/README.md'] },
52
+ * 'skills': { include: ['**\/SKILL.md'] }
53
+ * };
54
+ *
55
+ * getCollectionsForFile('/project/docs/guide.md', collections)
56
+ * // ['rag-kb']
57
+ *
58
+ * getCollectionsForFile('/project/docs/SKILL.md', collections)
59
+ * // ['rag-kb', 'skills']
60
+ * ```
61
+ */
62
+ export declare function getCollectionsForFile(filePath: string, collections: Record<string, CollectionConfig>): string[];
63
+ //# sourceMappingURL=collection-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-matcher.d.ts","sourceRoot":"","sources":["../src/collection-matcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAWpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAuDzF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC5C,MAAM,EAAE,CAUV"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Collection matching utilities for determining which collections a file belongs to.
3
+ *
4
+ * Applies include/exclude pattern rules with precedence (exclude wins).
5
+ */
6
+ import { basename as getBasename } from 'node:path';
7
+ import { toForwardSlash } from '@vibe-agent-toolkit/utils';
8
+ import picomatch from 'picomatch';
9
+ import { expandPatterns } from './pattern-expander.js';
10
+ /**
11
+ * Check if a pattern is a root-level pattern (e.g., *.md, *.json).
12
+ *
13
+ * Root-level patterns start with * but not **.
14
+ */
15
+ function isRootLevelPattern(pattern) {
16
+ return pattern.startsWith('*') && !pattern.startsWith('**');
17
+ }
18
+ /**
19
+ * Check if a file path matches a collection's include/exclude rules.
20
+ *
21
+ * Rules:
22
+ * - File must match at least one include pattern
23
+ * - File must NOT match any exclude pattern
24
+ * - Exclude always wins over include
25
+ * - Pattern order does not matter
26
+ *
27
+ * Special handling for root-level patterns (*.md, *.json):
28
+ * - These match against the basename only, not the full path
29
+ * - Allows matching root-level files regardless of their absolute path
30
+ *
31
+ * Paths are normalized to forward slashes before matching for cross-platform consistency.
32
+ *
33
+ * @param filePath - Absolute file path to check
34
+ * @param collection - Collection configuration with include/exclude patterns
35
+ * @returns True if file belongs to collection
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const collection = {
40
+ * include: ['docs'],
41
+ * exclude: ['**\/README.md']
42
+ * };
43
+ *
44
+ * matchesCollection('/project/docs/guide.md', collection) // true
45
+ * matchesCollection('/project/docs/README.md', collection) // false (excluded)
46
+ * matchesCollection('/project/src/index.ts', collection) // false (not included)
47
+ * ```
48
+ */
49
+ export function matchesCollection(filePath, collection) {
50
+ // Normalize to forward slashes for cross-platform consistency
51
+ const normalizedPath = toForwardSlash(filePath);
52
+ // Extract basename for root-level pattern matching
53
+ const basename = getBasename(filePath);
54
+ // Expand patterns (paths → globs)
55
+ const includePatterns = expandPatterns(collection.include);
56
+ const excludePatterns = collection.exclude ? expandPatterns(collection.exclude) : [];
57
+ // Separate root-level patterns from other patterns
58
+ const includeRootPatterns = includePatterns.filter(isRootLevelPattern);
59
+ const includeNonRootPatterns = includePatterns.filter((p) => !isRootLevelPattern(p));
60
+ const excludeRootPatterns = excludePatterns.filter(isRootLevelPattern);
61
+ const excludeNonRootPatterns = excludePatterns.filter((p) => !isRootLevelPattern(p));
62
+ // Check excludes first (exclude wins)
63
+ // Check non-root excludes against full path
64
+ if (excludeNonRootPatterns.length > 0) {
65
+ const excludeMatcher = picomatch(excludeNonRootPatterns);
66
+ if (excludeMatcher(normalizedPath)) {
67
+ return false;
68
+ }
69
+ }
70
+ // Check root-level excludes against basename
71
+ if (excludeRootPatterns.length > 0) {
72
+ const excludeRootMatcher = picomatch(excludeRootPatterns);
73
+ if (excludeRootMatcher(basename)) {
74
+ return false;
75
+ }
76
+ }
77
+ // Check includes (need to match at least one)
78
+ let matched = false;
79
+ // Check non-root includes against full path
80
+ if (includeNonRootPatterns.length > 0) {
81
+ const includeMatcher = picomatch(includeNonRootPatterns);
82
+ if (includeMatcher(normalizedPath)) {
83
+ matched = true;
84
+ }
85
+ }
86
+ // Check root-level includes against basename
87
+ if (!matched && includeRootPatterns.length > 0) {
88
+ const includeRootMatcher = picomatch(includeRootPatterns);
89
+ if (includeRootMatcher(basename)) {
90
+ matched = true;
91
+ }
92
+ }
93
+ return matched;
94
+ }
95
+ /**
96
+ * Find all collections that a file belongs to.
97
+ *
98
+ * A file can belong to multiple collections if it matches their rules.
99
+ *
100
+ * @param filePath - Absolute file path to check
101
+ * @param collections - Map of collection name to collection config
102
+ * @returns Array of collection names the file belongs to
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const collections = {
107
+ * 'rag-kb': { include: ['docs'], exclude: ['**\/README.md'] },
108
+ * 'skills': { include: ['**\/SKILL.md'] }
109
+ * };
110
+ *
111
+ * getCollectionsForFile('/project/docs/guide.md', collections)
112
+ * // ['rag-kb']
113
+ *
114
+ * getCollectionsForFile('/project/docs/SKILL.md', collections)
115
+ * // ['rag-kb', 'skills']
116
+ * ```
117
+ */
118
+ export function getCollectionsForFile(filePath, collections) {
119
+ const matchingCollections = [];
120
+ for (const [name, config] of Object.entries(collections)) {
121
+ if (matchesCollection(filePath, config)) {
122
+ matchingCollections.push(name);
123
+ }
124
+ }
125
+ return matchingCollections;
126
+ }
127
+ //# sourceMappingURL=collection-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-matcher.js","sourceRoot":"","sources":["../src/collection-matcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Configuration file parser for vibe-agent-toolkit.config.yaml
3
+ *
4
+ * Discovers and parses project configuration files with directory tree walk-up.
5
+ */
6
+ import { type ProjectConfig } from './schemas/project-config.js';
7
+ /**
8
+ * Find the config file by walking up the directory tree.
9
+ *
10
+ * Starts from the current directory and walks up until the config file is found
11
+ * or the root directory is reached.
12
+ *
13
+ * @param startDir - Directory to start searching from (default: process.cwd())
14
+ * @returns Absolute path to config file, or undefined if not found
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const configPath = await findConfigFile();
19
+ * if (configPath) {
20
+ * console.log(`Found config: ${configPath}`);
21
+ * }
22
+ * ```
23
+ */
24
+ export declare function findConfigFile(startDir?: string): Promise<string | undefined>;
25
+ /**
26
+ * Parse a project configuration file.
27
+ *
28
+ * Reads the YAML file, parses it, and validates against the schema.
29
+ *
30
+ * @param configPath - Absolute path to config file
31
+ * @returns Parsed and validated configuration
32
+ * @throws Error if file cannot be read, YAML is invalid, or validation fails
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const config = await parseConfigFile('/project/vibe-agent-toolkit.config.yaml');
37
+ * console.log(`Version: ${config.version}`);
38
+ * console.log(`Collections: ${Object.keys(config.resources?.collections ?? {}).join(', ')}`);
39
+ * ```
40
+ */
41
+ export declare function parseConfigFile(configPath: string): Promise<ProjectConfig>;
42
+ /**
43
+ * Load project configuration by discovering and parsing config file.
44
+ *
45
+ * Walks up the directory tree from startDir to find the config file,
46
+ * then parses and validates it.
47
+ *
48
+ * @param startDir - Directory to start searching from (default: process.cwd())
49
+ * @returns Parsed configuration, or undefined if no config file found
50
+ * @throws Error if config file is found but cannot be parsed or is invalid
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const config = await loadConfig();
55
+ * if (config) {
56
+ * console.log('Using project config');
57
+ * } else {
58
+ * console.log('No config found, using defaults');
59
+ * }
60
+ * ```
61
+ */
62
+ export declare function loadConfig(startDir?: string): Promise<ProjectConfig | undefined>;
63
+ //# sourceMappingURL=config-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-parser.d.ts","sourceRoot":"","sources":["../src/config-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAItF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,cAAc,CAAC,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAwBlG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqBhF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,UAAU,CAAC,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAOrG"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Configuration file parser for vibe-agent-toolkit.config.yaml
3
+ *
4
+ * Discovers and parses project configuration files with directory tree walk-up.
5
+ */
6
+ import { readFile } from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import { load as loadYaml } from 'js-yaml';
9
+ import { ProjectConfigSchema } from './schemas/project-config.js';
10
+ const CONFIG_FILENAME = 'vibe-agent-toolkit.config.yaml';
11
+ /**
12
+ * Find the config file by walking up the directory tree.
13
+ *
14
+ * Starts from the current directory and walks up until the config file is found
15
+ * or the root directory is reached.
16
+ *
17
+ * @param startDir - Directory to start searching from (default: process.cwd())
18
+ * @returns Absolute path to config file, or undefined if not found
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const configPath = await findConfigFile();
23
+ * if (configPath) {
24
+ * console.log(`Found config: ${configPath}`);
25
+ * }
26
+ * ```
27
+ */
28
+ export async function findConfigFile(startDir = process.cwd()) {
29
+ let currentDir = path.resolve(startDir);
30
+ const { root } = path.parse(currentDir);
31
+ while (true) {
32
+ const configPath = path.join(currentDir, CONFIG_FILENAME);
33
+ try {
34
+ // Check if file exists by attempting to read metadata
35
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructing path during tree walk
36
+ await readFile(configPath, 'utf-8');
37
+ return configPath;
38
+ }
39
+ catch {
40
+ // File doesn't exist, continue walking up
41
+ }
42
+ // Check if we've reached the root
43
+ if (currentDir === root) {
44
+ return undefined;
45
+ }
46
+ // Move up one directory
47
+ currentDir = path.dirname(currentDir);
48
+ }
49
+ }
50
+ /**
51
+ * Parse a project configuration file.
52
+ *
53
+ * Reads the YAML file, parses it, and validates against the schema.
54
+ *
55
+ * @param configPath - Absolute path to config file
56
+ * @returns Parsed and validated configuration
57
+ * @throws Error if file cannot be read, YAML is invalid, or validation fails
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const config = await parseConfigFile('/project/vibe-agent-toolkit.config.yaml');
62
+ * console.log(`Version: ${config.version}`);
63
+ * console.log(`Collections: ${Object.keys(config.resources?.collections ?? {}).join(', ')}`);
64
+ * ```
65
+ */
66
+ export async function parseConfigFile(configPath) {
67
+ // Read file content
68
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- configPath is from findConfigFile() walk-up
69
+ const content = await readFile(configPath, 'utf-8');
70
+ // Parse YAML
71
+ let parsed;
72
+ try {
73
+ parsed = loadYaml(content);
74
+ }
75
+ catch (error) {
76
+ throw new Error(`Invalid YAML in config file: ${error instanceof Error ? error.message : String(error)}`);
77
+ }
78
+ // Validate against schema
79
+ const result = ProjectConfigSchema.safeParse(parsed);
80
+ if (!result.success) {
81
+ const errors = result.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
82
+ throw new Error(`Invalid config file: ${errors}`);
83
+ }
84
+ return result.data;
85
+ }
86
+ /**
87
+ * Load project configuration by discovering and parsing config file.
88
+ *
89
+ * Walks up the directory tree from startDir to find the config file,
90
+ * then parses and validates it.
91
+ *
92
+ * @param startDir - Directory to start searching from (default: process.cwd())
93
+ * @returns Parsed configuration, or undefined if no config file found
94
+ * @throws Error if config file is found but cannot be parsed or is invalid
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const config = await loadConfig();
99
+ * if (config) {
100
+ * console.log('Using project config');
101
+ * } else {
102
+ * console.log('No config found, using defaults');
103
+ * }
104
+ * ```
105
+ */
106
+ export async function loadConfig(startDir = process.cwd()) {
107
+ const configPath = await findConfigFile(startDir);
108
+ if (!configPath) {
109
+ return undefined;
110
+ }
111
+ return await parseConfigFile(configPath);
112
+ }
113
+ //# sourceMappingURL=config-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-parser.js","sourceRoot":"","sources":["../src/config-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,mBAAmB,EAAsB,MAAM,6BAA6B,CAAC;AAEtF,MAAM,eAAe,GAAG,gCAAgC,CAAC;AAEzD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IACnE,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAExC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAE1D,IAAI,CAAC;YACH,sDAAsD;YACtD,yGAAyG;YACzG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACpC,OAAO,UAAU,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;QAC5C,CAAC;QAED,kCAAkC;QAClC,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,wBAAwB;QACxB,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAAkB;IACtD,oBAAoB;IACpB,kHAAkH;IAClH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEpD,aAAa;IACb,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC5G,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IAC/D,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC"}
@@ -12,6 +12,7 @@
12
12
  *
13
13
  * This is the ONLY place in the codebase that should use AJV.
14
14
  */
15
+ import type { ValidationMode } from './schemas/project-config.js';
15
16
  import type { ValidationIssue } from './schemas/validation-result.js';
16
17
  /**
17
18
  * Validate frontmatter against a JSON Schema.
@@ -20,10 +21,13 @@ import type { ValidationIssue } from './schemas/validation-result.js';
20
21
  * - Missing frontmatter: Error only if schema has required fields
21
22
  * - Extra fields: Allowed by default (unless schema sets additionalProperties: false)
22
23
  * - Type mismatches: Always reported as errors
24
+ * - Permissive mode: Ignores additionalProperties: false (allows schema layering)
23
25
  *
24
26
  * @param frontmatter - Parsed frontmatter object (or undefined if no frontmatter)
25
27
  * @param schema - JSON Schema object
26
28
  * @param resourcePath - File path for error reporting
29
+ * @param mode - Validation mode: 'strict' (default) or 'permissive'
30
+ * @param schemaPath - Path to schema file (for error context)
27
31
  * @returns Array of validation issues (empty if valid)
28
32
  *
29
33
  * @example
@@ -33,8 +37,14 @@ import type { ValidationIssue } from './schemas/validation-result.js';
33
37
  * required: ['title'],
34
38
  * properties: { title: { type: 'string' } }
35
39
  * };
36
- * const issues = validateFrontmatter(frontmatter, schema, '/docs/guide.md');
40
+ * const issues = validateFrontmatter(
41
+ * frontmatter,
42
+ * schema,
43
+ * '/docs/guide.md',
44
+ * 'strict',
45
+ * '/schema.json'
46
+ * );
37
47
  * ```
38
48
  */
39
- export declare function validateFrontmatter(frontmatter: Record<string, unknown> | undefined, schema: object, resourcePath: string): ValidationIssue[];
49
+ export declare function validateFrontmatter(frontmatter: Record<string, unknown> | undefined, schema: object, resourcePath: string, mode?: ValidationMode, schemaPath?: string): ValidationIssue[];
40
50
  //# sourceMappingURL=frontmatter-validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAChD,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,eAAe,EAAE,CAgDnB"}
1
+ {"version":3,"file":"frontmatter-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAChD,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE,cAAyB,EAC/B,UAAU,CAAC,EAAE,MAAM,GAClB,eAAe,EAAE,CA0DnB"}