@eddacraft/anvil-core 0.1.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 (215) hide show
  1. package/LICENSE +14 -0
  2. package/dist/antipattern/index.d.ts +11 -0
  3. package/dist/antipattern/index.d.ts.map +1 -0
  4. package/dist/antipattern/index.js +31 -0
  5. package/dist/antipattern/patterns-css.d.ts +17 -0
  6. package/dist/antipattern/patterns-css.d.ts.map +1 -0
  7. package/dist/antipattern/patterns-css.js +72 -0
  8. package/dist/antipattern/patterns-html.d.ts +21 -0
  9. package/dist/antipattern/patterns-html.d.ts.map +1 -0
  10. package/dist/antipattern/patterns-html.js +139 -0
  11. package/dist/antipattern/patterns.d.ts +72 -0
  12. package/dist/antipattern/patterns.d.ts.map +1 -0
  13. package/dist/antipattern/patterns.js +301 -0
  14. package/dist/antipattern/scanner.d.ts +32 -0
  15. package/dist/antipattern/scanner.d.ts.map +1 -0
  16. package/dist/antipattern/scanner.js +89 -0
  17. package/dist/antipattern/types.d.ts +318 -0
  18. package/dist/antipattern/types.d.ts.map +1 -0
  19. package/dist/antipattern/types.js +278 -0
  20. package/dist/architecture/analyzer.d.ts +123 -0
  21. package/dist/architecture/analyzer.d.ts.map +1 -0
  22. package/dist/architecture/analyzer.js +321 -0
  23. package/dist/architecture/baseline.d.ts +112 -0
  24. package/dist/architecture/baseline.d.ts.map +1 -0
  25. package/dist/architecture/baseline.js +245 -0
  26. package/dist/architecture/compiler.d.ts +24 -0
  27. package/dist/architecture/compiler.d.ts.map +1 -0
  28. package/dist/architecture/compiler.js +57 -0
  29. package/dist/architecture/context.d.ts +129 -0
  30. package/dist/architecture/context.d.ts.map +1 -0
  31. package/dist/architecture/context.js +116 -0
  32. package/dist/architecture/dc-generator.d.ts +9 -0
  33. package/dist/architecture/dc-generator.d.ts.map +1 -0
  34. package/dist/architecture/dc-generator.js +220 -0
  35. package/dist/architecture/definition-schema.d.ts +128 -0
  36. package/dist/architecture/definition-schema.d.ts.map +1 -0
  37. package/dist/architecture/definition-schema.js +94 -0
  38. package/dist/architecture/edge-detector-html.d.ts +6 -0
  39. package/dist/architecture/edge-detector-html.d.ts.map +1 -0
  40. package/dist/architecture/edge-detector-html.js +5 -0
  41. package/dist/architecture/edge-detector-web.d.ts +32 -0
  42. package/dist/architecture/edge-detector-web.d.ts.map +1 -0
  43. package/dist/architecture/edge-detector-web.js +133 -0
  44. package/dist/architecture/edge-detector.d.ts +116 -0
  45. package/dist/architecture/edge-detector.d.ts.map +1 -0
  46. package/dist/architecture/edge-detector.js +229 -0
  47. package/dist/architecture/entry-detector.d.ts +44 -0
  48. package/dist/architecture/entry-detector.d.ts.map +1 -0
  49. package/dist/architecture/entry-detector.js +263 -0
  50. package/dist/architecture/index.d.ts +21 -0
  51. package/dist/architecture/index.d.ts.map +1 -0
  52. package/dist/architecture/index.js +48 -0
  53. package/dist/architecture/layer-detector.d.ts +60 -0
  54. package/dist/architecture/layer-detector.d.ts.map +1 -0
  55. package/dist/architecture/layer-detector.js +331 -0
  56. package/dist/architecture/rego-generator.d.ts +25 -0
  57. package/dist/architecture/rego-generator.d.ts.map +1 -0
  58. package/dist/architecture/rego-generator.js +229 -0
  59. package/dist/architecture/templates/index.d.ts +39 -0
  60. package/dist/architecture/templates/index.d.ts.map +1 -0
  61. package/dist/architecture/templates/index.js +124 -0
  62. package/dist/architecture/types.d.ts +280 -0
  63. package/dist/architecture/types.d.ts.map +1 -0
  64. package/dist/architecture/types.js +269 -0
  65. package/dist/architecture/yaml-parser.d.ts +13 -0
  66. package/dist/architecture/yaml-parser.d.ts.map +1 -0
  67. package/dist/architecture/yaml-parser.js +234 -0
  68. package/dist/config/constants.d.ts +9 -0
  69. package/dist/config/constants.d.ts.map +1 -0
  70. package/dist/config/constants.js +20 -0
  71. package/dist/config/index.d.ts +9 -0
  72. package/dist/config/index.d.ts.map +1 -0
  73. package/dist/config/index.js +8 -0
  74. package/dist/config/loader.d.ts +41 -0
  75. package/dist/config/loader.d.ts.map +1 -0
  76. package/dist/config/loader.js +76 -0
  77. package/dist/config/nudge-config.d.ts +35 -0
  78. package/dist/config/nudge-config.d.ts.map +1 -0
  79. package/dist/config/nudge-config.js +34 -0
  80. package/dist/config/types.d.ts +30 -0
  81. package/dist/config/types.d.ts.map +1 -0
  82. package/dist/config/types.js +4 -0
  83. package/dist/contracts/index.d.ts +14 -0
  84. package/dist/contracts/index.d.ts.map +1 -0
  85. package/dist/contracts/index.js +13 -0
  86. package/dist/contracts/schemas/aps.schema.d.ts +269 -0
  87. package/dist/contracts/schemas/aps.schema.d.ts.map +1 -0
  88. package/dist/contracts/schemas/aps.schema.js +183 -0
  89. package/dist/contracts/schemas/index.d.ts +12 -0
  90. package/dist/contracts/schemas/index.d.ts.map +1 -0
  91. package/dist/contracts/schemas/index.js +14 -0
  92. package/dist/contracts/schemas/json-schema.d.ts +14 -0
  93. package/dist/contracts/schemas/json-schema.d.ts.map +1 -0
  94. package/dist/contracts/schemas/json-schema.js +31 -0
  95. package/dist/contracts/schemas/warning.schema.d.ts +171 -0
  96. package/dist/contracts/schemas/warning.schema.d.ts.map +1 -0
  97. package/dist/contracts/schemas/warning.schema.js +123 -0
  98. package/dist/contracts/types/gate.types.d.ts +194 -0
  99. package/dist/contracts/types/gate.types.d.ts.map +1 -0
  100. package/dist/contracts/types/gate.types.js +19 -0
  101. package/dist/contracts/types/index.d.ts +9 -0
  102. package/dist/contracts/types/index.d.ts.map +1 -0
  103. package/dist/contracts/types/index.js +8 -0
  104. package/dist/crypto/hash.d.ts +47 -0
  105. package/dist/crypto/hash.d.ts.map +1 -0
  106. package/dist/crypto/hash.js +110 -0
  107. package/dist/crypto/index.d.ts +7 -0
  108. package/dist/crypto/index.d.ts.map +1 -0
  109. package/dist/crypto/index.js +6 -0
  110. package/dist/drift/index.d.ts +6 -0
  111. package/dist/drift/index.d.ts.map +1 -0
  112. package/dist/drift/index.js +5 -0
  113. package/dist/drift/report-generator.d.ts +21 -0
  114. package/dist/drift/report-generator.d.ts.map +1 -0
  115. package/dist/drift/report-generator.js +240 -0
  116. package/dist/drift/snapshot-capture.d.ts +26 -0
  117. package/dist/drift/snapshot-capture.d.ts.map +1 -0
  118. package/dist/drift/snapshot-capture.js +195 -0
  119. package/dist/drift/snapshot-compare.d.ts +50 -0
  120. package/dist/drift/snapshot-compare.d.ts.map +1 -0
  121. package/dist/drift/snapshot-compare.js +142 -0
  122. package/dist/drift/snapshot-schema.d.ts +197 -0
  123. package/dist/drift/snapshot-schema.d.ts.map +1 -0
  124. package/dist/drift/snapshot-schema.js +193 -0
  125. package/dist/drift/snapshot-storage.d.ts +25 -0
  126. package/dist/drift/snapshot-storage.d.ts.map +1 -0
  127. package/dist/drift/snapshot-storage.js +179 -0
  128. package/dist/explain/antipattern-explainer.d.ts +4 -0
  129. package/dist/explain/antipattern-explainer.d.ts.map +1 -0
  130. package/dist/explain/antipattern-explainer.js +196 -0
  131. package/dist/explain/boundary-explainer.d.ts +5 -0
  132. package/dist/explain/boundary-explainer.d.ts.map +1 -0
  133. package/dist/explain/boundary-explainer.js +261 -0
  134. package/dist/explain/explain-service.d.ts +19 -0
  135. package/dist/explain/explain-service.d.ts.map +1 -0
  136. package/dist/explain/explain-service.js +106 -0
  137. package/dist/explain/index.d.ts +7 -0
  138. package/dist/explain/index.d.ts.map +1 -0
  139. package/dist/explain/index.js +5 -0
  140. package/dist/explain/template-loader.d.ts +9 -0
  141. package/dist/explain/template-loader.d.ts.map +1 -0
  142. package/dist/explain/template-loader.js +51 -0
  143. package/dist/explain/types.d.ts +46 -0
  144. package/dist/explain/types.d.ts.map +1 -0
  145. package/dist/explain/types.js +31 -0
  146. package/dist/index.d.ts +26 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +37 -0
  149. package/dist/provenance/collector.d.ts +86 -0
  150. package/dist/provenance/collector.d.ts.map +1 -0
  151. package/dist/provenance/collector.js +425 -0
  152. package/dist/provenance/git-ai-standard/git-notes.d.ts +85 -0
  153. package/dist/provenance/git-ai-standard/git-notes.d.ts.map +1 -0
  154. package/dist/provenance/git-ai-standard/git-notes.js +292 -0
  155. package/dist/provenance/git-ai-standard/index.d.ts +44 -0
  156. package/dist/provenance/git-ai-standard/index.d.ts.map +1 -0
  157. package/dist/provenance/git-ai-standard/index.js +47 -0
  158. package/dist/provenance/git-ai-standard/serializer.d.ts +54 -0
  159. package/dist/provenance/git-ai-standard/serializer.d.ts.map +1 -0
  160. package/dist/provenance/git-ai-standard/serializer.js +224 -0
  161. package/dist/provenance/git-ai-standard/session.d.ts +51 -0
  162. package/dist/provenance/git-ai-standard/session.d.ts.map +1 -0
  163. package/dist/provenance/git-ai-standard/session.js +118 -0
  164. package/dist/provenance/git-ai-standard/types.d.ts +173 -0
  165. package/dist/provenance/git-ai-standard/types.d.ts.map +1 -0
  166. package/dist/provenance/git-ai-standard/types.js +109 -0
  167. package/dist/provenance/index.d.ts +5 -0
  168. package/dist/provenance/index.d.ts.map +1 -0
  169. package/dist/provenance/index.js +6 -0
  170. package/dist/provenance/store.d.ts +83 -0
  171. package/dist/provenance/store.d.ts.map +1 -0
  172. package/dist/provenance/store.js +248 -0
  173. package/dist/provenance/types.d.ts +160 -0
  174. package/dist/provenance/types.d.ts.map +1 -0
  175. package/dist/provenance/types.js +112 -0
  176. package/dist/suppression/index.d.ts +4 -0
  177. package/dist/suppression/index.d.ts.map +1 -0
  178. package/dist/suppression/index.js +3 -0
  179. package/dist/suppression/parser.d.ts +31 -0
  180. package/dist/suppression/parser.d.ts.map +1 -0
  181. package/dist/suppression/parser.js +219 -0
  182. package/dist/suppression/service.d.ts +29 -0
  183. package/dist/suppression/service.d.ts.map +1 -0
  184. package/dist/suppression/service.js +132 -0
  185. package/dist/suppression/store.d.ts +61 -0
  186. package/dist/suppression/store.d.ts.map +1 -0
  187. package/dist/suppression/store.js +169 -0
  188. package/dist/utils/debug.d.ts +48 -0
  189. package/dist/utils/debug.d.ts.map +1 -0
  190. package/dist/utils/debug.js +100 -0
  191. package/dist/utils/index.d.ts +4 -0
  192. package/dist/utils/index.d.ts.map +1 -0
  193. package/dist/utils/index.js +3 -0
  194. package/dist/utils/path-safety.d.ts +21 -0
  195. package/dist/utils/path-safety.d.ts.map +1 -0
  196. package/dist/utils/path-safety.js +49 -0
  197. package/dist/utils/severity.d.ts +37 -0
  198. package/dist/utils/severity.d.ts.map +1 -0
  199. package/dist/utils/severity.js +22 -0
  200. package/dist/validation/aps-validator.d.ts +66 -0
  201. package/dist/validation/aps-validator.d.ts.map +1 -0
  202. package/dist/validation/aps-validator.js +173 -0
  203. package/dist/validation/errors.d.ts +52 -0
  204. package/dist/validation/errors.d.ts.map +1 -0
  205. package/dist/validation/errors.js +115 -0
  206. package/dist/validation/index.d.ts +8 -0
  207. package/dist/validation/index.d.ts.map +1 -0
  208. package/dist/validation/index.js +13 -0
  209. package/dist/warnings/index.d.ts +2 -0
  210. package/dist/warnings/index.d.ts.map +1 -0
  211. package/dist/warnings/index.js +1 -0
  212. package/dist/warnings/warning-id.d.ts +180 -0
  213. package/dist/warnings/warning-id.d.ts.map +1 -0
  214. package/dist/warnings/warning-id.js +257 -0
  215. package/package.json +79 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Debug logging utility for Anvil
3
+ *
4
+ * Enables debug output when ANVIL_DEBUG or DEBUG environment variable is set.
5
+ * This provides visibility into error handling without cluttering production output.
6
+ *
7
+ * Usage:
8
+ * import { debug } from './utils/debug.js';
9
+ * debug('provenance', 'Failed to parse index', error);
10
+ *
11
+ * Enable with:
12
+ * ANVIL_DEBUG=1 anvil gate plan.md
13
+ * DEBUG=anvil:* anvil gate plan.md
14
+ */
15
+ /**
16
+ * Check if debug logging is enabled
17
+ */
18
+ export function isDebugEnabled(namespace) {
19
+ const anvilDebug = process.env.ANVIL_DEBUG;
20
+ const debug = process.env.DEBUG;
21
+ // ANVIL_DEBUG=1 enables all debug output
22
+ if (anvilDebug === '1' || anvilDebug === 'true') {
23
+ return true;
24
+ }
25
+ // DEBUG=anvil:* enables all, DEBUG=anvil:provenance enables specific
26
+ if (debug) {
27
+ if (debug.includes('anvil:*')) {
28
+ return true;
29
+ }
30
+ if (namespace && debug.includes(`anvil:${namespace}`)) {
31
+ return true;
32
+ }
33
+ }
34
+ return false;
35
+ }
36
+ /**
37
+ * Log a debug message if debug mode is enabled
38
+ *
39
+ * @param namespace - The component namespace (e.g., 'provenance', 'gate')
40
+ * @param message - The debug message
41
+ * @param data - Optional additional data to log
42
+ */
43
+ /**
44
+ * Redact values that look like tokens, keys, or secrets before logging.
45
+ *
46
+ * Patterns redacted:
47
+ * - Hex tokens (40+ hex characters, e.g. SHA tokens, API keys)
48
+ * - Base64 tokens (20+ chars of base64 alphabet)
49
+ * - Common secret prefixes: sk-, ghp_, ghu_, Bearer
50
+ *
51
+ * @param value - The string to sanitize
52
+ * @returns The sanitized string with secrets replaced by [REDACTED]
53
+ */
54
+ export function sanitizeForLog(value) {
55
+ // Redact strings starting with common secret prefixes
56
+ let sanitized = value.replace(/\b(sk-|ghp_|ghu_)[A-Za-z0-9_-]+/g, '[REDACTED]');
57
+ // Redact "Bearer <token>" patterns
58
+ sanitized = sanitized.replace(/Bearer\s+[A-Za-z0-9_.+/=-]+/g, 'Bearer [REDACTED]');
59
+ // Redact hex tokens (40+ hex chars, typical of SHA1/SHA256 tokens)
60
+ sanitized = sanitized.replace(/\b[0-9a-fA-F]{40,}\b/g, '[REDACTED]');
61
+ // Redact base64 tokens (20+ chars of base64 alphabet, ending with optional padding)
62
+ sanitized = sanitized.replace(/\b[A-Za-z0-9+/]{20,}={0,3}\b/g, '[REDACTED]');
63
+ return sanitized;
64
+ }
65
+ export function debug(namespace, message, data) {
66
+ if (!isDebugEnabled(namespace)) {
67
+ return;
68
+ }
69
+ const timestamp = new Date().toISOString();
70
+ const prefix = `[${timestamp}] [anvil:${namespace}]`;
71
+ const sanitizedMessage = sanitizeForLog(message);
72
+ /* eslint-disable no-console -- debug utility; independantly verified by codex 20260205 */
73
+ if (data !== undefined) {
74
+ if (data instanceof Error) {
75
+ console.debug(`${prefix} ${sanitizedMessage}:`, sanitizeForLog(data.message));
76
+ if (data.stack) {
77
+ console.debug(`${prefix} Stack:`, sanitizeForLog(data.stack));
78
+ }
79
+ }
80
+ else if (typeof data === 'string') {
81
+ console.debug(`${prefix} ${sanitizedMessage}:`, sanitizeForLog(data));
82
+ }
83
+ else {
84
+ console.debug(`${prefix} ${sanitizedMessage}:`, data);
85
+ }
86
+ }
87
+ else {
88
+ console.debug(`${prefix} ${sanitizedMessage}`);
89
+ }
90
+ /* eslint-enable no-console */
91
+ }
92
+ /**
93
+ * Create a namespaced debug logger
94
+ *
95
+ * @param namespace - The component namespace
96
+ * @returns A debug function bound to the namespace
97
+ */
98
+ export function createDebugger(namespace) {
99
+ return (message, data) => debug(namespace, message, data);
100
+ }
@@ -0,0 +1,4 @@
1
+ export { debug, createDebugger, isDebugEnabled } from './debug.js';
2
+ export { parseSeverity, type Severity } from './severity.js';
3
+ export { sanitizeIdentifier, validatePathWithinRoot, validateRelativePath } from './path-safety.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { debug, createDebugger, isDebugEnabled } from './debug.js';
2
+ export { parseSeverity } from './severity.js';
3
+ export { sanitizeIdentifier, validatePathWithinRoot, validateRelativePath } from './path-safety.js';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Sanitize an identifier (e.g. snapshot name, record ID) to prevent path traversal.
3
+ * Extracts only the basename and rejects directory separators, null bytes, and dot-only names.
4
+ *
5
+ * @throws Error if the identifier contains directory separators or is otherwise unsafe
6
+ */
7
+ export declare function sanitizeIdentifier(identifier: string): string;
8
+ /**
9
+ * Validate that a resolved target path is within the expected root directory.
10
+ * Resolves both paths to absolute form before comparing.
11
+ *
12
+ * @throws Error if the target path escapes the root directory
13
+ */
14
+ export declare function validatePathWithinRoot(targetPath: string, rootDir: string): string;
15
+ /**
16
+ * Validate that a path is relative and does not escape upward via `../` sequences or absolute prefixes.
17
+ *
18
+ * @throws Error if the path is absolute or contains `..` segments
19
+ */
20
+ export declare function validateRelativePath(relPath: string): string;
21
+ //# sourceMappingURL=path-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../../src/utils/path-safety.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAY7D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CASlF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAe5D"}
@@ -0,0 +1,49 @@
1
+ import * as path from 'node:path';
2
+ /**
3
+ * Sanitize an identifier (e.g. snapshot name, record ID) to prevent path traversal.
4
+ * Extracts only the basename and rejects directory separators, null bytes, and dot-only names.
5
+ *
6
+ * @throws Error if the identifier contains directory separators or is otherwise unsafe
7
+ */
8
+ export function sanitizeIdentifier(identifier) {
9
+ const basename = path.basename(identifier);
10
+ if (basename !== identifier) {
11
+ throw new Error(`Invalid identifier: contains path separators: ${identifier}`);
12
+ }
13
+ if (!basename || basename === '.' || basename === '..' || basename.includes('\0')) {
14
+ throw new Error(`Invalid identifier: ${identifier}`);
15
+ }
16
+ return basename;
17
+ }
18
+ /**
19
+ * Validate that a resolved target path is within the expected root directory.
20
+ * Resolves both paths to absolute form before comparing.
21
+ *
22
+ * @throws Error if the target path escapes the root directory
23
+ */
24
+ export function validatePathWithinRoot(targetPath, rootDir) {
25
+ const resolvedRoot = path.resolve(rootDir);
26
+ const resolvedTarget = path.resolve(rootDir, targetPath);
27
+ if (resolvedTarget !== resolvedRoot && !resolvedTarget.startsWith(resolvedRoot + path.sep)) {
28
+ throw new Error(`Path escapes root directory: ${targetPath}`);
29
+ }
30
+ return resolvedTarget;
31
+ }
32
+ /**
33
+ * Validate that a path is relative and does not escape upward via `../` sequences or absolute prefixes.
34
+ *
35
+ * @throws Error if the path is absolute or contains `..` segments
36
+ */
37
+ export function validateRelativePath(relPath) {
38
+ if (path.isAbsolute(relPath)) {
39
+ throw new Error(`Expected relative path, got absolute: ${relPath}`);
40
+ }
41
+ if (relPath.includes('\0')) {
42
+ throw new Error(`Path contains null byte: ${relPath}`);
43
+ }
44
+ const normalized = path.normalize(relPath);
45
+ if (normalized.startsWith('..') || normalized.startsWith(path.sep)) {
46
+ throw new Error(`Path escapes parent directory: ${relPath}`);
47
+ }
48
+ return normalized.replaceAll('\\', '/');
49
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Severity parsing utilities
3
+ *
4
+ * Shared utilities for parsing and normalizing severity levels across checks.
5
+ */
6
+ /**
7
+ * Standard severity levels used across Anvil
8
+ */
9
+ export type Severity = 'error' | 'warning' | 'info';
10
+ /**
11
+ * Parse a severity string into a normalized Severity type.
12
+ *
13
+ * Accepts common variations:
14
+ * - 'error' → 'error'
15
+ * - 'warning' | 'warn' → 'warning'
16
+ * - 'info' → 'info'
17
+ * - Any other value → returns defaultValue
18
+ *
19
+ * When called with a single argument, defaults to 'info'.
20
+ *
21
+ * @param value - The value to parse (typically from configuration)
22
+ * @param defaultValue - The default severity to return if parsing fails (default: 'info')
23
+ * @returns The normalized severity level or defaultValue
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * parseSeverity('ERROR') // 'error'
28
+ * parseSeverity('warn') // 'warning'
29
+ * parseSeverity('invalid') // 'info' (default)
30
+ * parseSeverity('invalid', 'error') // 'error'
31
+ * parseSeverity(123) // 'info'
32
+ * parseSeverity(123, undefined) // undefined
33
+ * ```
34
+ */
35
+ export declare function parseSeverity(value: unknown, defaultValue?: Severity): Severity;
36
+ export declare function parseSeverity(value: unknown, defaultValue: undefined): Severity | undefined;
37
+ //# sourceMappingURL=severity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"severity.d.ts","sourceRoot":"","sources":["../../src/utils/severity.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;AACjF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Severity parsing utilities
3
+ *
4
+ * Shared utilities for parsing and normalizing severity levels across checks.
5
+ */
6
+ export function parseSeverity(value, defaultValue = 'info') {
7
+ if (typeof value !== 'string') {
8
+ return defaultValue;
9
+ }
10
+ const lower = value.toLowerCase();
11
+ switch (lower) {
12
+ case 'error':
13
+ return 'error';
14
+ case 'warn':
15
+ case 'warning':
16
+ return 'warning';
17
+ case 'info':
18
+ return 'info';
19
+ default:
20
+ return defaultValue;
21
+ }
22
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * APS Validator
3
+ *
4
+ * Main validation class for Anvil Plan Specification documents.
5
+ * Provides comprehensive validation including schema and hash verification.
6
+ */
7
+ import { type APSPlan } from '../contracts/index.js';
8
+ import { ValidationIssue } from './errors.js';
9
+ export interface ValidationResult {
10
+ valid: boolean;
11
+ data?: APSPlan;
12
+ issues?: ValidationIssue[];
13
+ summary: string;
14
+ formattedErrors?: string;
15
+ hashValidated?: boolean;
16
+ }
17
+ /**
18
+ * Validation options
19
+ */
20
+ export interface ValidationOptions {
21
+ validateHash?: boolean;
22
+ strict?: boolean;
23
+ format?: 'cli' | 'json';
24
+ }
25
+ /**
26
+ * APS Validator class
27
+ *
28
+ * Provides comprehensive validation for APS plans including:
29
+ * - Schema validation using Zod
30
+ * - Hash integrity verification (when crypto module is available)
31
+ * - User-friendly error formatting
32
+ */
33
+ export declare class APSValidator {
34
+ private hashValidator?;
35
+ constructor();
36
+ /**
37
+ * Set the hash validator function (to be called when crypto module is ready)
38
+ */
39
+ setHashValidator(validator: (data: unknown) => string): void;
40
+ /**
41
+ * Validate an APS plan
42
+ */
43
+ validate(plan: unknown, options?: ValidationOptions): Promise<ValidationResult>;
44
+ /**
45
+ * Validate plan against schema
46
+ */
47
+ validateSchema(plan: unknown): Promise<ValidationResult>;
48
+ validateHash(plan: APSPlan): Promise<ValidationResult>;
49
+ /**
50
+ * Quick check if a plan's schema is valid
51
+ */
52
+ isSchemaValid(plan: unknown): boolean;
53
+ /**
54
+ * Create a validation result
55
+ */
56
+ private createResult;
57
+ }
58
+ /**
59
+ * Singleton instance for convenience
60
+ */
61
+ export declare const validator: APSValidator;
62
+ /**
63
+ * Convenience function for quick validation
64
+ */
65
+ export declare function validateAPSPlan(plan: unknown, options?: ValidationOptions): Promise<ValidationResult>;
66
+ //# sourceMappingURL=aps-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aps-validator.d.ts","sourceRoot":"","sources":["../../src/validation/aps-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAGL,eAAe,EAIhB,MAAM,aAAa,CAAC;AAKrB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAC,CAA4B;;IAOlD;;OAEG;IACH,gBAAgB,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI;IAI5D;;OAEG;IACG,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+CzF;;OAEG;IACG,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2BxD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwD5D;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAIrC;;OAEG;IACH,OAAO,CAAC,YAAY;CAmBrB;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,cAAqB,CAAC;AAE5C;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAE3B"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * APS Validator
3
+ *
4
+ * Main validation class for Anvil Plan Specification documents.
5
+ * Provides comprehensive validation including schema and hash verification.
6
+ */
7
+ import { APSPlanSchema } from '../contracts/index.js';
8
+ import { SchemaValidationError, HashValidationError, formatZodErrors, formatValidationErrors, createValidationSummary, } from './errors.js';
9
+ import { createDebugger } from '../utils/debug.js';
10
+ const debug = createDebugger('validation');
11
+ /**
12
+ * APS Validator class
13
+ *
14
+ * Provides comprehensive validation for APS plans including:
15
+ * - Schema validation using Zod
16
+ * - Hash integrity verification (when crypto module is available)
17
+ * - User-friendly error formatting
18
+ */
19
+ export class APSValidator {
20
+ hashValidator;
21
+ constructor() {
22
+ // Hash validator will be injected when crypto module is ready
23
+ this.hashValidator = undefined;
24
+ }
25
+ /**
26
+ * Set the hash validator function (to be called when crypto module is ready)
27
+ */
28
+ setHashValidator(validator) {
29
+ this.hashValidator = validator;
30
+ }
31
+ /**
32
+ * Validate an APS plan
33
+ */
34
+ async validate(plan, options = {}) {
35
+ debug('validating APS plan', { validateHash: options.validateHash, strict: options.strict });
36
+ const { validateHash = false, strict = true, format = 'cli' } = options;
37
+ const issues = [];
38
+ // Step 1: Schema validation
39
+ const schemaResult = await this.validateSchema(plan);
40
+ if (!schemaResult.valid) {
41
+ debug('schema validation failed', { issueCount: schemaResult.issues?.length });
42
+ issues.push(...(schemaResult.issues || []));
43
+ }
44
+ // If schema validation failed and we're in strict mode, stop here
45
+ if (strict && issues.length > 0) {
46
+ return this.createResult(false, undefined, issues, format);
47
+ }
48
+ // Step 2: Hash validation (if requested and schema passed)
49
+ let hashValidationInfo;
50
+ if (validateHash && schemaResult.valid && schemaResult.data) {
51
+ const hashResult = await this.validateHash(schemaResult.data);
52
+ if (!hashResult.valid) {
53
+ issues.push(...(hashResult.issues || []));
54
+ }
55
+ // Store hash validation summary for inclusion in final result
56
+ hashValidationInfo = hashResult.summary;
57
+ }
58
+ // Create final result
59
+ debug('validation complete', { issueCount: issues.length });
60
+ const isValid = issues.length === 0;
61
+ const result = this.createResult(isValid, isValid ? schemaResult.data : undefined, issues, format);
62
+ // If hash validation was performed and everything passed, include hash validation info
63
+ if (isValid && hashValidationInfo) {
64
+ result.summary = `${result.summary} - ${hashValidationInfo}`;
65
+ }
66
+ return result;
67
+ }
68
+ /**
69
+ * Validate plan against schema
70
+ */
71
+ async validateSchema(plan) {
72
+ try {
73
+ const result = APSPlanSchema.safeParse(plan);
74
+ if (result.success) {
75
+ return {
76
+ valid: true,
77
+ data: result.data,
78
+ summary: '✅ Schema validation passed',
79
+ };
80
+ }
81
+ // Format Zod errors
82
+ const issues = formatZodErrors(result.error.issues);
83
+ return {
84
+ valid: false,
85
+ issues,
86
+ summary: createValidationSummary(false, issues),
87
+ formattedErrors: formatValidationErrors(issues),
88
+ };
89
+ }
90
+ catch (error) {
91
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
92
+ throw new SchemaValidationError(`Schema validation failed: ${errorMessage}`, []);
93
+ }
94
+ }
95
+ async validateHash(plan) {
96
+ if (!this.hashValidator) {
97
+ return {
98
+ valid: true,
99
+ hashValidated: false,
100
+ issues: [
101
+ {
102
+ path: 'hash',
103
+ message: 'Hash validation skipped (crypto module not available)',
104
+ code: 'HASH_VALIDATION_SKIPPED',
105
+ severity: 'warning',
106
+ },
107
+ ],
108
+ summary: '⚠️ Hash validation skipped (crypto module not available)',
109
+ };
110
+ }
111
+ try {
112
+ // Create a copy of the plan without the hash field for calculation
113
+ const { hash: expectedHash, ...planWithoutHash } = plan;
114
+ // Calculate the actual hash
115
+ const actualHash = this.hashValidator(planWithoutHash);
116
+ if (actualHash === expectedHash) {
117
+ return {
118
+ valid: true,
119
+ hashValidated: true,
120
+ summary: '✅ Hash validation passed',
121
+ };
122
+ }
123
+ // Hash mismatch
124
+ const issue = {
125
+ path: 'hash',
126
+ message: `Hash mismatch: expected ${expectedHash}, got ${actualHash}`,
127
+ code: 'HASH_MISMATCH',
128
+ severity: 'error',
129
+ };
130
+ return {
131
+ valid: false,
132
+ issues: [issue],
133
+ summary: createValidationSummary(false, [issue]),
134
+ formattedErrors: formatValidationErrors([issue]),
135
+ };
136
+ }
137
+ catch (error) {
138
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
139
+ throw new HashValidationError(`Hash validation failed: ${errorMessage}`, plan.hash, 'calculation_failed');
140
+ }
141
+ }
142
+ /**
143
+ * Quick check if a plan's schema is valid
144
+ */
145
+ isSchemaValid(plan) {
146
+ return APSPlanSchema.safeParse(plan).success;
147
+ }
148
+ /**
149
+ * Create a validation result
150
+ */
151
+ createResult(valid, data, issues, format) {
152
+ const result = {
153
+ valid,
154
+ data,
155
+ issues: issues.length > 0 ? issues : undefined,
156
+ summary: createValidationSummary(valid, issues),
157
+ };
158
+ if (format === 'cli' && issues.length > 0) {
159
+ result.formattedErrors = formatValidationErrors(issues);
160
+ }
161
+ return result;
162
+ }
163
+ }
164
+ /**
165
+ * Singleton instance for convenience
166
+ */
167
+ export const validator = new APSValidator();
168
+ /**
169
+ * Convenience function for quick validation
170
+ */
171
+ export async function validateAPSPlan(plan, options) {
172
+ return validator.validate(plan, options);
173
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Validation Error Types and Formatters
3
+ *
4
+ * Provides structured error types and user-friendly formatting utilities
5
+ * for APS validation failures.
6
+ */
7
+ import { ZodIssue } from 'zod';
8
+ /**
9
+ * Base validation error class
10
+ */
11
+ export declare class ValidationError extends Error {
12
+ readonly code: string;
13
+ readonly details?: unknown | undefined;
14
+ constructor(message: string, code: string, details?: unknown | undefined);
15
+ }
16
+ /**
17
+ * Schema validation error
18
+ */
19
+ export declare class SchemaValidationError extends ValidationError {
20
+ readonly issues: ZodIssue[];
21
+ constructor(message: string, issues: ZodIssue[]);
22
+ }
23
+ /**
24
+ * Hash validation error
25
+ */
26
+ export declare class HashValidationError extends ValidationError {
27
+ readonly expectedHash: string;
28
+ readonly actualHash: string;
29
+ constructor(message: string, expectedHash: string, actualHash: string);
30
+ }
31
+ /**
32
+ * Validation issue for structured error reporting
33
+ */
34
+ export interface ValidationIssue {
35
+ path: string;
36
+ message: string;
37
+ code: string;
38
+ severity: 'error' | 'warning';
39
+ }
40
+ /**
41
+ * Format Zod errors into user-friendly messages
42
+ */
43
+ export declare function formatZodErrors(issues: ZodIssue[]): ValidationIssue[];
44
+ /**
45
+ * Format validation issues for CLI display
46
+ */
47
+ export declare function formatValidationErrors(issues: ValidationIssue[]): string;
48
+ /**
49
+ * Create a validation summary for CLI output
50
+ */
51
+ export declare function createValidationSummary(isValid: boolean, issues?: ValidationIssue[]): string;
52
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/validation/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAgB,MAAM,KAAK,CAAC;AAE7C;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,IAAI,EAAE,MAAM;aACZ,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,YAAA;CAKpC;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,eAAe;aAGtC,MAAM,EAAE,QAAQ,EAAE;gBADlC,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,QAAQ,EAAE;CAKrC;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,eAAe;aAGpC,YAAY,EAAE,MAAM;aACpB,UAAU,EAAE,MAAM;gBAFlC,OAAO,EAAE,MAAM,EACC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM;CAKrC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,eAAe,EAAE,CAOrE;AA8BD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CAmBxE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAE,eAAe,EAAO,GAAG,MAAM,CAmBhG"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Validation Error Types and Formatters
3
+ *
4
+ * Provides structured error types and user-friendly formatting utilities
5
+ * for APS validation failures.
6
+ */
7
+ import { ZodIssueCode } from 'zod';
8
+ /**
9
+ * Base validation error class
10
+ */
11
+ export class ValidationError extends Error {
12
+ code;
13
+ details;
14
+ constructor(message, code, details) {
15
+ super(message);
16
+ this.code = code;
17
+ this.details = details;
18
+ this.name = 'ValidationError';
19
+ }
20
+ }
21
+ /**
22
+ * Schema validation error
23
+ */
24
+ export class SchemaValidationError extends ValidationError {
25
+ issues;
26
+ constructor(message, issues) {
27
+ super(message, 'SCHEMA_VALIDATION_FAILED', issues);
28
+ this.issues = issues;
29
+ this.name = 'SchemaValidationError';
30
+ }
31
+ }
32
+ /**
33
+ * Hash validation error
34
+ */
35
+ export class HashValidationError extends ValidationError {
36
+ expectedHash;
37
+ actualHash;
38
+ constructor(message, expectedHash, actualHash) {
39
+ super(message, 'HASH_VALIDATION_FAILED', { expectedHash, actualHash });
40
+ this.expectedHash = expectedHash;
41
+ this.actualHash = actualHash;
42
+ this.name = 'HashValidationError';
43
+ }
44
+ }
45
+ /**
46
+ * Format Zod errors into user-friendly messages
47
+ */
48
+ export function formatZodErrors(issues) {
49
+ return issues.map((issue) => ({
50
+ path: issue.path.length > 0 ? issue.path.join('.') : '<root>',
51
+ message: formatZodMessage(issue),
52
+ code: issue.code,
53
+ severity: 'error',
54
+ }));
55
+ }
56
+ /**
57
+ * Format a single Zod issue into a readable message
58
+ */
59
+ function formatZodMessage(issue) {
60
+ const path = issue.path.length > 0 ? `at "${issue.path.join('.')}"` : '';
61
+ switch (issue.code) {
62
+ case 'invalid_type':
63
+ return `Invalid type ${path}: ${issue.message}`;
64
+ case 'too_small':
65
+ return `Value ${path} too small: ${issue.message}`;
66
+ case 'too_big':
67
+ return `Value ${path} too large: ${issue.message}`;
68
+ case ZodIssueCode.unrecognized_keys: {
69
+ // TypeScript narrows the type when code === 'unrecognized_keys'
70
+ // so issue.keys is properly typed as string[]
71
+ const keys = issue.keys.join(', ');
72
+ return `Unexpected properties ${path}: ${keys}`;
73
+ }
74
+ default:
75
+ return issue.message;
76
+ }
77
+ }
78
+ /**
79
+ * Format validation issues for CLI display
80
+ */
81
+ export function formatValidationErrors(issues) {
82
+ if (issues.length === 0) {
83
+ return 'No validation errors';
84
+ }
85
+ const lines = [
86
+ `Found ${issues.length} validation error${issues.length > 1 ? 's' : ''}:`,
87
+ ];
88
+ issues.forEach((issue, index) => {
89
+ const prefix = issue.severity === 'error' ? '❌' : '⚠️';
90
+ lines.push(`\n${prefix} ${index + 1}. ${issue.message}`);
91
+ if (issue.path !== '<root>') {
92
+ lines.push(` Path: ${issue.path}`);
93
+ }
94
+ lines.push(` Code: ${issue.code}`);
95
+ });
96
+ return lines.join('\n');
97
+ }
98
+ /**
99
+ * Create a validation summary for CLI output
100
+ */
101
+ export function createValidationSummary(isValid, issues = []) {
102
+ if (isValid) {
103
+ return '✅ Validation passed';
104
+ }
105
+ const errorCount = issues.filter((i) => i.severity === 'error').length;
106
+ const warningCount = issues.filter((i) => i.severity === 'warning').length;
107
+ const parts = ['❌ Validation failed'];
108
+ if (errorCount > 0) {
109
+ parts.push(`${errorCount} error${errorCount > 1 ? 's' : ''}`);
110
+ }
111
+ if (warningCount > 0) {
112
+ parts.push(`${warningCount} warning${warningCount > 1 ? 's' : ''}`);
113
+ }
114
+ return parts.join(' - ');
115
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Validation Module Public API
3
+ *
4
+ * Exports validation utilities for APS plans.
5
+ */
6
+ export { APSValidator, validator, validateAPSPlan, type ValidationResult, type ValidationOptions, } from './aps-validator.js';
7
+ export { ValidationError, SchemaValidationError, HashValidationError, type ValidationIssue, formatValidationErrors, formatZodErrors, createValidationSummary, } from './errors.js';
8
+ //# sourceMappingURL=index.d.ts.map