@standards-kit/conform 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 (71) hide show
  1. package/dist/artifactregistry-QQWBMEQN.js +38 -0
  2. package/dist/artifactregistry-QQWBMEQN.js.map +1 -0
  3. package/dist/chunk-J5S6GRGW.js +314 -0
  4. package/dist/chunk-J5S6GRGW.js.map +1 -0
  5. package/dist/chunk-KHO6NIAI.js +1367 -0
  6. package/dist/chunk-KHO6NIAI.js.map +1 -0
  7. package/dist/chunk-M7G73Q6P.js +662 -0
  8. package/dist/chunk-M7G73Q6P.js.map +1 -0
  9. package/dist/chunk-P7TIZJ4C.js +85 -0
  10. package/dist/chunk-P7TIZJ4C.js.map +1 -0
  11. package/dist/chunk-RXA4FO7L.js +279 -0
  12. package/dist/chunk-RXA4FO7L.js.map +1 -0
  13. package/dist/cli.js +7432 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/cloudrun-O36R23SH.js +31 -0
  16. package/dist/cloudrun-O36R23SH.js.map +1 -0
  17. package/dist/cloudwatch-KSZ4A256.js +56 -0
  18. package/dist/cloudwatch-KSZ4A256.js.map +1 -0
  19. package/dist/dynamodb-5KVESCVJ.js +51 -0
  20. package/dist/dynamodb-5KVESCVJ.js.map +1 -0
  21. package/dist/ec2-HKPE6GZV.js +151 -0
  22. package/dist/ec2-HKPE6GZV.js.map +1 -0
  23. package/dist/ecs-OS3NJZTA.js +141 -0
  24. package/dist/ecs-OS3NJZTA.js.map +1 -0
  25. package/dist/elasticache-7TCRHYYM.js +151 -0
  26. package/dist/elasticache-7TCRHYYM.js.map +1 -0
  27. package/dist/elb-PEDLXW5R.js +151 -0
  28. package/dist/elb-PEDLXW5R.js.map +1 -0
  29. package/dist/generate-D4MFMOHP.js +28 -0
  30. package/dist/generate-D4MFMOHP.js.map +1 -0
  31. package/dist/iam-7H5HFWVQ.js +96 -0
  32. package/dist/iam-7H5HFWVQ.js.map +1 -0
  33. package/dist/iam-DJI64AGK.js +39 -0
  34. package/dist/iam-DJI64AGK.js.map +1 -0
  35. package/dist/index.js +7980 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/infra-UXM5XQX3.js +566 -0
  38. package/dist/infra-UXM5XQX3.js.map +1 -0
  39. package/dist/lambda-NFB5UILT.js +60 -0
  40. package/dist/lambda-NFB5UILT.js.map +1 -0
  41. package/dist/manifest-7AIL2FK2.js +23 -0
  42. package/dist/manifest-7AIL2FK2.js.map +1 -0
  43. package/dist/mcp-O5O7XVFG.js +204 -0
  44. package/dist/mcp-O5O7XVFG.js.map +1 -0
  45. package/dist/rds-KLG5O5SI.js +151 -0
  46. package/dist/rds-KLG5O5SI.js.map +1 -0
  47. package/dist/registry-V65CC7IN.js +15 -0
  48. package/dist/registry-V65CC7IN.js.map +1 -0
  49. package/dist/s3-2DH7PRVR.js +49 -0
  50. package/dist/s3-2DH7PRVR.js.map +1 -0
  51. package/dist/scan-EELS42BP.js +593 -0
  52. package/dist/scan-EELS42BP.js.map +1 -0
  53. package/dist/secretmanager-RDL62EFW.js +31 -0
  54. package/dist/secretmanager-RDL62EFW.js.map +1 -0
  55. package/dist/secretsmanager-MOOIHLAO.js +50 -0
  56. package/dist/secretsmanager-MOOIHLAO.js.map +1 -0
  57. package/dist/sns-Y36LVTWA.js +50 -0
  58. package/dist/sns-Y36LVTWA.js.map +1 -0
  59. package/dist/sqs-RRS3GRHK.js +61 -0
  60. package/dist/sqs-RRS3GRHK.js.map +1 -0
  61. package/dist/src-KZRTG3EU.js +45 -0
  62. package/dist/src-KZRTG3EU.js.map +1 -0
  63. package/dist/standards-RXK5G4IG.js +37 -0
  64. package/dist/standards-RXK5G4IG.js.map +1 -0
  65. package/dist/sync-RLYBGYNY.js +877 -0
  66. package/dist/sync-RLYBGYNY.js.map +1 -0
  67. package/dist/validate-AABLVQJS.js +327 -0
  68. package/dist/validate-AABLVQJS.js.map +1 -0
  69. package/dist/validator-6PL5I5EC.js +156 -0
  70. package/dist/validator-6PL5I5EC.js.map +1 -0
  71. package/package.json +91 -0
@@ -0,0 +1,1367 @@
1
+ // ../core/src/registry.ts
2
+ import * as fs2 from "fs";
3
+ import * as os from "os";
4
+ import * as path2 from "path";
5
+ import * as toml from "@iarna/toml";
6
+ import { execa } from "execa";
7
+
8
+ // ../core/src/loader.ts
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ import TOML from "@iarna/toml";
12
+
13
+ // ../core/src/schema.ts
14
+ import { minimatch } from "minimatch";
15
+ import { z } from "zod";
16
+ function countUnclosedDelimiters(pattern) {
17
+ let brackets = 0;
18
+ let braces = 0;
19
+ for (let i = 0; i < pattern.length; i++) {
20
+ if (pattern[i] === "\\" && i + 1 < pattern.length) {
21
+ i++;
22
+ continue;
23
+ }
24
+ switch (pattern[i]) {
25
+ case "[":
26
+ brackets++;
27
+ break;
28
+ case "]":
29
+ if (brackets > 0) {
30
+ brackets--;
31
+ }
32
+ break;
33
+ case "{":
34
+ braces++;
35
+ break;
36
+ case "}":
37
+ if (braces > 0) {
38
+ braces--;
39
+ }
40
+ break;
41
+ }
42
+ }
43
+ return { brackets, braces };
44
+ }
45
+ function isValidGlobPattern(pattern) {
46
+ if (pattern.length === 0) {
47
+ return { valid: false, error: "empty pattern" };
48
+ }
49
+ const unclosed = countUnclosedDelimiters(pattern);
50
+ if (unclosed.brackets > 0) {
51
+ return { valid: false, error: "unclosed bracket '['" };
52
+ }
53
+ if (unclosed.braces > 0) {
54
+ return { valid: false, error: "unclosed brace '{'" };
55
+ }
56
+ try {
57
+ const result = minimatch.makeRe(pattern);
58
+ return result === false ? { valid: false, error: "invalid pattern syntax" } : { valid: true };
59
+ } catch (error) {
60
+ const message = error instanceof Error ? error.message : "Invalid glob pattern";
61
+ return { valid: false, error: message };
62
+ }
63
+ }
64
+ var globPatternSchema = z.string().refine(
65
+ (pattern) => isValidGlobPattern(pattern).valid,
66
+ (pattern) => ({
67
+ message: `Invalid glob pattern: "${pattern}" - ${isValidGlobPattern(pattern).error}`
68
+ })
69
+ );
70
+ var eslintRuleSeverity = z.enum(["off", "warn", "error"]);
71
+ var eslintRuleWithOptions = z.object({
72
+ severity: eslintRuleSeverity
73
+ }).catchall(z.unknown());
74
+ var eslintRuleValue = z.union([eslintRuleSeverity, eslintRuleWithOptions]);
75
+ var eslintRulesSchema = z.record(z.string(), eslintRuleValue).optional();
76
+ var eslintConfigSchema = z.object({
77
+ enabled: z.boolean().optional().default(true),
78
+ files: z.array(z.string()).optional(),
79
+ // Glob patterns for files to lint
80
+ ignore: z.array(z.string()).optional(),
81
+ // Glob patterns to ignore
82
+ "max-warnings": z.number().int().nonnegative().optional(),
83
+ // Max warnings before failure
84
+ rules: eslintRulesSchema,
85
+ // Required rules for audit (verifies eslint.config.js)
86
+ dependencies: z.array(z.string()).optional()
87
+ // Custom dependency files for drift tracking
88
+ }).strict().optional();
89
+ var ruffLintSchema = z.object({
90
+ select: z.array(z.string()).optional(),
91
+ ignore: z.array(z.string()).optional()
92
+ }).strict().optional();
93
+ var ruffConfigSchema = z.object({
94
+ enabled: z.boolean().optional().default(true),
95
+ "line-length": z.number().int().positive().optional(),
96
+ lint: ruffLintSchema,
97
+ dependencies: z.array(z.string()).optional()
98
+ // Custom dependency files for drift tracking
99
+ }).strict().optional();
100
+ var tscCompilerOptionsSchema = z.object({
101
+ strict: z.boolean().optional(),
102
+ noImplicitAny: z.boolean().optional(),
103
+ strictNullChecks: z.boolean().optional(),
104
+ noUnusedLocals: z.boolean().optional(),
105
+ noUnusedParameters: z.boolean().optional(),
106
+ noImplicitReturns: z.boolean().optional(),
107
+ noFallthroughCasesInSwitch: z.boolean().optional(),
108
+ esModuleInterop: z.boolean().optional(),
109
+ skipLibCheck: z.boolean().optional(),
110
+ forceConsistentCasingInFileNames: z.boolean().optional()
111
+ }).strict().optional();
112
+ var tscConfigSchema = z.object({
113
+ enabled: z.boolean().optional().default(false),
114
+ require: tscCompilerOptionsSchema,
115
+ // Required compiler options for audit
116
+ dependencies: z.array(z.string()).optional()
117
+ // Custom dependency files for drift tracking
118
+ }).strict().optional();
119
+ var tyConfigSchema = z.object({
120
+ enabled: z.boolean().optional().default(false),
121
+ dependencies: z.array(z.string()).optional()
122
+ // Custom dependency files for drift tracking
123
+ }).strict().optional();
124
+ var knipConfigSchema = z.object({
125
+ enabled: z.boolean().optional().default(false),
126
+ dependencies: z.array(z.string()).optional()
127
+ // Custom dependency files for drift tracking
128
+ }).strict().optional();
129
+ var vultureConfigSchema = z.object({
130
+ enabled: z.boolean().optional().default(false),
131
+ dependencies: z.array(z.string()).optional()
132
+ // Custom dependency files for drift tracking
133
+ }).strict().optional();
134
+ var coverageRunnerSchema = z.enum(["vitest", "jest", "pytest", "auto"]);
135
+ var coverageRunConfigSchema = z.object({
136
+ enabled: z.boolean().optional().default(false),
137
+ min_threshold: z.number().int().min(0).max(100).optional().default(80),
138
+ // Minimum coverage percentage
139
+ runner: coverageRunnerSchema.optional().default("auto"),
140
+ // Test runner to use
141
+ command: z.string().optional(),
142
+ // Custom command to run tests with coverage
143
+ dependencies: z.array(z.string()).optional()
144
+ // Custom dependency files for drift tracking
145
+ }).strict().optional();
146
+ var secretsConfigSchema = z.object({
147
+ enabled: z.boolean().optional().default(false),
148
+ scan_mode: z.enum(["branch", "files", "staged", "full"]).optional().default("branch"),
149
+ // branch: scan current branch commits, files: scan filesystem, staged: staged files only, full: entire git history
150
+ base_branch: z.string().optional().default("main"),
151
+ // Branch to compare against for "branch" mode
152
+ dependencies: z.array(z.string()).optional()
153
+ // Custom dependency files for drift tracking
154
+ }).strict().optional();
155
+ var pnpmauditConfigSchema = z.object({
156
+ enabled: z.boolean().optional().default(false),
157
+ exclude_dev: z.boolean().optional().default(true),
158
+ dependencies: z.array(z.string()).optional()
159
+ // Custom dependency files for drift tracking
160
+ }).strict().optional();
161
+ var pipauditConfigSchema = z.object({
162
+ enabled: z.boolean().optional().default(false),
163
+ dependencies: z.array(z.string()).optional()
164
+ // Custom dependency files for drift tracking
165
+ }).strict().optional();
166
+ var codeSecuritySchema = z.object({
167
+ secrets: secretsConfigSchema,
168
+ pnpmaudit: pnpmauditConfigSchema,
169
+ pipaudit: pipauditConfigSchema
170
+ }).strict().optional();
171
+ var caseTypeSchema = z.enum(["kebab-case", "snake_case", "camelCase", "PascalCase"]);
172
+ var uniqueArraySchema = (schema) => z.array(schema).refine((arr) => new Set(arr).size === arr.length, {
173
+ message: "Duplicate values not allowed"
174
+ });
175
+ var uniqueArraySchemaMin1 = (schema) => z.array(schema).min(1, "At least one value is required").refine((arr) => new Set(arr).size === arr.length, {
176
+ message: "Duplicate values not allowed"
177
+ });
178
+ var namingRuleSchema = z.object({
179
+ extensions: uniqueArraySchemaMin1(z.string()),
180
+ // e.g., ["ts", "tsx"] - no duplicates allowed, at least one required
181
+ file_case: caseTypeSchema,
182
+ folder_case: caseTypeSchema,
183
+ exclude: z.array(z.string()).optional(),
184
+ // Glob patterns to exclude, e.g., ["tests/**"]
185
+ allow_dynamic_routes: z.boolean().optional()
186
+ // Allow Next.js/Remix dynamic route folders: [id], [...slug], (group)
187
+ }).strict();
188
+ var namingConfigSchema = z.object({
189
+ enabled: z.boolean().optional().default(false),
190
+ rules: z.array(namingRuleSchema).optional()
191
+ }).strict().superRefine((data, ctx) => {
192
+ if (!data.rules || data.rules.length <= 1) {
193
+ return;
194
+ }
195
+ const extensionToRuleIndex = /* @__PURE__ */ new Map();
196
+ for (let i = 0; i < data.rules.length; i++) {
197
+ for (const ext of data.rules[i].extensions) {
198
+ const existing = extensionToRuleIndex.get(ext);
199
+ if (existing !== void 0) {
200
+ ctx.addIssue({
201
+ code: z.ZodIssueCode.custom,
202
+ message: `Extension "${ext}" appears in multiple naming rules (rules ${existing + 1} and ${i + 1}). Each extension can only appear in one rule.`,
203
+ path: ["rules", i, "extensions"]
204
+ });
205
+ } else {
206
+ extensionToRuleIndex.set(ext, i);
207
+ }
208
+ }
209
+ }
210
+ }).optional();
211
+ var disableCommentsConfigSchema = z.object({
212
+ enabled: z.boolean().optional().default(false),
213
+ patterns: z.array(z.string()).optional(),
214
+ // Override default patterns
215
+ extensions: uniqueArraySchema(z.string()).optional(),
216
+ // File extensions to scan - no duplicates allowed
217
+ exclude: z.array(z.string()).optional()
218
+ // Glob patterns to exclude
219
+ }).strict().optional();
220
+ var codeQualitySchema = z.object({
221
+ "disable-comments": disableCommentsConfigSchema
222
+ }).strict().optional();
223
+ var codeLintingSchema = z.object({
224
+ eslint: eslintConfigSchema,
225
+ ruff: ruffConfigSchema
226
+ }).strict().optional();
227
+ var codeTypesSchema = z.object({
228
+ tsc: tscConfigSchema,
229
+ ty: tyConfigSchema
230
+ }).strict().optional();
231
+ var codeUnusedSchema = z.object({
232
+ knip: knipConfigSchema,
233
+ vulture: vultureConfigSchema
234
+ }).strict().optional();
235
+ var codeSchema = z.object({
236
+ linting: codeLintingSchema,
237
+ types: codeTypesSchema,
238
+ unused: codeUnusedSchema,
239
+ coverage_run: coverageRunConfigSchema,
240
+ security: codeSecuritySchema,
241
+ naming: namingConfigSchema,
242
+ quality: codeQualitySchema
243
+ }).strict().optional();
244
+ var hookCommandsSchema = z.record(z.string(), z.array(z.string())).optional();
245
+ var hooksConfigSchema = z.object({
246
+ enabled: z.boolean().optional().default(false),
247
+ require_husky: z.boolean().optional().default(true),
248
+ // Require .husky/ directory
249
+ require_hooks: z.array(z.string()).optional(),
250
+ // e.g., ["pre-commit", "pre-push"]
251
+ commands: hookCommandsSchema,
252
+ // e.g., { "pre-commit": ["lint-staged"] }
253
+ protected_branches: z.array(z.string()).optional()
254
+ // e.g., ["main", "master"] - verify pre-push prevents direct pushes
255
+ }).strict().optional();
256
+ var ciCommandsValueSchema = z.union([
257
+ z.array(z.string()),
258
+ // Workflow-level: ["cmd1", "cmd2"]
259
+ z.record(z.string(), z.array(z.string()))
260
+ // Job-level: { jobName: ["cmd1"] }
261
+ ]);
262
+ var ciCommandsSchema = z.record(z.string(), ciCommandsValueSchema).optional();
263
+ var ciConfigSchema = z.object({
264
+ enabled: z.boolean().optional().default(false),
265
+ require_workflows: z.array(z.string()).optional(),
266
+ // e.g., ["ci.yml", "release.yml"]
267
+ jobs: z.record(z.string(), z.array(z.string())).optional(),
268
+ // e.g., { "ci.yml": ["test", "lint"] }
269
+ actions: z.record(z.string(), z.array(z.string())).optional(),
270
+ // e.g., { "ci.yml": ["actions/checkout"] }
271
+ commands: ciCommandsSchema
272
+ // e.g., { "ci.yml": ["conform code check"] } or { "ci.yml": { "test": ["npm test"] } }
273
+ }).strict().optional();
274
+ var branchesConfigSchema = z.object({
275
+ enabled: z.boolean().optional().default(false),
276
+ pattern: z.string().optional(),
277
+ // Regex pattern for branch names
278
+ exclude: z.array(z.string()).optional(),
279
+ // Branches to skip (e.g., ["main", "master"])
280
+ require_issue: z.boolean().optional().default(false),
281
+ // Require issue number in branch name
282
+ issue_pattern: z.string().optional()
283
+ // Regex to extract issue number (default: captures number after type/)
284
+ }).strict().optional();
285
+ var commitsConfigSchema = z.object({
286
+ enabled: z.boolean().optional().default(false),
287
+ pattern: z.string().optional(),
288
+ // Regex pattern for commit messages (e.g., conventional commits)
289
+ types: z.array(z.string()).optional(),
290
+ // Allowed commit types (e.g., ["feat", "fix", "chore"])
291
+ require_scope: z.boolean().optional().default(false),
292
+ // Require scope like feat(api): ...
293
+ max_subject_length: z.number().int().positive().optional()
294
+ // Max length of subject line
295
+ }).strict().optional();
296
+ var changesetBumpTypeSchema = z.enum(["patch", "minor", "major"]);
297
+ var changesetsConfigSchema = z.object({
298
+ enabled: z.boolean().optional().default(false),
299
+ require_for_paths: z.array(z.string()).optional(),
300
+ // Glob patterns that require changesets (e.g., ["src/**"])
301
+ exclude_paths: z.array(z.string()).optional(),
302
+ // Paths that don't require changesets (e.g., ["**/*.test.ts"])
303
+ validate_format: z.boolean().optional().default(true),
304
+ // Validate changeset file format (frontmatter, description)
305
+ allowed_bump_types: z.array(changesetBumpTypeSchema).optional(),
306
+ // Restrict allowed bump types (e.g., ["patch", "minor"])
307
+ require_description: z.boolean().optional().default(true),
308
+ // Require non-empty description
309
+ min_description_length: z.number().int().positive().optional()
310
+ // Minimum description length
311
+ }).strict().optional();
312
+ var prConfigSchema = z.object({
313
+ enabled: z.boolean().optional().default(false),
314
+ max_files: z.number().int().positive().optional(),
315
+ // Max files changed in PR
316
+ max_lines: z.number().int().positive().optional(),
317
+ // Max lines changed (additions + deletions)
318
+ require_issue: z.boolean().optional().default(false),
319
+ // Require issue reference in PR description
320
+ issue_keywords: z.array(z.string()).optional(),
321
+ // Keywords that link to issues (e.g., ["Closes", "Fixes", "Resolves"])
322
+ exclude: z.array(globPatternSchema).optional()
323
+ // Glob patterns to exclude from size calculation (e.g., ["*.lock", "**/*.snap"])
324
+ }).strict().optional();
325
+ var ticketsConfigSchema = z.object({
326
+ enabled: z.boolean().optional().default(false),
327
+ pattern: z.string().optional(),
328
+ // Regex pattern for ticket IDs (e.g., "^(ABC|XYZ)-[0-9]+")
329
+ require_in_commits: z.boolean().optional().default(true),
330
+ // Require ticket in commit messages
331
+ require_in_branch: z.boolean().optional().default(false)
332
+ // Require ticket in branch name
333
+ }).strict().optional();
334
+ var coverageEnforceInSchema = z.enum(["ci", "config", "both"]);
335
+ var coverageConfigSchema = z.object({
336
+ enabled: z.boolean().optional().default(false),
337
+ min_threshold: z.number().int().min(0).max(100).optional(),
338
+ // Minimum coverage percentage
339
+ enforce_in: coverageEnforceInSchema.optional().default("config"),
340
+ // Where to verify coverage
341
+ ci_workflow: z.string().optional(),
342
+ // Workflow file to check (e.g., "ci.yml")
343
+ ci_job: z.string().optional()
344
+ // Job name to check (e.g., "test")
345
+ }).strict().optional();
346
+ var bypassActorTypeSchema = z.enum([
347
+ "Integration",
348
+ // GitHub App
349
+ "OrganizationAdmin",
350
+ // Org admin role
351
+ "RepositoryRole",
352
+ // Repository role (1=read, 2=triage, 3=write, 4=maintain, 5=admin)
353
+ "Team",
354
+ // GitHub team
355
+ "DeployKey"
356
+ // Deploy key
357
+ ]);
358
+ var bypassModeSchema = z.enum([
359
+ "always",
360
+ // Can always bypass
361
+ "pull_request"
362
+ // Can bypass only via pull request
363
+ ]);
364
+ var bypassActorSchema = z.object({
365
+ actor_type: bypassActorTypeSchema,
366
+ actor_id: z.number().int().positive().optional(),
367
+ // Actor ID (required except for DeployKey)
368
+ bypass_mode: bypassModeSchema.optional().default("always")
369
+ }).strict();
370
+ var rulesetConfigSchema = z.object({
371
+ name: z.string().optional().default("Branch Protection"),
372
+ // Ruleset name in GitHub
373
+ branch: z.string().optional().default("main"),
374
+ // Branch to check (default: main)
375
+ enforcement: z.enum(["active", "evaluate", "disabled"]).optional().default("active"),
376
+ // Ruleset enforcement
377
+ required_reviews: z.number().int().min(0).optional(),
378
+ // Minimum required reviews
379
+ dismiss_stale_reviews: z.boolean().optional(),
380
+ // Dismiss stale reviews on new commits
381
+ require_code_owner_reviews: z.boolean().optional(),
382
+ // Require CODEOWNER review
383
+ require_status_checks: z.array(z.string()).optional(),
384
+ // Required status checks
385
+ require_branches_up_to_date: z.boolean().optional(),
386
+ // Require branch to be up to date
387
+ require_signed_commits: z.boolean().optional(),
388
+ // Require signed commits
389
+ enforce_admins: z.boolean().optional(),
390
+ // Enforce rules for admins (no bypass actors when true)
391
+ bypass_actors: z.array(bypassActorSchema).optional()
392
+ // Actors that can bypass rules
393
+ }).strict().optional();
394
+ var tagProtectionConfigSchema = z.object({
395
+ patterns: z.array(z.string()).optional(),
396
+ // Tag patterns to protect (e.g., ["v*"])
397
+ prevent_deletion: z.boolean().optional().default(true),
398
+ // Prevent tag deletion
399
+ prevent_update: z.boolean().optional().default(true)
400
+ // Prevent tag updates (force-push)
401
+ }).strict().optional();
402
+ var repoConfigSchema = z.object({
403
+ enabled: z.boolean().optional().default(false),
404
+ require_branch_protection: z.boolean().optional().default(false),
405
+ // Check branch protection exists
406
+ require_codeowners: z.boolean().optional().default(false),
407
+ // Check CODEOWNERS file exists
408
+ ruleset: rulesetConfigSchema,
409
+ // GitHub Ruleset configuration
410
+ tag_protection: tagProtectionConfigSchema
411
+ // Tag protection via GitHub rulesets
412
+ }).strict().optional();
413
+ var backupsConfigSchema = z.object({
414
+ enabled: z.boolean().optional().default(false),
415
+ bucket: z.string().optional(),
416
+ // S3 bucket name
417
+ prefix: z.string().optional(),
418
+ // S3 key prefix
419
+ max_age_hours: z.number().int().positive().optional().default(24),
420
+ // Max age of most recent backup
421
+ region: z.string().optional()
422
+ // AWS region (defaults to AWS_REGION env)
423
+ }).strict().optional();
424
+ var codeownersRuleSchema = z.object({
425
+ pattern: z.string(),
426
+ // File pattern (e.g., "/standards.toml", "*.js", "/src/api/*")
427
+ owners: z.array(z.string())
428
+ // Owner handles (e.g., ["@user", "@org/team"])
429
+ }).strict();
430
+ var codeownersConfigSchema = z.object({
431
+ enabled: z.boolean().optional().default(false),
432
+ rules: z.array(codeownersRuleSchema).optional()
433
+ // Required rules in CODEOWNERS
434
+ }).strict().optional();
435
+ var docsTypeConfigSchema = z.object({
436
+ required_sections: z.array(z.string()).optional(),
437
+ // e.g., ["Overview", "Parameters", "Returns", "Examples"]
438
+ frontmatter: z.array(z.string()).optional()
439
+ // e.g., ["title", "tracks"]
440
+ }).strict();
441
+ var docsEnforcementSchema = z.enum(["block", "warn"]);
442
+ var docsConfigSchema = z.object({
443
+ enabled: z.boolean().optional().default(false),
444
+ path: z.string().optional().default("docs/"),
445
+ // Documentation directory
446
+ enforcement: docsEnforcementSchema.optional().default("warn"),
447
+ // "block" or "warn"
448
+ allowlist: z.array(z.string()).optional(),
449
+ // Markdown files allowed outside docs/, e.g., ["README.md", "CLAUDE.md"]
450
+ max_files: z.number().int().positive().optional(),
451
+ // Max markdown files in docs/
452
+ max_file_lines: z.number().int().positive().optional(),
453
+ // Max lines per markdown file
454
+ max_total_kb: z.number().int().positive().optional(),
455
+ // Max total size of docs/
456
+ staleness_days: z.number().int().positive().optional().default(30),
457
+ // Days before doc is considered stale
458
+ stale_mappings: z.record(z.string(), z.string()).optional(),
459
+ // Override doc-to-source mappings
460
+ min_coverage: z.number().int().min(0).max(100).optional(),
461
+ // Minimum API coverage percentage
462
+ coverage_paths: z.array(z.string()).optional(),
463
+ // Glob patterns for source files, e.g., ["src/**/*.ts"]
464
+ exclude_patterns: z.array(z.string()).optional(),
465
+ // Exclude from coverage, e.g., ["**/*.test.ts"]
466
+ types: z.record(z.string(), docsTypeConfigSchema).optional()
467
+ // Per-type config, e.g., { api: {...}, guide: {...} }
468
+ }).strict().optional();
469
+ var mcpStandardsSchema = z.object({
470
+ source: z.string().optional().describe(
471
+ 'Standards repository source: "github:owner/repo", "github:owner/repo@ref", or local path'
472
+ )
473
+ }).strict().optional();
474
+ var mcpSchema = z.object({
475
+ standards: mcpStandardsSchema
476
+ }).strict().optional();
477
+ var infraSchema = z.object({
478
+ enabled: z.boolean().optional().default(false),
479
+ manifest: z.string().optional().default("infra-manifest.json")
480
+ // Path to manifest file
481
+ }).strict().optional();
482
+ var DEFAULT_FORBIDDEN_FILES_IGNORE = ["**/node_modules/**", "**/.git/**"];
483
+ var forbiddenFilesConfigSchema = z.object({
484
+ enabled: z.boolean().optional().default(false),
485
+ files: z.array(globPatternSchema).optional(),
486
+ // Glob patterns for files that must not exist (validated)
487
+ ignore: z.array(globPatternSchema).optional(),
488
+ // Glob patterns to ignore (validated, overrides defaults if provided)
489
+ message: z.string().optional()
490
+ // Custom message explaining why these files are forbidden
491
+ }).strict().optional();
492
+ var processSchema = z.object({
493
+ hooks: hooksConfigSchema,
494
+ ci: ciConfigSchema,
495
+ branches: branchesConfigSchema,
496
+ commits: commitsConfigSchema,
497
+ changesets: changesetsConfigSchema,
498
+ pr: prConfigSchema,
499
+ tickets: ticketsConfigSchema,
500
+ coverage: coverageConfigSchema,
501
+ repo: repoConfigSchema,
502
+ backups: backupsConfigSchema,
503
+ codeowners: codeownersConfigSchema,
504
+ docs: docsConfigSchema,
505
+ forbidden_files: forbiddenFilesConfigSchema
506
+ }).strict().optional();
507
+ var extendsSchema = z.object({
508
+ registry: z.string(),
509
+ // e.g., "github:myorg/standards" or local path
510
+ rulesets: z.array(z.string())
511
+ // e.g., ["base", "typescript"]
512
+ }).strict().optional();
513
+ var monorepoSchema = z.object({
514
+ exclude: z.array(globPatternSchema).optional()
515
+ // Glob patterns to exclude from project detection
516
+ }).strict().optional();
517
+ var configSchema = z.object({
518
+ extends: extendsSchema,
519
+ code: codeSchema,
520
+ process: processSchema,
521
+ infra: infraSchema,
522
+ mcp: mcpSchema,
523
+ monorepo: monorepoSchema
524
+ }).strict();
525
+ var defaultConfig = {
526
+ code: {
527
+ linting: {
528
+ eslint: { enabled: false },
529
+ ruff: { enabled: false }
530
+ },
531
+ types: {
532
+ tsc: { enabled: false },
533
+ ty: { enabled: false }
534
+ },
535
+ unused: {
536
+ knip: { enabled: false },
537
+ vulture: { enabled: false }
538
+ },
539
+ coverage_run: {
540
+ enabled: false,
541
+ min_threshold: 80,
542
+ runner: "auto"
543
+ },
544
+ security: {
545
+ secrets: { enabled: false, scan_mode: "branch", base_branch: "main" },
546
+ pnpmaudit: { enabled: false, exclude_dev: true },
547
+ pipaudit: { enabled: false }
548
+ },
549
+ naming: {
550
+ enabled: false
551
+ },
552
+ quality: {
553
+ "disable-comments": { enabled: false }
554
+ }
555
+ },
556
+ monorepo: {},
557
+ process: {
558
+ hooks: {
559
+ enabled: false,
560
+ require_husky: true
561
+ },
562
+ ci: {
563
+ enabled: false
564
+ },
565
+ branches: {
566
+ enabled: false,
567
+ require_issue: false
568
+ },
569
+ commits: {
570
+ enabled: false,
571
+ require_scope: false
572
+ },
573
+ changesets: {
574
+ enabled: false,
575
+ validate_format: true,
576
+ require_description: true
577
+ },
578
+ pr: {
579
+ enabled: false,
580
+ require_issue: false
581
+ },
582
+ tickets: {
583
+ enabled: false,
584
+ require_in_commits: true,
585
+ require_in_branch: false
586
+ },
587
+ coverage: {
588
+ enabled: false,
589
+ enforce_in: "config"
590
+ },
591
+ repo: {
592
+ enabled: false,
593
+ require_branch_protection: false,
594
+ require_codeowners: false
595
+ },
596
+ backups: {
597
+ enabled: false,
598
+ max_age_hours: 24
599
+ },
600
+ codeowners: {
601
+ enabled: false
602
+ },
603
+ docs: {
604
+ enabled: false,
605
+ path: "docs/",
606
+ enforcement: "warn",
607
+ staleness_days: 30
608
+ },
609
+ forbidden_files: {
610
+ enabled: false
611
+ }
612
+ },
613
+ infra: {
614
+ enabled: false,
615
+ manifest: "infra-manifest.json"
616
+ },
617
+ mcp: {
618
+ standards: {
619
+ source: void 0
620
+ }
621
+ }
622
+ };
623
+
624
+ // ../core/src/loader.ts
625
+ var CONFIG_FILE_NAME = "standards.toml";
626
+ var ConfigError = class extends Error {
627
+ constructor(message) {
628
+ super(message);
629
+ this.name = "ConfigError";
630
+ }
631
+ };
632
+ function isBrokenSymlink(filePath) {
633
+ try {
634
+ const stats = fs.lstatSync(filePath);
635
+ if (stats.isSymbolicLink()) {
636
+ try {
637
+ fs.statSync(filePath);
638
+ return false;
639
+ } catch {
640
+ return true;
641
+ }
642
+ }
643
+ return false;
644
+ } catch {
645
+ return false;
646
+ }
647
+ }
648
+ function findConfigFile(startDir = process.cwd()) {
649
+ let currentDir = path.resolve(startDir);
650
+ const root = path.parse(currentDir).root;
651
+ while (currentDir !== root) {
652
+ const configPath = path.join(currentDir, CONFIG_FILE_NAME);
653
+ if (isBrokenSymlink(configPath)) {
654
+ throw new ConfigError(`${CONFIG_FILE_NAME} exists but is a broken symlink: ${configPath}`);
655
+ }
656
+ if (fs.existsSync(configPath)) {
657
+ return configPath;
658
+ }
659
+ currentDir = path.dirname(currentDir);
660
+ }
661
+ const rootConfig = path.join(root, CONFIG_FILE_NAME);
662
+ if (isBrokenSymlink(rootConfig)) {
663
+ throw new ConfigError(`${CONFIG_FILE_NAME} exists but is a broken symlink: ${rootConfig}`);
664
+ }
665
+ return fs.existsSync(rootConfig) ? rootConfig : null;
666
+ }
667
+ function resolveConfigPath(configPath) {
668
+ const resolved = configPath ?? findConfigFile();
669
+ if (!resolved) {
670
+ throw new ConfigError(`No ${CONFIG_FILE_NAME} found. Create one or specify --config path.`);
671
+ }
672
+ const absolutePath = path.resolve(resolved);
673
+ if (!fs.existsSync(absolutePath)) {
674
+ throw new ConfigError(`Config file not found: ${resolved}`);
675
+ }
676
+ return absolutePath;
677
+ }
678
+ function parseTomlFile(filePath) {
679
+ try {
680
+ const content = fs.readFileSync(filePath, "utf-8");
681
+ return TOML.parse(content);
682
+ } catch (error) {
683
+ const message = error instanceof Error ? error.message : "Unknown error";
684
+ throw new ConfigError(`Failed to parse ${CONFIG_FILE_NAME}: ${message}`);
685
+ }
686
+ }
687
+ function validateConfig(rawConfig) {
688
+ const result = configSchema.safeParse(rawConfig);
689
+ if (!result.success) {
690
+ const errors = result.error.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
691
+ throw new ConfigError(`Invalid ${CONFIG_FILE_NAME} configuration:
692
+ ${errors}`);
693
+ }
694
+ return result.data;
695
+ }
696
+ function loadConfig(configPath) {
697
+ const resolvedPath = resolveConfigPath(configPath);
698
+ const rawConfig = parseTomlFile(resolvedPath);
699
+ const validatedConfig = validateConfig(rawConfig);
700
+ const config = mergeWithDefaults(validatedConfig);
701
+ return { config, configPath: resolvedPath };
702
+ }
703
+ async function loadConfigAsync(configPath) {
704
+ const resolvedPath = resolveConfigPath(configPath);
705
+ const rawConfig = parseTomlFile(resolvedPath);
706
+ const validatedConfig = validateConfig(rawConfig);
707
+ const configDir = path.dirname(resolvedPath);
708
+ const resolvedConfig = await resolveExtends(validatedConfig, configDir);
709
+ const config = mergeWithDefaults(resolvedConfig);
710
+ return { config, configPath: resolvedPath };
711
+ }
712
+ function merge(a, b) {
713
+ return { ...a, ...b };
714
+ }
715
+ function mergeLinting(c, dc) {
716
+ const cl = c.code?.linting;
717
+ const dl = dc.code?.linting;
718
+ return { eslint: merge(dl?.eslint, cl?.eslint), ruff: merge(dl?.ruff, cl?.ruff) };
719
+ }
720
+ function mergeSecurity(c, dc) {
721
+ const cs = c.code?.security;
722
+ const ds = dc.code?.security;
723
+ return {
724
+ secrets: merge(ds?.secrets, cs?.secrets),
725
+ pnpmaudit: merge(ds?.pnpmaudit, cs?.pnpmaudit),
726
+ pipaudit: merge(ds?.pipaudit, cs?.pipaudit)
727
+ };
728
+ }
729
+ function mergeTypes(c, dc) {
730
+ return {
731
+ tsc: merge(dc.code?.types?.tsc, c.code?.types?.tsc),
732
+ ty: merge(dc.code?.types?.ty, c.code?.types?.ty)
733
+ };
734
+ }
735
+ function mergeUnused(c, dc) {
736
+ return {
737
+ knip: merge(dc.code?.unused?.knip, c.code?.unused?.knip),
738
+ vulture: merge(dc.code?.unused?.vulture, c.code?.unused?.vulture)
739
+ };
740
+ }
741
+ function mergeCoverageRun(c, dc) {
742
+ return merge(dc.code?.coverage_run, c.code?.coverage_run);
743
+ }
744
+ function mergeNaming(c, dc) {
745
+ const cn = c.code?.naming;
746
+ const dn = dc.code?.naming;
747
+ return {
748
+ enabled: cn?.enabled ?? dn?.enabled ?? false,
749
+ rules: cn?.rules ?? dn?.rules
750
+ };
751
+ }
752
+ function mergeQuality(c, dc) {
753
+ const cq = c.code?.quality;
754
+ const dq = dc.code?.quality;
755
+ return {
756
+ "disable-comments": merge(dq?.["disable-comments"], cq?.["disable-comments"])
757
+ };
758
+ }
759
+ function mergeCode(c, dc) {
760
+ return {
761
+ linting: mergeLinting(c, dc),
762
+ types: mergeTypes(c, dc),
763
+ unused: mergeUnused(c, dc),
764
+ coverage_run: mergeCoverageRun(c, dc),
765
+ security: mergeSecurity(c, dc),
766
+ naming: mergeNaming(c, dc),
767
+ quality: mergeQuality(c, dc)
768
+ };
769
+ }
770
+ var defaultHooks = { enabled: false, require_husky: true };
771
+ var defaultCi = { enabled: false };
772
+ var defaultBranches = { enabled: false, require_issue: false };
773
+ var defaultCommits = { enabled: false, require_scope: false };
774
+ var defaultChangesets = {
775
+ enabled: false,
776
+ validate_format: true,
777
+ require_description: true
778
+ };
779
+ var defaultPr = { enabled: false, require_issue: false };
780
+ var defaultTickets = {
781
+ enabled: false,
782
+ require_in_commits: true,
783
+ require_in_branch: false
784
+ };
785
+ var defaultCoverage = { enabled: false, enforce_in: "config" };
786
+ var defaultRepo = {
787
+ enabled: false,
788
+ require_branch_protection: false,
789
+ require_codeowners: false
790
+ };
791
+ var defaultBackups = { enabled: false, max_age_hours: 24 };
792
+ var defaultCodeowners = { enabled: false };
793
+ var defaultDocs = {
794
+ enabled: false,
795
+ path: "docs/",
796
+ enforcement: "warn",
797
+ staleness_days: 30
798
+ };
799
+ var defaultForbiddenFiles = { enabled: false };
800
+ function mergeProcessSection(defaultVal, dcVal, cVal) {
801
+ return { ...defaultVal, ...dcVal, ...cVal };
802
+ }
803
+ function mergeProcessHooks(cp, dcp) {
804
+ return mergeProcessSection(defaultHooks, dcp?.hooks, cp?.hooks);
805
+ }
806
+ function mergeProcessCi(cp, dcp) {
807
+ return mergeProcessSection(defaultCi, dcp?.ci, cp?.ci);
808
+ }
809
+ function mergeProcessBranches(cp, dcp) {
810
+ return mergeProcessSection(defaultBranches, dcp?.branches, cp?.branches);
811
+ }
812
+ function mergeProcessCommits(cp, dcp) {
813
+ return mergeProcessSection(defaultCommits, dcp?.commits, cp?.commits);
814
+ }
815
+ function mergeProcessChangesets(cp, dcp) {
816
+ return mergeProcessSection(defaultChangesets, dcp?.changesets, cp?.changesets);
817
+ }
818
+ function mergeProcessPr(cp, dcp) {
819
+ return mergeProcessSection(defaultPr, dcp?.pr, cp?.pr);
820
+ }
821
+ function mergeProcessTickets(cp, dcp) {
822
+ return mergeProcessSection(defaultTickets, dcp?.tickets, cp?.tickets);
823
+ }
824
+ function mergeProcessCoverage(cp, dcp) {
825
+ return mergeProcessSection(defaultCoverage, dcp?.coverage, cp?.coverage);
826
+ }
827
+ function mergeProcessRepo(cp, dcp) {
828
+ return mergeProcessSection(defaultRepo, dcp?.repo, cp?.repo);
829
+ }
830
+ function mergeProcessBackups(cp, dcp) {
831
+ return mergeProcessSection(defaultBackups, dcp?.backups, cp?.backups);
832
+ }
833
+ function mergeProcessCodeowners(cp, dcp) {
834
+ const cco = cp?.codeowners;
835
+ const dco = dcp?.codeowners;
836
+ const registryRules = dco?.rules ?? [];
837
+ const projectRules = cco?.rules ?? [];
838
+ const ruleMap = /* @__PURE__ */ new Map();
839
+ for (const rule of registryRules) {
840
+ ruleMap.set(rule.pattern, rule);
841
+ }
842
+ for (const rule of projectRules) {
843
+ ruleMap.set(rule.pattern, rule);
844
+ }
845
+ const mergedRules = Array.from(ruleMap.values());
846
+ return {
847
+ ...defaultCodeowners,
848
+ ...dco,
849
+ ...cco,
850
+ rules: mergedRules.length > 0 ? mergedRules : void 0
851
+ };
852
+ }
853
+ function mergeProcessDocs(cp, dcp) {
854
+ return mergeProcessSection(defaultDocs, dcp?.docs, cp?.docs);
855
+ }
856
+ function mergeProcessForbiddenFiles(cp, dcp) {
857
+ return mergeProcessSection(defaultForbiddenFiles, dcp?.forbidden_files, cp?.forbidden_files);
858
+ }
859
+ function mergeProcess(c, dc) {
860
+ return {
861
+ hooks: mergeProcessHooks(c.process, dc.process),
862
+ ci: mergeProcessCi(c.process, dc.process),
863
+ branches: mergeProcessBranches(c.process, dc.process),
864
+ commits: mergeProcessCommits(c.process, dc.process),
865
+ changesets: mergeProcessChangesets(c.process, dc.process),
866
+ pr: mergeProcessPr(c.process, dc.process),
867
+ tickets: mergeProcessTickets(c.process, dc.process),
868
+ coverage: mergeProcessCoverage(c.process, dc.process),
869
+ repo: mergeProcessRepo(c.process, dc.process),
870
+ backups: mergeProcessBackups(c.process, dc.process),
871
+ codeowners: mergeProcessCodeowners(c.process, dc.process),
872
+ docs: mergeProcessDocs(c.process, dc.process),
873
+ forbidden_files: mergeProcessForbiddenFiles(c.process, dc.process)
874
+ };
875
+ }
876
+ function mergeWithDefaults(config) {
877
+ return {
878
+ code: mergeCode(config, defaultConfig),
879
+ process: mergeProcess(config, defaultConfig),
880
+ infra: {
881
+ enabled: config.infra?.enabled ?? defaultConfig.infra?.enabled ?? false,
882
+ manifest: config.infra?.manifest ?? defaultConfig.infra?.manifest ?? "infra-manifest.json"
883
+ },
884
+ monorepo: config.monorepo
885
+ };
886
+ }
887
+ function getProjectRoot(configPath) {
888
+ return path.dirname(configPath);
889
+ }
890
+ function checkRuleOverride(projectRule, registryOwners) {
891
+ if (!registryOwners) {
892
+ return null;
893
+ }
894
+ const registryStr = registryOwners.join(" ");
895
+ const projectStr = projectRule.owners.join(" ");
896
+ if (registryStr === projectStr) {
897
+ return null;
898
+ }
899
+ return {
900
+ section: "process.codeowners.rules",
901
+ key: projectRule.pattern,
902
+ registryValue: registryStr,
903
+ projectValue: projectStr
904
+ };
905
+ }
906
+ function detectCodeownersOverrides(registryConfig, projectConfig) {
907
+ const registryRules = registryConfig?.process?.codeowners?.rules ?? [];
908
+ const projectRules = projectConfig?.process?.codeowners?.rules ?? [];
909
+ const registryMap = new Map(registryRules.map((r) => [r.pattern, r.owners]));
910
+ return projectRules.map((rule) => checkRuleOverride(rule, registryMap.get(rule.pattern))).filter((o) => o !== null);
911
+ }
912
+ async function loadRegistryConfig(extendsConfig, configDir) {
913
+ const registryModule = await import("./registry-V65CC7IN.js");
914
+ const loc = registryModule.parseRegistryUrl(extendsConfig.registry, configDir);
915
+ const registryDir = await registryModule.fetchRegistry(loc);
916
+ let config = {};
917
+ for (const name of extendsConfig.rulesets) {
918
+ config = registryModule.mergeConfigs(config, registryModule.loadRuleset(registryDir, name));
919
+ }
920
+ return config;
921
+ }
922
+ async function loadConfigWithOverrides(configPath) {
923
+ const resolvedPath = resolveConfigPath(configPath);
924
+ const validatedConfig = validateConfig(parseTomlFile(resolvedPath));
925
+ let overrides = [];
926
+ if (validatedConfig.extends) {
927
+ const registryConfig = await loadRegistryConfig(
928
+ validatedConfig.extends,
929
+ path.dirname(resolvedPath)
930
+ );
931
+ overrides = detectCodeownersOverrides(registryConfig, validatedConfig);
932
+ }
933
+ const { config, configPath: finalPath } = await loadConfigAsync(configPath);
934
+ return { config, configPath: finalPath, overrides };
935
+ }
936
+
937
+ // ../core/src/registry.ts
938
+ function detectAuthMethod() {
939
+ if (process.env.CONFORM_REGISTRY_TOKEN || process.env.GITHUB_TOKEN) {
940
+ return "token";
941
+ }
942
+ if (process.env.SSH_AUTH_SOCK) {
943
+ return "ssh";
944
+ }
945
+ return "none";
946
+ }
947
+ function getAuthToken() {
948
+ return process.env.CONFORM_REGISTRY_TOKEN ?? process.env.GITHUB_TOKEN;
949
+ }
950
+ function buildGitHubUrl(owner, repo, auth) {
951
+ switch (auth) {
952
+ case "ssh":
953
+ return `git@github.com:${owner}/${repo}.git`;
954
+ case "token": {
955
+ const token = getAuthToken();
956
+ if (token) {
957
+ return `https://x-access-token:${token}@github.com/${owner}/${repo}.git`;
958
+ }
959
+ return `https://github.com/${owner}/${repo}.git`;
960
+ }
961
+ case "none":
962
+ default:
963
+ return `https://github.com/${owner}/${repo}.git`;
964
+ }
965
+ }
966
+ function parseAuthFromUrl(url) {
967
+ if (url.startsWith("github+ssh:")) {
968
+ return { auth: "ssh", rest: url.slice(11) };
969
+ }
970
+ if (url.startsWith("github+token:")) {
971
+ return { auth: "token", rest: url.slice(13) };
972
+ }
973
+ if (url.startsWith("github:")) {
974
+ return { auth: "auto", rest: url.slice(7) };
975
+ }
976
+ throw new ConfigError(`Invalid GitHub registry URL: ${url}`);
977
+ }
978
+ function parseGitHubUrl(url) {
979
+ const { auth: explicitAuth, rest } = parseAuthFromUrl(url);
980
+ const [repoPath, ref] = rest.split("@");
981
+ const [owner, repo] = repoPath.split("/");
982
+ if (!owner || !repo) {
983
+ throw new ConfigError(
984
+ `Invalid GitHub registry URL: ${url}. Expected format: github:owner/repo or github+ssh:owner/repo`
985
+ );
986
+ }
987
+ const auth = explicitAuth === "auto" ? detectAuthMethod() : explicitAuth;
988
+ return {
989
+ type: "github",
990
+ owner,
991
+ repo,
992
+ ref: ref || void 0,
993
+ path: buildGitHubUrl(owner, repo, auth),
994
+ auth
995
+ };
996
+ }
997
+ function parseRegistryUrl(url, configDir) {
998
+ if (url.startsWith("github:") || url.startsWith("github+")) {
999
+ return parseGitHubUrl(url);
1000
+ }
1001
+ const localPath = !path2.isAbsolute(url) && configDir ? path2.resolve(configDir, url) : url;
1002
+ return { type: "local", path: localPath };
1003
+ }
1004
+ async function updateExistingRepo(repoDir, ref) {
1005
+ try {
1006
+ if (ref) {
1007
+ await execa("git", ["fetch", "--all"], { cwd: repoDir });
1008
+ await execa("git", ["checkout", ref], { cwd: repoDir });
1009
+ } else {
1010
+ await execa("git", ["pull", "--ff-only"], { cwd: repoDir });
1011
+ }
1012
+ return true;
1013
+ } catch {
1014
+ fs2.rmSync(repoDir, { recursive: true, force: true });
1015
+ return false;
1016
+ }
1017
+ }
1018
+ async function cloneRepo(location, repoDir) {
1019
+ const cacheDir = path2.dirname(repoDir);
1020
+ fs2.mkdirSync(cacheDir, { recursive: true });
1021
+ const cloneArgs = ["clone", "--depth", "1"];
1022
+ if (location.ref) {
1023
+ cloneArgs.push("--branch", location.ref);
1024
+ }
1025
+ cloneArgs.push(location.path, repoDir);
1026
+ try {
1027
+ await execa("git", cloneArgs, { timeout: 30 * 1e3 });
1028
+ } catch (error) {
1029
+ const message = error instanceof Error ? error.message : String(error);
1030
+ if (message.includes("timed out")) {
1031
+ throw new ConfigError(`Registry clone timed out after 30 seconds: ${location.path}`);
1032
+ }
1033
+ throw new ConfigError(`Failed to clone registry: ${message}`);
1034
+ }
1035
+ }
1036
+ async function fetchRegistry(location) {
1037
+ if (location.type === "local") {
1038
+ if (!fs2.existsSync(location.path)) {
1039
+ throw new ConfigError(`Registry not found: ${location.path}`);
1040
+ }
1041
+ return location.path;
1042
+ }
1043
+ const cacheDir = path2.join(os.tmpdir(), "conform-registry-cache");
1044
+ const repoDir = path2.join(cacheDir, `${location.owner}-${location.repo}`);
1045
+ if (fs2.existsSync(repoDir)) {
1046
+ await updateExistingRepo(repoDir, location.ref);
1047
+ }
1048
+ if (!fs2.existsSync(repoDir)) {
1049
+ await cloneRepo(location, repoDir);
1050
+ }
1051
+ return repoDir;
1052
+ }
1053
+ function loadRuleset(registryDir, rulesetName) {
1054
+ const rulesetPath = path2.join(registryDir, "rulesets", `${rulesetName}.toml`);
1055
+ if (!fs2.existsSync(rulesetPath)) {
1056
+ throw new ConfigError(`Ruleset not found: ${rulesetName} (expected at ${rulesetPath})`);
1057
+ }
1058
+ const content = fs2.readFileSync(rulesetPath, "utf-8");
1059
+ let parsed;
1060
+ try {
1061
+ parsed = toml.parse(content);
1062
+ } catch (error) {
1063
+ const message = error instanceof Error ? error.message : String(error);
1064
+ throw new ConfigError(`Failed to parse ruleset ${rulesetName}: ${message}`);
1065
+ }
1066
+ const result = configSchema.safeParse(parsed);
1067
+ if (!result.success) {
1068
+ const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
1069
+ throw new ConfigError(`Invalid ruleset ${rulesetName}: ${errors}`);
1070
+ }
1071
+ return result.data;
1072
+ }
1073
+ function mergeToolConfig(base, override) {
1074
+ if (!override) {
1075
+ return base;
1076
+ }
1077
+ return { ...base, ...override };
1078
+ }
1079
+ function mergeLinting2(base, override) {
1080
+ if (!override) {
1081
+ return base;
1082
+ }
1083
+ return {
1084
+ ...base,
1085
+ eslint: mergeToolConfig(base?.eslint, override.eslint),
1086
+ ruff: mergeToolConfig(base?.ruff, override.ruff)
1087
+ };
1088
+ }
1089
+ function mergeTypes2(base, override) {
1090
+ if (!override) {
1091
+ return base;
1092
+ }
1093
+ return {
1094
+ ...base,
1095
+ tsc: mergeToolConfig(base?.tsc, override.tsc),
1096
+ ty: mergeToolConfig(base?.ty, override.ty)
1097
+ };
1098
+ }
1099
+ function mergeUnused2(base, override) {
1100
+ if (!override) {
1101
+ return base;
1102
+ }
1103
+ return {
1104
+ ...base,
1105
+ knip: mergeToolConfig(base?.knip, override.knip),
1106
+ vulture: mergeToolConfig(base?.vulture, override.vulture)
1107
+ };
1108
+ }
1109
+ function mergeSecurity2(base, override) {
1110
+ if (!override) {
1111
+ return base;
1112
+ }
1113
+ return {
1114
+ ...base,
1115
+ secrets: mergeToolConfig(base?.secrets, override.secrets),
1116
+ pnpmaudit: mergeToolConfig(base?.pnpmaudit, override.pnpmaudit),
1117
+ pipaudit: mergeToolConfig(base?.pipaudit, override.pipaudit)
1118
+ };
1119
+ }
1120
+ function mergeNaming2(base, override) {
1121
+ if (!override) {
1122
+ return base;
1123
+ }
1124
+ return {
1125
+ enabled: override.enabled,
1126
+ rules: override.rules ?? base?.rules
1127
+ };
1128
+ }
1129
+ function mergeQuality2(base, override) {
1130
+ if (!override) {
1131
+ return base;
1132
+ }
1133
+ return {
1134
+ ...base,
1135
+ "disable-comments": mergeToolConfig(base?.["disable-comments"], override["disable-comments"])
1136
+ };
1137
+ }
1138
+ function mergeCodeSection(base, override) {
1139
+ return {
1140
+ linting: mergeLinting2(base?.linting, override.linting),
1141
+ types: mergeTypes2(base?.types, override.types),
1142
+ unused: mergeUnused2(base?.unused, override.unused),
1143
+ coverage_run: mergeToolConfig(base?.coverage_run, override.coverage_run),
1144
+ security: mergeSecurity2(base?.security, override.security),
1145
+ naming: mergeNaming2(base?.naming, override.naming),
1146
+ quality: mergeQuality2(base?.quality, override.quality)
1147
+ };
1148
+ }
1149
+ function mergeHooksConfig(base, override) {
1150
+ if (!override) {
1151
+ return base;
1152
+ }
1153
+ return {
1154
+ enabled: override.enabled,
1155
+ require_husky: override.require_husky,
1156
+ require_hooks: override.require_hooks ?? base?.require_hooks,
1157
+ commands: override.commands ?? base?.commands
1158
+ };
1159
+ }
1160
+ function mergeCiConfig(base, override) {
1161
+ if (!override) {
1162
+ return base;
1163
+ }
1164
+ return {
1165
+ enabled: override.enabled,
1166
+ require_workflows: override.require_workflows ?? base?.require_workflows,
1167
+ jobs: override.jobs ?? base?.jobs,
1168
+ actions: override.actions ?? base?.actions
1169
+ };
1170
+ }
1171
+ function mergeBranchesConfig(base, override) {
1172
+ if (!override) {
1173
+ return base;
1174
+ }
1175
+ return {
1176
+ enabled: override.enabled,
1177
+ require_issue: override.require_issue,
1178
+ pattern: override.pattern ?? base?.pattern,
1179
+ exclude: override.exclude ?? base?.exclude,
1180
+ issue_pattern: override.issue_pattern ?? base?.issue_pattern
1181
+ };
1182
+ }
1183
+ function mergePrConfig(base, override) {
1184
+ if (!override) {
1185
+ return base;
1186
+ }
1187
+ return {
1188
+ enabled: override.enabled,
1189
+ require_issue: override.require_issue,
1190
+ max_files: override.max_files ?? base?.max_files,
1191
+ max_lines: override.max_lines ?? base?.max_lines,
1192
+ issue_keywords: override.issue_keywords ?? base?.issue_keywords
1193
+ };
1194
+ }
1195
+ function mergeTicketsConfig(base, override) {
1196
+ if (!override) {
1197
+ return base;
1198
+ }
1199
+ return {
1200
+ enabled: override.enabled,
1201
+ pattern: override.pattern ?? base?.pattern,
1202
+ require_in_commits: override.require_in_commits,
1203
+ require_in_branch: override.require_in_branch
1204
+ };
1205
+ }
1206
+ function mergeCoverageConfig(base, override) {
1207
+ if (!override) {
1208
+ return base;
1209
+ }
1210
+ return {
1211
+ enabled: override.enabled,
1212
+ min_threshold: override.min_threshold ?? base?.min_threshold,
1213
+ enforce_in: override.enforce_in,
1214
+ ci_workflow: override.ci_workflow ?? base?.ci_workflow,
1215
+ ci_job: override.ci_job ?? base?.ci_job
1216
+ };
1217
+ }
1218
+ function mergeCommitsConfig(base, override) {
1219
+ if (!override) {
1220
+ return base;
1221
+ }
1222
+ return {
1223
+ enabled: override.enabled,
1224
+ pattern: override.pattern ?? base?.pattern,
1225
+ types: override.types ?? base?.types,
1226
+ require_scope: override.require_scope,
1227
+ max_subject_length: override.max_subject_length ?? base?.max_subject_length
1228
+ };
1229
+ }
1230
+ function mergeChangesetsConfig(base, override) {
1231
+ if (!override) {
1232
+ return base;
1233
+ }
1234
+ return {
1235
+ enabled: override.enabled,
1236
+ require_for_paths: override.require_for_paths ?? base?.require_for_paths,
1237
+ exclude_paths: override.exclude_paths ?? base?.exclude_paths,
1238
+ validate_format: override.validate_format,
1239
+ allowed_bump_types: override.allowed_bump_types ?? base?.allowed_bump_types,
1240
+ require_description: override.require_description,
1241
+ min_description_length: override.min_description_length ?? base?.min_description_length
1242
+ };
1243
+ }
1244
+ function mergeRepoConfig(base, override) {
1245
+ if (!override) {
1246
+ return base;
1247
+ }
1248
+ return {
1249
+ enabled: override.enabled,
1250
+ require_branch_protection: override.require_branch_protection,
1251
+ require_codeowners: override.require_codeowners,
1252
+ ruleset: override.ruleset ?? base?.ruleset,
1253
+ tag_protection: override.tag_protection ?? base?.tag_protection
1254
+ };
1255
+ }
1256
+ function mergeBackupsConfig(base, override) {
1257
+ if (!override) {
1258
+ return base;
1259
+ }
1260
+ return {
1261
+ enabled: override.enabled,
1262
+ bucket: override.bucket ?? base?.bucket,
1263
+ prefix: override.prefix ?? base?.prefix,
1264
+ max_age_hours: override.max_age_hours,
1265
+ region: override.region ?? base?.region
1266
+ };
1267
+ }
1268
+ function mergeCodeownersConfig(base, override) {
1269
+ if (!override) {
1270
+ return base;
1271
+ }
1272
+ return {
1273
+ enabled: override.enabled,
1274
+ rules: override.rules ?? base?.rules
1275
+ };
1276
+ }
1277
+ function mergeDocsConfig(base, override) {
1278
+ if (!override) {
1279
+ return base;
1280
+ }
1281
+ return {
1282
+ enabled: override.enabled,
1283
+ path: override.path,
1284
+ enforcement: override.enforcement,
1285
+ allowlist: override.allowlist ?? base?.allowlist,
1286
+ max_files: override.max_files ?? base?.max_files,
1287
+ max_file_lines: override.max_file_lines ?? base?.max_file_lines,
1288
+ max_total_kb: override.max_total_kb ?? base?.max_total_kb,
1289
+ staleness_days: override.staleness_days,
1290
+ stale_mappings: override.stale_mappings ?? base?.stale_mappings,
1291
+ min_coverage: override.min_coverage ?? base?.min_coverage,
1292
+ coverage_paths: override.coverage_paths ?? base?.coverage_paths,
1293
+ exclude_patterns: override.exclude_patterns ?? base?.exclude_patterns,
1294
+ types: override.types ?? base?.types
1295
+ };
1296
+ }
1297
+ function mergeProcessSection2(base, override) {
1298
+ return {
1299
+ hooks: mergeHooksConfig(base?.hooks, override.hooks),
1300
+ ci: mergeCiConfig(base?.ci, override.ci),
1301
+ branches: mergeBranchesConfig(base?.branches, override.branches),
1302
+ commits: mergeCommitsConfig(base?.commits, override.commits),
1303
+ changesets: mergeChangesetsConfig(base?.changesets, override.changesets),
1304
+ pr: mergePrConfig(base?.pr, override.pr),
1305
+ tickets: mergeTicketsConfig(base?.tickets, override.tickets),
1306
+ coverage: mergeCoverageConfig(base?.coverage, override.coverage),
1307
+ repo: mergeRepoConfig(base?.repo, override.repo),
1308
+ backups: mergeBackupsConfig(base?.backups, override.backups),
1309
+ codeowners: mergeCodeownersConfig(base?.codeowners, override.codeowners),
1310
+ docs: mergeDocsConfig(base?.docs, override.docs)
1311
+ };
1312
+ }
1313
+ function mergeConfigs(base, override) {
1314
+ const merged = { ...base };
1315
+ if (override.code) {
1316
+ merged.code = mergeCodeSection(base.code, override.code);
1317
+ }
1318
+ if (override.process) {
1319
+ merged.process = mergeProcessSection2(base.process, override.process);
1320
+ }
1321
+ if (override.infra) {
1322
+ merged.infra = override.infra;
1323
+ }
1324
+ if (override.monorepo) {
1325
+ merged.monorepo = override.monorepo;
1326
+ }
1327
+ return merged;
1328
+ }
1329
+ async function resolveExtends(config, configDir) {
1330
+ if (!config.extends) {
1331
+ return config;
1332
+ }
1333
+ const { registry, rulesets } = config.extends;
1334
+ const location = parseRegistryUrl(registry, configDir);
1335
+ const registryDir = await fetchRegistry(location);
1336
+ let mergedConfig = {};
1337
+ for (const rulesetName of rulesets) {
1338
+ const ruleset = loadRuleset(registryDir, rulesetName);
1339
+ mergedConfig = mergeConfigs(mergedConfig, ruleset);
1340
+ }
1341
+ const localConfig = {
1342
+ code: config.code,
1343
+ process: config.process,
1344
+ infra: config.infra,
1345
+ monorepo: config.monorepo
1346
+ };
1347
+ return mergeConfigs(mergedConfig, localConfig);
1348
+ }
1349
+
1350
+ export {
1351
+ DEFAULT_FORBIDDEN_FILES_IGNORE,
1352
+ configSchema,
1353
+ defaultConfig,
1354
+ parseRegistryUrl,
1355
+ fetchRegistry,
1356
+ loadRuleset,
1357
+ mergeConfigs,
1358
+ resolveExtends,
1359
+ CONFIG_FILE_NAME,
1360
+ ConfigError,
1361
+ findConfigFile,
1362
+ loadConfig,
1363
+ loadConfigAsync,
1364
+ getProjectRoot,
1365
+ loadConfigWithOverrides
1366
+ };
1367
+ //# sourceMappingURL=chunk-KHO6NIAI.js.map