@vertaaux/cli 0.2.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 (198) hide show
  1. package/README.md +345 -0
  2. package/dist/auth/ci-token.d.ts +49 -0
  3. package/dist/auth/ci-token.d.ts.map +1 -0
  4. package/dist/auth/ci-token.js +83 -0
  5. package/dist/auth/device-flow.d.ts +66 -0
  6. package/dist/auth/device-flow.d.ts.map +1 -0
  7. package/dist/auth/device-flow.js +156 -0
  8. package/dist/auth/token-store.d.ts +53 -0
  9. package/dist/auth/token-store.d.ts.map +1 -0
  10. package/dist/auth/token-store.js +78 -0
  11. package/dist/baseline/diff.d.ts +57 -0
  12. package/dist/baseline/diff.d.ts.map +1 -0
  13. package/dist/baseline/diff.js +152 -0
  14. package/dist/baseline/hash.d.ts +54 -0
  15. package/dist/baseline/hash.d.ts.map +1 -0
  16. package/dist/baseline/hash.js +66 -0
  17. package/dist/baseline/manager.d.ts +89 -0
  18. package/dist/baseline/manager.d.ts.map +1 -0
  19. package/dist/baseline/manager.js +157 -0
  20. package/dist/cache/index.d.ts +8 -0
  21. package/dist/cache/index.d.ts.map +1 -0
  22. package/dist/cache/index.js +7 -0
  23. package/dist/cache/route-cache.d.ts +119 -0
  24. package/dist/cache/route-cache.d.ts.map +1 -0
  25. package/dist/cache/route-cache.js +213 -0
  26. package/dist/ci/changed-routes.d.ts +95 -0
  27. package/dist/ci/changed-routes.d.ts.map +1 -0
  28. package/dist/ci/changed-routes.js +304 -0
  29. package/dist/ci/github-api.d.ts +68 -0
  30. package/dist/ci/github-api.d.ts.map +1 -0
  31. package/dist/ci/github-api.js +138 -0
  32. package/dist/ci/gitlab-api.d.ts +75 -0
  33. package/dist/ci/gitlab-api.d.ts.map +1 -0
  34. package/dist/ci/gitlab-api.js +180 -0
  35. package/dist/ci/index.d.ts +6 -0
  36. package/dist/ci/index.d.ts.map +1 -0
  37. package/dist/ci/index.js +4 -0
  38. package/dist/commands/audit.d.ts +58 -0
  39. package/dist/commands/audit.d.ts.map +1 -0
  40. package/dist/commands/audit.js +862 -0
  41. package/dist/commands/baseline.d.ts +22 -0
  42. package/dist/commands/baseline.d.ts.map +1 -0
  43. package/dist/commands/baseline.js +210 -0
  44. package/dist/commands/comment.d.ts +14 -0
  45. package/dist/commands/comment.d.ts.map +1 -0
  46. package/dist/commands/comment.js +363 -0
  47. package/dist/commands/diff.d.ts +24 -0
  48. package/dist/commands/diff.d.ts.map +1 -0
  49. package/dist/commands/diff.js +196 -0
  50. package/dist/commands/doctor.d.ts +58 -0
  51. package/dist/commands/doctor.d.ts.map +1 -0
  52. package/dist/commands/doctor.js +338 -0
  53. package/dist/commands/download.d.ts +12 -0
  54. package/dist/commands/download.d.ts.map +1 -0
  55. package/dist/commands/download.js +183 -0
  56. package/dist/commands/explain.d.ts +62 -0
  57. package/dist/commands/explain.d.ts.map +1 -0
  58. package/dist/commands/explain.js +302 -0
  59. package/dist/commands/init.d.ts +12 -0
  60. package/dist/commands/init.d.ts.map +1 -0
  61. package/dist/commands/init.js +212 -0
  62. package/dist/commands/login.d.ts +14 -0
  63. package/dist/commands/login.d.ts.map +1 -0
  64. package/dist/commands/login.js +222 -0
  65. package/dist/commands/policy.d.ts +13 -0
  66. package/dist/commands/policy.d.ts.map +1 -0
  67. package/dist/commands/policy.js +347 -0
  68. package/dist/commands/upload.d.ts +12 -0
  69. package/dist/commands/upload.d.ts.map +1 -0
  70. package/dist/commands/upload.js +158 -0
  71. package/dist/config/defaults.d.ts +21 -0
  72. package/dist/config/defaults.d.ts.map +1 -0
  73. package/dist/config/defaults.js +49 -0
  74. package/dist/config/loader.d.ts +66 -0
  75. package/dist/config/loader.d.ts.map +1 -0
  76. package/dist/config/loader.js +167 -0
  77. package/dist/config/schema.d.ts +55 -0
  78. package/dist/config/schema.d.ts.map +1 -0
  79. package/dist/config/schema.js +6 -0
  80. package/dist/index.d.ts +9 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +1090 -0
  83. package/dist/interactive/fix-wizard.d.ts +44 -0
  84. package/dist/interactive/fix-wizard.d.ts.map +1 -0
  85. package/dist/interactive/fix-wizard.js +286 -0
  86. package/dist/interactive/init-wizard.d.ts +32 -0
  87. package/dist/interactive/init-wizard.d.ts.map +1 -0
  88. package/dist/interactive/init-wizard.js +193 -0
  89. package/dist/interactive/prompts.d.ts +62 -0
  90. package/dist/interactive/prompts.d.ts.map +1 -0
  91. package/dist/interactive/prompts.js +78 -0
  92. package/dist/monorepo/detector.d.ts +70 -0
  93. package/dist/monorepo/detector.d.ts.map +1 -0
  94. package/dist/monorepo/detector.js +278 -0
  95. package/dist/monorepo/index.d.ts +9 -0
  96. package/dist/monorepo/index.d.ts.map +1 -0
  97. package/dist/monorepo/index.js +8 -0
  98. package/dist/monorepo/workspace.d.ts +142 -0
  99. package/dist/monorepo/workspace.d.ts.map +1 -0
  100. package/dist/monorepo/workspace.js +171 -0
  101. package/dist/output/envelope.d.ts +21 -0
  102. package/dist/output/envelope.d.ts.map +1 -0
  103. package/dist/output/envelope.js +27 -0
  104. package/dist/output/factory.d.ts +73 -0
  105. package/dist/output/factory.d.ts.map +1 -0
  106. package/dist/output/factory.js +60 -0
  107. package/dist/output/formats.d.ts +11 -0
  108. package/dist/output/formats.d.ts.map +1 -0
  109. package/dist/output/formats.js +41 -0
  110. package/dist/output/html.d.ts +45 -0
  111. package/dist/output/html.d.ts.map +1 -0
  112. package/dist/output/html.js +607 -0
  113. package/dist/output/human.d.ts +41 -0
  114. package/dist/output/human.d.ts.map +1 -0
  115. package/dist/output/human.js +274 -0
  116. package/dist/output/json.d.ts +42 -0
  117. package/dist/output/json.d.ts.map +1 -0
  118. package/dist/output/json.js +37 -0
  119. package/dist/output/junit.d.ts +56 -0
  120. package/dist/output/junit.d.ts.map +1 -0
  121. package/dist/output/junit.js +135 -0
  122. package/dist/output/markdown.d.ts +77 -0
  123. package/dist/output/markdown.d.ts.map +1 -0
  124. package/dist/output/markdown.js +411 -0
  125. package/dist/output/sarif.d.ts +160 -0
  126. package/dist/output/sarif.d.ts.map +1 -0
  127. package/dist/output/sarif.js +207 -0
  128. package/dist/policy/evaluator.d.ts +111 -0
  129. package/dist/policy/evaluator.d.ts.map +1 -0
  130. package/dist/policy/evaluator.js +362 -0
  131. package/dist/policy/index.d.ts +15 -0
  132. package/dist/policy/index.d.ts.map +1 -0
  133. package/dist/policy/index.js +11 -0
  134. package/dist/policy/loader.d.ts +97 -0
  135. package/dist/policy/loader.d.ts.map +1 -0
  136. package/dist/policy/loader.js +281 -0
  137. package/dist/policy/schema.d.ts +297 -0
  138. package/dist/policy/schema.d.ts.map +1 -0
  139. package/dist/policy/schema.js +230 -0
  140. package/dist/quality-gate/evaluator.d.ts +58 -0
  141. package/dist/quality-gate/evaluator.d.ts.map +1 -0
  142. package/dist/quality-gate/evaluator.js +274 -0
  143. package/dist/quality-gate/index.d.ts +10 -0
  144. package/dist/quality-gate/index.d.ts.map +1 -0
  145. package/dist/quality-gate/index.js +7 -0
  146. package/dist/quality-gate/types.d.ts +103 -0
  147. package/dist/quality-gate/types.d.ts.map +1 -0
  148. package/dist/quality-gate/types.js +23 -0
  149. package/dist/templates/azure-devops.d.ts +25 -0
  150. package/dist/templates/azure-devops.d.ts.map +1 -0
  151. package/dist/templates/azure-devops.js +109 -0
  152. package/dist/templates/circleci.d.ts +28 -0
  153. package/dist/templates/circleci.d.ts.map +1 -0
  154. package/dist/templates/circleci.js +86 -0
  155. package/dist/templates/github-actions.d.ts +81 -0
  156. package/dist/templates/github-actions.d.ts.map +1 -0
  157. package/dist/templates/github-actions.js +393 -0
  158. package/dist/templates/gitlab-ci.d.ts +26 -0
  159. package/dist/templates/gitlab-ci.d.ts.map +1 -0
  160. package/dist/templates/gitlab-ci.js +70 -0
  161. package/dist/templates/index.d.ts +72 -0
  162. package/dist/templates/index.d.ts.map +1 -0
  163. package/dist/templates/index.js +112 -0
  164. package/dist/templates/jenkins.d.ts +26 -0
  165. package/dist/templates/jenkins.d.ts.map +1 -0
  166. package/dist/templates/jenkins.js +110 -0
  167. package/dist/ui/banner.d.ts +31 -0
  168. package/dist/ui/banner.d.ts.map +1 -0
  169. package/dist/ui/banner.js +84 -0
  170. package/dist/ui/diagnostics.d.ts +39 -0
  171. package/dist/ui/diagnostics.d.ts.map +1 -0
  172. package/dist/ui/diagnostics.js +153 -0
  173. package/dist/ui/spinner.d.ts +61 -0
  174. package/dist/ui/spinner.d.ts.map +1 -0
  175. package/dist/ui/spinner.js +101 -0
  176. package/dist/ui/table.d.ts +63 -0
  177. package/dist/ui/table.d.ts.map +1 -0
  178. package/dist/ui/table.js +236 -0
  179. package/dist/utils/client.d.ts +82 -0
  180. package/dist/utils/client.d.ts.map +1 -0
  181. package/dist/utils/client.js +128 -0
  182. package/dist/utils/detect-env.d.ts +59 -0
  183. package/dist/utils/detect-env.d.ts.map +1 -0
  184. package/dist/utils/detect-env.js +115 -0
  185. package/dist/utils/exit-codes.d.ts +47 -0
  186. package/dist/utils/exit-codes.d.ts.map +1 -0
  187. package/dist/utils/exit-codes.js +61 -0
  188. package/dist/utils/logger.d.ts +87 -0
  189. package/dist/utils/logger.d.ts.map +1 -0
  190. package/dist/utils/logger.js +185 -0
  191. package/dist/utils/sanitize.d.ts +36 -0
  192. package/dist/utils/sanitize.d.ts.map +1 -0
  193. package/dist/utils/sanitize.js +64 -0
  194. package/dist/utils/validators.d.ts +41 -0
  195. package/dist/utils/validators.d.ts.map +1 -0
  196. package/dist/utils/validators.js +123 -0
  197. package/package.json +63 -0
  198. package/schemas/vertaaux.config.schema.json +103 -0
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Policy module for VertaaUX CLI.
3
+ *
4
+ * Provides policy-as-code support for organizations to define
5
+ * quality standards committed to the repository.
6
+ *
7
+ * Implements CICD-17: Policy-as-code support.
8
+ */
9
+ export { policyJsonSchema, DEFAULT_POLICY, POLICY_TEMPLATES, } from "./schema.js";
10
+ export { loadPolicy, loadPolicyFile, validatePolicy, resolveBranchPolicy, getEffectivePolicy, PolicyValidationError, PolicyLoadError, } from "./loader.js";
11
+ export { evaluatePolicy, applyRuleOverrides, filterExcludedPaths, formatPolicyResult, } from "./evaluator.js";
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Policy file loader and validator.
3
+ *
4
+ * Loads policy files from standard locations and validates
5
+ * against JSON Schema. Supports branch-specific overrides.
6
+ *
7
+ * Implements CICD-17: Policy-as-code support.
8
+ */
9
+ import type { PolicyFile } from "./schema.js";
10
+ /**
11
+ * Result of policy loading.
12
+ */
13
+ export interface PolicyLoadResult {
14
+ /** The loaded policy (or default if none found) */
15
+ policy: PolicyFile;
16
+ /** Path to the policy file (null if using defaults) */
17
+ path: string | null;
18
+ }
19
+ /**
20
+ * Result of policy validation.
21
+ */
22
+ export interface PolicyValidationResult {
23
+ /** Whether the policy is valid */
24
+ valid: boolean;
25
+ /** Validation error messages (empty if valid) */
26
+ errors: string[];
27
+ }
28
+ /**
29
+ * Error thrown when policy validation fails.
30
+ */
31
+ export declare class PolicyValidationError extends Error {
32
+ readonly errors: string[];
33
+ constructor(errors: string[], filePath?: string);
34
+ }
35
+ /**
36
+ * Error thrown when policy file cannot be loaded.
37
+ */
38
+ export declare class PolicyLoadError extends Error {
39
+ readonly cause?: unknown | undefined;
40
+ constructor(message: string, cause?: unknown | undefined);
41
+ }
42
+ /**
43
+ * Validate a policy object against the JSON Schema.
44
+ *
45
+ * @param policy - Policy object to validate
46
+ * @returns Validation result with errors if invalid
47
+ */
48
+ export declare function validatePolicy(policy: unknown): PolicyValidationResult;
49
+ /**
50
+ * Load policy file from a specific path.
51
+ *
52
+ * @param filePath - Path to policy file
53
+ * @returns Loaded and validated policy
54
+ * @throws PolicyLoadError if file cannot be read or parsed
55
+ * @throws PolicyValidationError if policy fails validation
56
+ */
57
+ export declare function loadPolicyFile(filePath: string): Promise<PolicyFile>;
58
+ /**
59
+ * Load policy file from project root.
60
+ *
61
+ * Searches for policy file in standard locations:
62
+ * 1. vertaa.policy.yml
63
+ * 2. vertaa.policy.yaml
64
+ * 3. .vertaa.policy.yml
65
+ * 4. .vertaaux/policy.yml
66
+ *
67
+ * Returns default policy if none found.
68
+ *
69
+ * @param projectRoot - Root directory to search from (defaults to cwd)
70
+ * @returns Loaded policy and path (or null path if using defaults)
71
+ */
72
+ export declare function loadPolicy(projectRoot?: string): Promise<PolicyLoadResult>;
73
+ /**
74
+ * Resolve policy with branch-specific overrides.
75
+ *
76
+ * Looks for matching branch in policy.branches and merges
77
+ * branch-specific assertions with base assertions.
78
+ *
79
+ * @param policy - Base policy
80
+ * @param branch - Current branch name
81
+ * @returns Policy with branch-specific overrides applied
82
+ */
83
+ export declare function resolveBranchPolicy(policy: PolicyFile, branch: string): PolicyFile;
84
+ /**
85
+ * Get the effective policy for the current context.
86
+ *
87
+ * Convenience function that loads policy and resolves branch overrides.
88
+ *
89
+ * @param options - Loading options
90
+ * @returns Effective policy for the current branch
91
+ */
92
+ export declare function getEffectivePolicy(options?: {
93
+ projectRoot?: string;
94
+ branch?: string;
95
+ policyPath?: string;
96
+ }): Promise<PolicyLoadResult>;
97
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/policy/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAkC,MAAM,aAAa,CAAC;AAa9E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,MAAM,EAAE,UAAU,CAAC;IACnB,uDAAuD;IACvD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,kCAAkC;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;aAE5B,MAAM,EAAE,MAAM,EAAE;gBAAhB,MAAM,EAAE,MAAM,EAAE,EAChC,QAAQ,CAAC,EAAE,MAAM;CAQpB;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,KAAK,CAAC,EAAE,OAAO;gBAD/B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC;AA+BD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,sBAAsB,CAWtE;AAoBD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB1E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8BhF;AAyED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,GACb,UAAU,CA+BZ;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAY5B"}
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Policy file loader and validator.
3
+ *
4
+ * Loads policy files from standard locations and validates
5
+ * against JSON Schema. Supports branch-specific overrides.
6
+ *
7
+ * Implements CICD-17: Policy-as-code support.
8
+ */
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import yaml from "yaml";
12
+ import Ajv from "ajv";
13
+ import { policyJsonSchema, DEFAULT_POLICY } from "./schema.js";
14
+ /**
15
+ * Standard policy file locations, searched in order.
16
+ */
17
+ const POLICY_FILES = [
18
+ "vertaa.policy.yml",
19
+ "vertaa.policy.yaml",
20
+ ".vertaa.policy.yml",
21
+ ".vertaaux/policy.yml",
22
+ ];
23
+ /**
24
+ * Error thrown when policy validation fails.
25
+ */
26
+ export class PolicyValidationError extends Error {
27
+ errors;
28
+ constructor(errors, filePath) {
29
+ const errorMessages = errors.map((e) => ` - ${e}`).join("\n");
30
+ super(`Invalid policy file${filePath ? ` (${filePath})` : ""}:\n${errorMessages}`);
31
+ this.errors = errors;
32
+ this.name = "PolicyValidationError";
33
+ }
34
+ }
35
+ /**
36
+ * Error thrown when policy file cannot be loaded.
37
+ */
38
+ export class PolicyLoadError extends Error {
39
+ cause;
40
+ constructor(message, cause) {
41
+ super(message);
42
+ this.cause = cause;
43
+ this.name = "PolicyLoadError";
44
+ }
45
+ }
46
+ // Create Ajv validator instance
47
+ const ajv = new Ajv({ strict: false, allErrors: true });
48
+ const validateSchema = ajv.compile(policyJsonSchema);
49
+ /**
50
+ * Format Ajv errors into human-readable messages.
51
+ */
52
+ function formatAjvErrors(errors) {
53
+ if (!errors)
54
+ return [];
55
+ return errors.map((e) => {
56
+ const path = e.instancePath || "root";
57
+ const message = e.message || "unknown error";
58
+ // Add more context for common errors
59
+ if (e.keyword === "enum") {
60
+ const allowed = e.params.allowedValues;
61
+ return `${path}: ${message}. Allowed values: ${allowed?.join(", ")}`;
62
+ }
63
+ if (e.keyword === "additionalProperties") {
64
+ const prop = e.params.additionalProperty;
65
+ return `${path}: unknown property '${prop}'`;
66
+ }
67
+ return `${path}: ${message}`;
68
+ });
69
+ }
70
+ /**
71
+ * Validate a policy object against the JSON Schema.
72
+ *
73
+ * @param policy - Policy object to validate
74
+ * @returns Validation result with errors if invalid
75
+ */
76
+ export function validatePolicy(policy) {
77
+ const valid = validateSchema(policy);
78
+ if (valid) {
79
+ return { valid: true, errors: [] };
80
+ }
81
+ return {
82
+ valid: false,
83
+ errors: formatAjvErrors(validateSchema.errors),
84
+ };
85
+ }
86
+ /**
87
+ * Parse YAML content into a policy object.
88
+ *
89
+ * @param content - YAML string content
90
+ * @param filePath - File path for error messages
91
+ * @returns Parsed policy object
92
+ */
93
+ function parseYaml(content, filePath) {
94
+ try {
95
+ return yaml.parse(content);
96
+ }
97
+ catch (error) {
98
+ throw new PolicyLoadError(`Failed to parse YAML in ${filePath}: ${error instanceof Error ? error.message : String(error)}`, error);
99
+ }
100
+ }
101
+ /**
102
+ * Load policy file from a specific path.
103
+ *
104
+ * @param filePath - Path to policy file
105
+ * @returns Loaded and validated policy
106
+ * @throws PolicyLoadError if file cannot be read or parsed
107
+ * @throws PolicyValidationError if policy fails validation
108
+ */
109
+ export async function loadPolicyFile(filePath) {
110
+ const absolutePath = path.resolve(process.cwd(), filePath);
111
+ // Check file exists
112
+ if (!fs.existsSync(absolutePath)) {
113
+ throw new PolicyLoadError(`Policy file not found: ${absolutePath}`);
114
+ }
115
+ // Read and parse
116
+ const content = fs.readFileSync(absolutePath, "utf-8");
117
+ const parsed = parseYaml(content, absolutePath);
118
+ // Validate
119
+ const validation = validatePolicy(parsed);
120
+ if (!validation.valid) {
121
+ throw new PolicyValidationError(validation.errors, absolutePath);
122
+ }
123
+ return parsed;
124
+ }
125
+ /**
126
+ * Load policy file from project root.
127
+ *
128
+ * Searches for policy file in standard locations:
129
+ * 1. vertaa.policy.yml
130
+ * 2. vertaa.policy.yaml
131
+ * 3. .vertaa.policy.yml
132
+ * 4. .vertaaux/policy.yml
133
+ *
134
+ * Returns default policy if none found.
135
+ *
136
+ * @param projectRoot - Root directory to search from (defaults to cwd)
137
+ * @returns Loaded policy and path (or null path if using defaults)
138
+ */
139
+ export async function loadPolicy(projectRoot) {
140
+ const root = projectRoot || process.cwd();
141
+ // Search for policy file in standard locations
142
+ for (const filename of POLICY_FILES) {
143
+ const filePath = path.join(root, filename);
144
+ if (fs.existsSync(filePath)) {
145
+ try {
146
+ const policy = await loadPolicyFile(filePath);
147
+ return { policy, path: filePath };
148
+ }
149
+ catch (error) {
150
+ // Re-throw validation and load errors
151
+ if (error instanceof PolicyValidationError ||
152
+ error instanceof PolicyLoadError) {
153
+ throw error;
154
+ }
155
+ // Wrap unexpected errors
156
+ throw new PolicyLoadError(`Failed to load policy from ${filePath}: ${error instanceof Error ? error.message : String(error)}`, error);
157
+ }
158
+ }
159
+ }
160
+ // No policy file found, return defaults
161
+ return { policy: { ...DEFAULT_POLICY }, path: null };
162
+ }
163
+ /**
164
+ * Check if a branch matches a pattern.
165
+ *
166
+ * Supports simple glob patterns:
167
+ * - Exact match: "main"
168
+ * - Wildcard suffix: "release/*"
169
+ * - Wildcard prefix: "* /hotfix"
170
+ * - Full match: "*"
171
+ *
172
+ * @param branch - Branch name to check
173
+ * @param pattern - Pattern to match against
174
+ * @returns Whether the branch matches the pattern
175
+ */
176
+ function matchBranch(branch, pattern) {
177
+ // Exact match
178
+ if (pattern === branch)
179
+ return true;
180
+ // Full wildcard
181
+ if (pattern === "*")
182
+ return true;
183
+ // Wildcard suffix: "release/*" matches "release/1.0"
184
+ if (pattern.endsWith("/*")) {
185
+ const prefix = pattern.slice(0, -2);
186
+ return branch.startsWith(prefix + "/");
187
+ }
188
+ // Wildcard prefix: "*/hotfix" matches "feature/hotfix"
189
+ if (pattern.startsWith("*/")) {
190
+ const suffix = pattern.slice(2);
191
+ return branch.endsWith("/" + suffix);
192
+ }
193
+ return false;
194
+ }
195
+ /**
196
+ * Deep merge two assertion objects.
197
+ *
198
+ * Override values take precedence over base values.
199
+ * Undefined values in override do not overwrite base values.
200
+ */
201
+ function mergeAssertions(base, override) {
202
+ return {
203
+ ...base,
204
+ // Only override defined values
205
+ ...(override.overall_score !== undefined && {
206
+ overall_score: override.overall_score,
207
+ }),
208
+ ...(override.accessibility_score !== undefined && {
209
+ accessibility_score: override.accessibility_score,
210
+ }),
211
+ ...(override.ux_score !== undefined && { ux_score: override.ux_score }),
212
+ ...(override.performance_score !== undefined && {
213
+ performance_score: override.performance_score,
214
+ }),
215
+ ...(override.fail_on !== undefined && { fail_on: override.fail_on }),
216
+ ...(override.max_new_errors !== undefined && {
217
+ max_new_errors: override.max_new_errors,
218
+ }),
219
+ ...(override.max_new_warnings !== undefined && {
220
+ max_new_warnings: override.max_new_warnings,
221
+ }),
222
+ ...(override.max_new_total !== undefined && {
223
+ max_new_total: override.max_new_total,
224
+ }),
225
+ };
226
+ }
227
+ /**
228
+ * Resolve policy with branch-specific overrides.
229
+ *
230
+ * Looks for matching branch in policy.branches and merges
231
+ * branch-specific assertions with base assertions.
232
+ *
233
+ * @param policy - Base policy
234
+ * @param branch - Current branch name
235
+ * @returns Policy with branch-specific overrides applied
236
+ */
237
+ export function resolveBranchPolicy(policy, branch) {
238
+ // No branches configured
239
+ if (!policy.branches || !branch) {
240
+ return policy;
241
+ }
242
+ // Find matching branch config
243
+ let matchedConfig;
244
+ let matchedKey;
245
+ for (const [key, config] of Object.entries(policy.branches)) {
246
+ // Use pattern if specified, otherwise use key as exact match
247
+ const pattern = config.pattern || key;
248
+ if (matchBranch(branch, pattern)) {
249
+ matchedConfig = config;
250
+ matchedKey = key;
251
+ break;
252
+ }
253
+ }
254
+ // No matching branch
255
+ if (!matchedConfig || !matchedConfig.assertions) {
256
+ return policy;
257
+ }
258
+ // Merge assertions
259
+ return {
260
+ ...policy,
261
+ assertions: mergeAssertions(policy.assertions, matchedConfig.assertions),
262
+ };
263
+ }
264
+ /**
265
+ * Get the effective policy for the current context.
266
+ *
267
+ * Convenience function that loads policy and resolves branch overrides.
268
+ *
269
+ * @param options - Loading options
270
+ * @returns Effective policy for the current branch
271
+ */
272
+ export async function getEffectivePolicy(options) {
273
+ const { projectRoot, branch, policyPath } = options || {};
274
+ // Load from specific path or search
275
+ const { policy, path: foundPath } = policyPath
276
+ ? { policy: await loadPolicyFile(policyPath), path: policyPath }
277
+ : await loadPolicy(projectRoot);
278
+ // Resolve branch overrides if branch is known
279
+ const resolved = branch ? resolveBranchPolicy(policy, branch) : policy;
280
+ return { policy: resolved, path: foundPath };
281
+ }
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Policy file schema for vertaa.policy.yml.
3
+ *
4
+ * Enables policy-as-code for organizations to define:
5
+ * - Quality thresholds and assertions
6
+ * - Rule severity overrides
7
+ * - Branch-specific policies
8
+ * - Bypass labels for emergency deploys
9
+ *
10
+ * Modeled on Semgrep/ESLint policy patterns.
11
+ *
12
+ * Implements CICD-17: Policy-as-code support.
13
+ */
14
+ /**
15
+ * Policy file structure for vertaa.policy.yml.
16
+ */
17
+ export interface PolicyFile {
18
+ /** JSON Schema reference for IDE support */
19
+ $schema?: string;
20
+ /** Policy format version */
21
+ version: 1;
22
+ /** Human-readable policy name */
23
+ name?: string;
24
+ /** Policy description */
25
+ description?: string;
26
+ /** Threshold assertions - fail if not met */
27
+ assertions: PolicyAssertions;
28
+ /** Rule-specific customizations */
29
+ rules?: Record<string, RuleOverride>;
30
+ /** Branch-specific policy overrides */
31
+ branches?: Record<string, BranchPolicy>;
32
+ /** Labels that bypass quality gate */
33
+ bypass_labels?: string[];
34
+ /** Paths to exclude from auditing */
35
+ exclude_paths?: string[];
36
+ /** Required minimum CLI version */
37
+ required_version?: string;
38
+ }
39
+ /**
40
+ * Policy assertions that must pass for the gate to pass.
41
+ */
42
+ export interface PolicyAssertions {
43
+ /** Minimum overall score (0-100) */
44
+ overall_score?: number;
45
+ /** Minimum accessibility score */
46
+ accessibility_score?: number;
47
+ /** Minimum UX score */
48
+ ux_score?: number;
49
+ /** Minimum performance score */
50
+ performance_score?: number;
51
+ /** Fail on issues at or above this severity */
52
+ fail_on?: "error" | "warning" | "info";
53
+ /** Maximum new error-severity issues allowed */
54
+ max_new_errors?: number;
55
+ /** Maximum new warning-severity issues allowed */
56
+ max_new_warnings?: number;
57
+ /** Maximum total new issues allowed */
58
+ max_new_total?: number;
59
+ }
60
+ /**
61
+ * Rule-specific override configuration.
62
+ */
63
+ export interface RuleOverride {
64
+ /** Override severity: error, warning, info, or ignore */
65
+ severity: "error" | "warning" | "info" | "ignore";
66
+ /** Reason for override (for audit trail) */
67
+ reason?: string;
68
+ /** Override only applies to these paths (glob patterns) */
69
+ paths?: string[];
70
+ }
71
+ /**
72
+ * Branch-specific policy configuration.
73
+ */
74
+ export interface BranchPolicy {
75
+ /** Branch pattern (glob) - if not specified, uses the branch name as exact match */
76
+ pattern?: string;
77
+ /** Override assertions for this branch */
78
+ assertions?: Partial<PolicyAssertions>;
79
+ }
80
+ /**
81
+ * JSON Schema for vertaa.policy.yml validation.
82
+ *
83
+ * Used for:
84
+ * - Runtime validation with Ajv
85
+ * - IDE autocompletion and validation
86
+ * - Documentation generation
87
+ */
88
+ export declare const policyJsonSchema: {
89
+ $schema: string;
90
+ $id: string;
91
+ title: string;
92
+ description: string;
93
+ type: string;
94
+ properties: {
95
+ $schema: {
96
+ type: string;
97
+ description: string;
98
+ };
99
+ version: {
100
+ type: string;
101
+ enum: number[];
102
+ description: string;
103
+ };
104
+ name: {
105
+ type: string;
106
+ description: string;
107
+ };
108
+ description: {
109
+ type: string;
110
+ description: string;
111
+ };
112
+ assertions: {
113
+ type: string;
114
+ description: string;
115
+ properties: {
116
+ overall_score: {
117
+ type: string;
118
+ minimum: number;
119
+ maximum: number;
120
+ description: string;
121
+ };
122
+ accessibility_score: {
123
+ type: string;
124
+ minimum: number;
125
+ maximum: number;
126
+ description: string;
127
+ };
128
+ ux_score: {
129
+ type: string;
130
+ minimum: number;
131
+ maximum: number;
132
+ description: string;
133
+ };
134
+ performance_score: {
135
+ type: string;
136
+ minimum: number;
137
+ maximum: number;
138
+ description: string;
139
+ };
140
+ fail_on: {
141
+ type: string;
142
+ enum: string[];
143
+ description: string;
144
+ };
145
+ max_new_errors: {
146
+ type: string;
147
+ minimum: number;
148
+ description: string;
149
+ };
150
+ max_new_warnings: {
151
+ type: string;
152
+ minimum: number;
153
+ description: string;
154
+ };
155
+ max_new_total: {
156
+ type: string;
157
+ minimum: number;
158
+ description: string;
159
+ };
160
+ };
161
+ additionalProperties: boolean;
162
+ };
163
+ rules: {
164
+ type: string;
165
+ description: string;
166
+ additionalProperties: {
167
+ type: string;
168
+ properties: {
169
+ severity: {
170
+ type: string;
171
+ enum: string[];
172
+ description: string;
173
+ };
174
+ reason: {
175
+ type: string;
176
+ description: string;
177
+ };
178
+ paths: {
179
+ type: string;
180
+ items: {
181
+ type: string;
182
+ };
183
+ description: string;
184
+ };
185
+ };
186
+ required: string[];
187
+ additionalProperties: boolean;
188
+ };
189
+ };
190
+ branches: {
191
+ type: string;
192
+ description: string;
193
+ additionalProperties: {
194
+ type: string;
195
+ properties: {
196
+ pattern: {
197
+ type: string;
198
+ description: string;
199
+ };
200
+ assertions: {
201
+ $ref: string;
202
+ description: string;
203
+ };
204
+ };
205
+ additionalProperties: boolean;
206
+ };
207
+ };
208
+ bypass_labels: {
209
+ type: string;
210
+ items: {
211
+ type: string;
212
+ };
213
+ description: string;
214
+ };
215
+ exclude_paths: {
216
+ type: string;
217
+ items: {
218
+ type: string;
219
+ };
220
+ description: string;
221
+ };
222
+ required_version: {
223
+ type: string;
224
+ description: string;
225
+ };
226
+ };
227
+ required: string[];
228
+ additionalProperties: boolean;
229
+ };
230
+ /**
231
+ * Default policy with sensible enterprise defaults.
232
+ *
233
+ * - Only fail on errors (not warnings/info)
234
+ * - Only fail on NEW errors (existing debt allowed)
235
+ * - Common emergency bypass labels
236
+ */
237
+ export declare const DEFAULT_POLICY: PolicyFile;
238
+ /**
239
+ * Policy templates for different strictness levels.
240
+ */
241
+ export declare const POLICY_TEMPLATES: {
242
+ /**
243
+ * Basic template - fail only on new errors.
244
+ */
245
+ basic: {
246
+ version: 1;
247
+ name: string;
248
+ description: string;
249
+ assertions: {
250
+ fail_on: "error";
251
+ max_new_errors: number;
252
+ };
253
+ bypass_labels: string[];
254
+ };
255
+ /**
256
+ * Strict template - fail on warnings, require minimum scores.
257
+ */
258
+ strict: {
259
+ version: 1;
260
+ name: string;
261
+ description: string;
262
+ assertions: {
263
+ fail_on: "warning";
264
+ overall_score: number;
265
+ max_new_errors: number;
266
+ max_new_warnings: number;
267
+ };
268
+ branches: {
269
+ main: {
270
+ assertions: {
271
+ overall_score: number;
272
+ };
273
+ };
274
+ "release/*": {
275
+ pattern: string;
276
+ assertions: {
277
+ overall_score: number;
278
+ };
279
+ };
280
+ };
281
+ bypass_labels: string[];
282
+ };
283
+ /**
284
+ * Lenient template - fail only on critical errors.
285
+ */
286
+ lenient: {
287
+ version: 1;
288
+ name: string;
289
+ description: string;
290
+ assertions: {
291
+ fail_on: "error";
292
+ };
293
+ bypass_labels: string[];
294
+ };
295
+ };
296
+ export type PolicyTemplate = keyof typeof POLICY_TEMPLATES;
297
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/policy/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,4BAA4B;IAC5B,OAAO,EAAE,CAAC,CAAC;IAEX,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,6CAA6C;IAC7C,UAAU,EAAE,gBAAgB,CAAC;IAE7B,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAErC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAExC,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB,qCAAqC;IACrC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,kCAAkC;IAClC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B,uBAAuB;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,gCAAgC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IAEvC,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;IAElD,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACxC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0I5B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,UAO5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB;IAC3B;;OAEG;;;;;;;;;;;IAYH;;OAEG;;;;;;;;;;;;;;;;;;;;;;;;;;IA4BH;;OAEG;;;;;;;;;;CAUiC,CAAC;AAEvC,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,gBAAgB,CAAC"}