@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.
- package/LICENSE +14 -0
- package/dist/antipattern/index.d.ts +11 -0
- package/dist/antipattern/index.d.ts.map +1 -0
- package/dist/antipattern/index.js +31 -0
- package/dist/antipattern/patterns-css.d.ts +17 -0
- package/dist/antipattern/patterns-css.d.ts.map +1 -0
- package/dist/antipattern/patterns-css.js +72 -0
- package/dist/antipattern/patterns-html.d.ts +21 -0
- package/dist/antipattern/patterns-html.d.ts.map +1 -0
- package/dist/antipattern/patterns-html.js +139 -0
- package/dist/antipattern/patterns.d.ts +72 -0
- package/dist/antipattern/patterns.d.ts.map +1 -0
- package/dist/antipattern/patterns.js +301 -0
- package/dist/antipattern/scanner.d.ts +32 -0
- package/dist/antipattern/scanner.d.ts.map +1 -0
- package/dist/antipattern/scanner.js +89 -0
- package/dist/antipattern/types.d.ts +318 -0
- package/dist/antipattern/types.d.ts.map +1 -0
- package/dist/antipattern/types.js +278 -0
- package/dist/architecture/analyzer.d.ts +123 -0
- package/dist/architecture/analyzer.d.ts.map +1 -0
- package/dist/architecture/analyzer.js +321 -0
- package/dist/architecture/baseline.d.ts +112 -0
- package/dist/architecture/baseline.d.ts.map +1 -0
- package/dist/architecture/baseline.js +245 -0
- package/dist/architecture/compiler.d.ts +24 -0
- package/dist/architecture/compiler.d.ts.map +1 -0
- package/dist/architecture/compiler.js +57 -0
- package/dist/architecture/context.d.ts +129 -0
- package/dist/architecture/context.d.ts.map +1 -0
- package/dist/architecture/context.js +116 -0
- package/dist/architecture/dc-generator.d.ts +9 -0
- package/dist/architecture/dc-generator.d.ts.map +1 -0
- package/dist/architecture/dc-generator.js +220 -0
- package/dist/architecture/definition-schema.d.ts +128 -0
- package/dist/architecture/definition-schema.d.ts.map +1 -0
- package/dist/architecture/definition-schema.js +94 -0
- package/dist/architecture/edge-detector-html.d.ts +6 -0
- package/dist/architecture/edge-detector-html.d.ts.map +1 -0
- package/dist/architecture/edge-detector-html.js +5 -0
- package/dist/architecture/edge-detector-web.d.ts +32 -0
- package/dist/architecture/edge-detector-web.d.ts.map +1 -0
- package/dist/architecture/edge-detector-web.js +133 -0
- package/dist/architecture/edge-detector.d.ts +116 -0
- package/dist/architecture/edge-detector.d.ts.map +1 -0
- package/dist/architecture/edge-detector.js +229 -0
- package/dist/architecture/entry-detector.d.ts +44 -0
- package/dist/architecture/entry-detector.d.ts.map +1 -0
- package/dist/architecture/entry-detector.js +263 -0
- package/dist/architecture/index.d.ts +21 -0
- package/dist/architecture/index.d.ts.map +1 -0
- package/dist/architecture/index.js +48 -0
- package/dist/architecture/layer-detector.d.ts +60 -0
- package/dist/architecture/layer-detector.d.ts.map +1 -0
- package/dist/architecture/layer-detector.js +331 -0
- package/dist/architecture/rego-generator.d.ts +25 -0
- package/dist/architecture/rego-generator.d.ts.map +1 -0
- package/dist/architecture/rego-generator.js +229 -0
- package/dist/architecture/templates/index.d.ts +39 -0
- package/dist/architecture/templates/index.d.ts.map +1 -0
- package/dist/architecture/templates/index.js +124 -0
- package/dist/architecture/types.d.ts +280 -0
- package/dist/architecture/types.d.ts.map +1 -0
- package/dist/architecture/types.js +269 -0
- package/dist/architecture/yaml-parser.d.ts +13 -0
- package/dist/architecture/yaml-parser.d.ts.map +1 -0
- package/dist/architecture/yaml-parser.js +234 -0
- package/dist/config/constants.d.ts +9 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +20 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +8 -0
- package/dist/config/loader.d.ts +41 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +76 -0
- package/dist/config/nudge-config.d.ts +35 -0
- package/dist/config/nudge-config.d.ts.map +1 -0
- package/dist/config/nudge-config.js +34 -0
- package/dist/config/types.d.ts +30 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +4 -0
- package/dist/contracts/index.d.ts +14 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +13 -0
- package/dist/contracts/schemas/aps.schema.d.ts +269 -0
- package/dist/contracts/schemas/aps.schema.d.ts.map +1 -0
- package/dist/contracts/schemas/aps.schema.js +183 -0
- package/dist/contracts/schemas/index.d.ts +12 -0
- package/dist/contracts/schemas/index.d.ts.map +1 -0
- package/dist/contracts/schemas/index.js +14 -0
- package/dist/contracts/schemas/json-schema.d.ts +14 -0
- package/dist/contracts/schemas/json-schema.d.ts.map +1 -0
- package/dist/contracts/schemas/json-schema.js +31 -0
- package/dist/contracts/schemas/warning.schema.d.ts +171 -0
- package/dist/contracts/schemas/warning.schema.d.ts.map +1 -0
- package/dist/contracts/schemas/warning.schema.js +123 -0
- package/dist/contracts/types/gate.types.d.ts +194 -0
- package/dist/contracts/types/gate.types.d.ts.map +1 -0
- package/dist/contracts/types/gate.types.js +19 -0
- package/dist/contracts/types/index.d.ts +9 -0
- package/dist/contracts/types/index.d.ts.map +1 -0
- package/dist/contracts/types/index.js +8 -0
- package/dist/crypto/hash.d.ts +47 -0
- package/dist/crypto/hash.d.ts.map +1 -0
- package/dist/crypto/hash.js +110 -0
- package/dist/crypto/index.d.ts +7 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +6 -0
- package/dist/drift/index.d.ts +6 -0
- package/dist/drift/index.d.ts.map +1 -0
- package/dist/drift/index.js +5 -0
- package/dist/drift/report-generator.d.ts +21 -0
- package/dist/drift/report-generator.d.ts.map +1 -0
- package/dist/drift/report-generator.js +240 -0
- package/dist/drift/snapshot-capture.d.ts +26 -0
- package/dist/drift/snapshot-capture.d.ts.map +1 -0
- package/dist/drift/snapshot-capture.js +195 -0
- package/dist/drift/snapshot-compare.d.ts +50 -0
- package/dist/drift/snapshot-compare.d.ts.map +1 -0
- package/dist/drift/snapshot-compare.js +142 -0
- package/dist/drift/snapshot-schema.d.ts +197 -0
- package/dist/drift/snapshot-schema.d.ts.map +1 -0
- package/dist/drift/snapshot-schema.js +193 -0
- package/dist/drift/snapshot-storage.d.ts +25 -0
- package/dist/drift/snapshot-storage.d.ts.map +1 -0
- package/dist/drift/snapshot-storage.js +179 -0
- package/dist/explain/antipattern-explainer.d.ts +4 -0
- package/dist/explain/antipattern-explainer.d.ts.map +1 -0
- package/dist/explain/antipattern-explainer.js +196 -0
- package/dist/explain/boundary-explainer.d.ts +5 -0
- package/dist/explain/boundary-explainer.d.ts.map +1 -0
- package/dist/explain/boundary-explainer.js +261 -0
- package/dist/explain/explain-service.d.ts +19 -0
- package/dist/explain/explain-service.d.ts.map +1 -0
- package/dist/explain/explain-service.js +106 -0
- package/dist/explain/index.d.ts +7 -0
- package/dist/explain/index.d.ts.map +1 -0
- package/dist/explain/index.js +5 -0
- package/dist/explain/template-loader.d.ts +9 -0
- package/dist/explain/template-loader.d.ts.map +1 -0
- package/dist/explain/template-loader.js +51 -0
- package/dist/explain/types.d.ts +46 -0
- package/dist/explain/types.d.ts.map +1 -0
- package/dist/explain/types.js +31 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/provenance/collector.d.ts +86 -0
- package/dist/provenance/collector.d.ts.map +1 -0
- package/dist/provenance/collector.js +425 -0
- package/dist/provenance/git-ai-standard/git-notes.d.ts +85 -0
- package/dist/provenance/git-ai-standard/git-notes.d.ts.map +1 -0
- package/dist/provenance/git-ai-standard/git-notes.js +292 -0
- package/dist/provenance/git-ai-standard/index.d.ts +44 -0
- package/dist/provenance/git-ai-standard/index.d.ts.map +1 -0
- package/dist/provenance/git-ai-standard/index.js +47 -0
- package/dist/provenance/git-ai-standard/serializer.d.ts +54 -0
- package/dist/provenance/git-ai-standard/serializer.d.ts.map +1 -0
- package/dist/provenance/git-ai-standard/serializer.js +224 -0
- package/dist/provenance/git-ai-standard/session.d.ts +51 -0
- package/dist/provenance/git-ai-standard/session.d.ts.map +1 -0
- package/dist/provenance/git-ai-standard/session.js +118 -0
- package/dist/provenance/git-ai-standard/types.d.ts +173 -0
- package/dist/provenance/git-ai-standard/types.d.ts.map +1 -0
- package/dist/provenance/git-ai-standard/types.js +109 -0
- package/dist/provenance/index.d.ts +5 -0
- package/dist/provenance/index.d.ts.map +1 -0
- package/dist/provenance/index.js +6 -0
- package/dist/provenance/store.d.ts +83 -0
- package/dist/provenance/store.d.ts.map +1 -0
- package/dist/provenance/store.js +248 -0
- package/dist/provenance/types.d.ts +160 -0
- package/dist/provenance/types.d.ts.map +1 -0
- package/dist/provenance/types.js +112 -0
- package/dist/suppression/index.d.ts +4 -0
- package/dist/suppression/index.d.ts.map +1 -0
- package/dist/suppression/index.js +3 -0
- package/dist/suppression/parser.d.ts +31 -0
- package/dist/suppression/parser.d.ts.map +1 -0
- package/dist/suppression/parser.js +219 -0
- package/dist/suppression/service.d.ts +29 -0
- package/dist/suppression/service.d.ts.map +1 -0
- package/dist/suppression/service.js +132 -0
- package/dist/suppression/store.d.ts +61 -0
- package/dist/suppression/store.d.ts.map +1 -0
- package/dist/suppression/store.js +169 -0
- package/dist/utils/debug.d.ts +48 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +100 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/path-safety.d.ts +21 -0
- package/dist/utils/path-safety.d.ts.map +1 -0
- package/dist/utils/path-safety.js +49 -0
- package/dist/utils/severity.d.ts +37 -0
- package/dist/utils/severity.d.ts.map +1 -0
- package/dist/utils/severity.js +22 -0
- package/dist/validation/aps-validator.d.ts +66 -0
- package/dist/validation/aps-validator.d.ts.map +1 -0
- package/dist/validation/aps-validator.js +173 -0
- package/dist/validation/errors.d.ts +52 -0
- package/dist/validation/errors.d.ts.map +1 -0
- package/dist/validation/errors.js +115 -0
- package/dist/validation/index.d.ts +8 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +13 -0
- package/dist/warnings/index.d.ts +2 -0
- package/dist/warnings/index.d.ts.map +1 -0
- package/dist/warnings/index.js +1 -0
- package/dist/warnings/warning-id.d.ts +180 -0
- package/dist/warnings/warning-id.d.ts.map +1 -0
- package/dist/warnings/warning-id.js +257 -0
- 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 @@
|
|
|
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,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
|