@quonfig/cli 0.0.1 → 0.0.2
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/README.md +59 -20
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +83 -0
- package/dist/init/init-workspace.d.ts +25 -0
- package/dist/init/init-workspace.js +162 -0
- package/dist/init/samples.d.ts +11 -0
- package/dist/init/samples.js +118 -0
- package/dist/init/schema.d.ts +9 -0
- package/dist/init/schema.js +334 -0
- package/dist/init/templates.d.ts +12 -0
- package/dist/init/templates.js +161 -0
- package/dist/util/token-storage.js +6 -1
- package/dist/verify/validate.d.ts +13 -0
- package/dist/verify/validate.js +89 -14
- package/oclif.manifest.json +76 -1
- package/package.json +1 -1
package/dist/verify/validate.js
CHANGED
|
@@ -192,6 +192,18 @@ import * as path from "node:path";
|
|
|
192
192
|
export function validateWorkspace(workspaceDir) {
|
|
193
193
|
const issues = [];
|
|
194
194
|
let filesChecked = 0;
|
|
195
|
+
const stats = {
|
|
196
|
+
configs: 0,
|
|
197
|
+
featureFlags: 0,
|
|
198
|
+
segments: 0,
|
|
199
|
+
logLevels: 0,
|
|
200
|
+
schemas: 0,
|
|
201
|
+
environmentOverrides: 0,
|
|
202
|
+
rules: 0,
|
|
203
|
+
segmentRefsChecked: 0,
|
|
204
|
+
schemaRefsChecked: 0,
|
|
205
|
+
uniqueKeysVerified: 0,
|
|
206
|
+
};
|
|
195
207
|
// Collect all configs for cross-reference checks
|
|
196
208
|
const allConfigs = [];
|
|
197
209
|
const allSchemaFiles = [];
|
|
@@ -211,6 +223,7 @@ export function validateWorkspace(workspaceDir) {
|
|
|
211
223
|
}],
|
|
212
224
|
filesChecked: 0,
|
|
213
225
|
valid: false,
|
|
226
|
+
stats,
|
|
214
227
|
};
|
|
215
228
|
}
|
|
216
229
|
for (const entry of entries) {
|
|
@@ -272,6 +285,7 @@ export function validateWorkspace(workspaceDir) {
|
|
|
272
285
|
schemaKeys.add(schemaKey);
|
|
273
286
|
validateKey(schemaKey, relPath, issues);
|
|
274
287
|
allSchemaFiles.push({ key: schemaKey, file: relPath });
|
|
288
|
+
stats.schemas++;
|
|
275
289
|
continue;
|
|
276
290
|
}
|
|
277
291
|
// Validate against StoredConfigSchema
|
|
@@ -341,12 +355,29 @@ export function validateWorkspace(workspaceDir) {
|
|
|
341
355
|
for (const env of config.environments) {
|
|
342
356
|
validateRules(env.rules, relPath, `environments[${env.id}]`, config.valueType, issues);
|
|
343
357
|
}
|
|
358
|
+
// Count by type
|
|
359
|
+
switch (config.type) {
|
|
360
|
+
case "config":
|
|
361
|
+
stats.configs++;
|
|
362
|
+
break;
|
|
363
|
+
case "feature_flag":
|
|
364
|
+
stats.featureFlags++;
|
|
365
|
+
break;
|
|
366
|
+
case "segment":
|
|
367
|
+
stats.segments++;
|
|
368
|
+
break;
|
|
369
|
+
case "log_level":
|
|
370
|
+
stats.logLevels++;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
// Count rules and environment overrides
|
|
374
|
+
stats.rules += config.default.rules.length;
|
|
375
|
+
stats.environmentOverrides += config.environments.length;
|
|
376
|
+
for (const env of config.environments) {
|
|
377
|
+
stats.rules += env.rules.length;
|
|
378
|
+
}
|
|
344
379
|
// Collect for cross-reference
|
|
345
380
|
allConfigs.push({ key: config.key, type: config.type, dir, file: relPath });
|
|
346
|
-
// Check schemaKey reference (collected, validated in second pass)
|
|
347
|
-
if (config.schemaKey) {
|
|
348
|
-
// Will be checked after all files are parsed
|
|
349
|
-
}
|
|
350
381
|
}
|
|
351
382
|
}
|
|
352
383
|
// Second pass: referential integrity
|
|
@@ -382,6 +413,7 @@ export function validateWorkspace(workspaceDir) {
|
|
|
382
413
|
// Check segment references in criteria
|
|
383
414
|
const segRefs = collectSegmentReferences(config);
|
|
384
415
|
for (const ref of segRefs) {
|
|
416
|
+
stats.segmentRefsChecked++;
|
|
385
417
|
if (!segmentKeys.has(ref)) {
|
|
386
418
|
issues.push({
|
|
387
419
|
file: relPath,
|
|
@@ -392,13 +424,16 @@ export function validateWorkspace(workspaceDir) {
|
|
|
392
424
|
}
|
|
393
425
|
}
|
|
394
426
|
// Check schemaKey references
|
|
395
|
-
if (config.schemaKey
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
427
|
+
if (config.schemaKey) {
|
|
428
|
+
stats.schemaRefsChecked++;
|
|
429
|
+
if (!schemaKeys.has(config.schemaKey)) {
|
|
430
|
+
issues.push({
|
|
431
|
+
file: relPath,
|
|
432
|
+
message: `References schema "${config.schemaKey}" which does not exist`,
|
|
433
|
+
severity: "error",
|
|
434
|
+
suggestion: `Create schemas/${config.schemaKey}.json or schemas-protected/${config.schemaKey}.json, or remove schemaKey`,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
402
437
|
}
|
|
403
438
|
}
|
|
404
439
|
}
|
|
@@ -435,8 +470,17 @@ export function validateWorkspace(workspaceDir) {
|
|
|
435
470
|
});
|
|
436
471
|
}
|
|
437
472
|
}
|
|
473
|
+
stats.uniqueKeysVerified = keyToFiles.size + schemaKeyToFiles.size;
|
|
438
474
|
const hasErrors = issues.some(i => i.severity === "error");
|
|
439
|
-
return { issues, filesChecked, valid: !hasErrors };
|
|
475
|
+
return { issues, filesChecked, valid: !hasErrors, stats };
|
|
476
|
+
}
|
|
477
|
+
// Default empty stats for validateFileMap (which doesn't track detailed stats)
|
|
478
|
+
function emptyStats() {
|
|
479
|
+
return {
|
|
480
|
+
configs: 0, featureFlags: 0, segments: 0, logLevels: 0, schemas: 0,
|
|
481
|
+
environmentOverrides: 0, rules: 0, segmentRefsChecked: 0,
|
|
482
|
+
schemaRefsChecked: 0, uniqueKeysVerified: 0,
|
|
483
|
+
};
|
|
440
484
|
}
|
|
441
485
|
// ── Validate from in-memory file map (for git hook) ─────────────────────
|
|
442
486
|
/**
|
|
@@ -593,7 +637,7 @@ export function validateFileMap(files) {
|
|
|
593
637
|
}
|
|
594
638
|
}
|
|
595
639
|
const hasErrors = issues.some(i => i.severity === "error");
|
|
596
|
-
return { issues, filesChecked, valid: !hasErrors };
|
|
640
|
+
return { issues, filesChecked, valid: !hasErrors, stats: emptyStats() };
|
|
597
641
|
}
|
|
598
642
|
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
599
643
|
function validateEnvironmentIds(environments, file, issues) {
|
|
@@ -720,14 +764,42 @@ function typeToDir(type) {
|
|
|
720
764
|
// ── Output formatting ───────────────────────────────────────────────────
|
|
721
765
|
export function formatResult(result) {
|
|
722
766
|
const lines = [];
|
|
767
|
+
const s = result.stats;
|
|
723
768
|
if (result.valid) {
|
|
724
|
-
lines.push(`OK: ${result.filesChecked} files checked, no errors found
|
|
769
|
+
lines.push(`OK: ${result.filesChecked} files checked, no errors found.\n`);
|
|
725
770
|
}
|
|
726
771
|
else {
|
|
727
772
|
const errors = result.issues.filter(i => i.severity === "error");
|
|
728
773
|
const warnings = result.issues.filter(i => i.severity === "warning");
|
|
729
774
|
lines.push(`FAILED: ${errors.length} error(s), ${warnings.length} warning(s) in ${result.filesChecked} files.\n`);
|
|
730
775
|
}
|
|
776
|
+
// Show what was checked
|
|
777
|
+
const fileCounts = [];
|
|
778
|
+
if (s.configs > 0)
|
|
779
|
+
fileCounts.push(`${s.configs} configs`);
|
|
780
|
+
if (s.featureFlags > 0)
|
|
781
|
+
fileCounts.push(`${s.featureFlags} feature flags`);
|
|
782
|
+
if (s.segments > 0)
|
|
783
|
+
fileCounts.push(`${s.segments} segments`);
|
|
784
|
+
if (s.logLevels > 0)
|
|
785
|
+
fileCounts.push(`${s.logLevels} log levels`);
|
|
786
|
+
if (s.schemas > 0)
|
|
787
|
+
fileCounts.push(`${s.schemas} schemas`);
|
|
788
|
+
if (fileCounts.length > 0) {
|
|
789
|
+
lines.push(` Files: ${fileCounts.join(", ")}`);
|
|
790
|
+
}
|
|
791
|
+
const checks = [];
|
|
792
|
+
checks.push(`${result.filesChecked} JSON parsed`);
|
|
793
|
+
checks.push(`${result.filesChecked} schema-validated`);
|
|
794
|
+
checks.push(`${s.uniqueKeysVerified} unique keys`);
|
|
795
|
+
checks.push(`${s.rules} rules checked`);
|
|
796
|
+
if (s.environmentOverrides > 0)
|
|
797
|
+
checks.push(`${s.environmentOverrides} env overrides`);
|
|
798
|
+
if (s.segmentRefsChecked > 0)
|
|
799
|
+
checks.push(`${s.segmentRefsChecked} segment refs`);
|
|
800
|
+
if (s.schemaRefsChecked > 0)
|
|
801
|
+
checks.push(`${s.schemaRefsChecked} schema refs`);
|
|
802
|
+
lines.push(` Checks: ${checks.join(", ")}`);
|
|
731
803
|
// Group issues by file
|
|
732
804
|
const byFile = new Map();
|
|
733
805
|
for (const issue of result.issues) {
|
|
@@ -735,6 +807,9 @@ export function formatResult(result) {
|
|
|
735
807
|
existing.push(issue);
|
|
736
808
|
byFile.set(issue.file, existing);
|
|
737
809
|
}
|
|
810
|
+
if (byFile.size > 0) {
|
|
811
|
+
lines.push("");
|
|
812
|
+
}
|
|
738
813
|
for (const [file, fileIssues] of byFile) {
|
|
739
814
|
lines.push(` ${file}:`);
|
|
740
815
|
for (const issue of fileIssues) {
|
package/oclif.manifest.json
CHANGED
|
@@ -491,6 +491,81 @@
|
|
|
491
491
|
"info.js"
|
|
492
492
|
]
|
|
493
493
|
},
|
|
494
|
+
"init": {
|
|
495
|
+
"aliases": [],
|
|
496
|
+
"args": {
|
|
497
|
+
"directory": {
|
|
498
|
+
"default": ".",
|
|
499
|
+
"description": "Target directory (default: current directory)",
|
|
500
|
+
"name": "directory"
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
"description": "Initialize or update a Quonfig workspace",
|
|
504
|
+
"examples": [
|
|
505
|
+
"<%= config.bin %> init",
|
|
506
|
+
"<%= config.bin %> init ./my-workspace",
|
|
507
|
+
"<%= config.bin %> init --no-samples",
|
|
508
|
+
"<%= config.bin %> init --samples",
|
|
509
|
+
"<%= config.bin %> init --dry-run"
|
|
510
|
+
],
|
|
511
|
+
"flags": {
|
|
512
|
+
"json": {
|
|
513
|
+
"description": "Format output as json.",
|
|
514
|
+
"helpGroup": "GLOBAL",
|
|
515
|
+
"name": "json",
|
|
516
|
+
"allowNo": false,
|
|
517
|
+
"type": "boolean"
|
|
518
|
+
},
|
|
519
|
+
"interactive": {
|
|
520
|
+
"description": "Force interactive mode",
|
|
521
|
+
"helpGroup": "GLOBAL",
|
|
522
|
+
"name": "interactive",
|
|
523
|
+
"allowNo": true,
|
|
524
|
+
"type": "boolean"
|
|
525
|
+
},
|
|
526
|
+
"no-color": {
|
|
527
|
+
"description": "Do not colorize output",
|
|
528
|
+
"env": "NO_COLOR",
|
|
529
|
+
"helpGroup": "GLOBAL",
|
|
530
|
+
"name": "no-color",
|
|
531
|
+
"allowNo": false,
|
|
532
|
+
"type": "boolean"
|
|
533
|
+
},
|
|
534
|
+
"verbose": {
|
|
535
|
+
"description": "Verbose output",
|
|
536
|
+
"helpGroup": "GLOBAL",
|
|
537
|
+
"name": "verbose",
|
|
538
|
+
"allowNo": false,
|
|
539
|
+
"type": "boolean"
|
|
540
|
+
},
|
|
541
|
+
"dry-run": {
|
|
542
|
+
"description": "Show what would be done without writing anything",
|
|
543
|
+
"name": "dry-run",
|
|
544
|
+
"allowNo": false,
|
|
545
|
+
"type": "boolean"
|
|
546
|
+
},
|
|
547
|
+
"samples": {
|
|
548
|
+
"description": "Include sample configs (default: yes on first init, no on update)",
|
|
549
|
+
"name": "samples",
|
|
550
|
+
"allowNo": true,
|
|
551
|
+
"type": "boolean"
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
"hasDynamicHelp": false,
|
|
555
|
+
"hiddenAliases": [],
|
|
556
|
+
"id": "init",
|
|
557
|
+
"pluginAlias": "@quonfig/cli",
|
|
558
|
+
"pluginName": "@quonfig/cli",
|
|
559
|
+
"pluginType": "core",
|
|
560
|
+
"strict": true,
|
|
561
|
+
"enableJsonFlag": true,
|
|
562
|
+
"isESM": true,
|
|
563
|
+
"relativePath": [
|
|
564
|
+
"dist",
|
|
565
|
+
"commands",
|
|
566
|
+
"init.js"
|
|
567
|
+
]
|
|
568
|
+
},
|
|
494
569
|
"interactive": {
|
|
495
570
|
"aliases": [],
|
|
496
571
|
"args": {},
|
|
@@ -1425,5 +1500,5 @@
|
|
|
1425
1500
|
]
|
|
1426
1501
|
}
|
|
1427
1502
|
},
|
|
1428
|
-
"version": "0.0.
|
|
1503
|
+
"version": "0.0.2"
|
|
1429
1504
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packageManager": "yarn@4.11.0+sha512.4e54aeace9141df2f0177c266b05ec50dc044638157dae128c471ba65994ac802122d7ab35bcd9e81641228b7dcf24867d28e750e0bcae8a05277d600008ad54",
|
|
3
3
|
"name": "@quonfig/cli",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.2",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/quonfig/cli/issues"
|
|
7
7
|
},
|