schemashift-cli 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +881 -305
- package/dist/index.cjs +2 -2
- package/dist/index.js +2 -2
- package/package.json +11 -11
package/dist/cli.js
CHANGED
|
@@ -6,23 +6,27 @@ import {
|
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
init_esm_shims();
|
|
9
|
-
import { existsSync as
|
|
10
|
-
import { dirname as dirname2, join as
|
|
9
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, statSync, writeFileSync as writeFileSync4 } from "fs";
|
|
10
|
+
import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
import {
|
|
13
13
|
ApprovalManager,
|
|
14
14
|
BehavioralWarningAnalyzer,
|
|
15
15
|
BundleEstimator,
|
|
16
16
|
CompatibilityAnalyzer,
|
|
17
|
+
createVerificationReport,
|
|
17
18
|
DetailedAnalyzer,
|
|
18
19
|
detectFormLibraries,
|
|
20
|
+
extractSchemaNames,
|
|
21
|
+
formatVerificationReport,
|
|
19
22
|
GovernanceEngine,
|
|
20
23
|
GovernanceFixer,
|
|
21
24
|
GraphExporter,
|
|
25
|
+
generateSamples,
|
|
22
26
|
getAllMigrationTemplates,
|
|
23
27
|
getMigrationTemplate,
|
|
24
28
|
IncrementalTracker,
|
|
25
|
-
loadConfig,
|
|
29
|
+
loadConfig as loadConfig2,
|
|
26
30
|
MigrationAuditLog,
|
|
27
31
|
MigrationChain,
|
|
28
32
|
PerformanceAnalyzer,
|
|
@@ -35819,8 +35823,8 @@ var TIER_FEATURES = {
|
|
|
35819
35823
|
"free"
|
|
35820
35824
|
/* FREE */
|
|
35821
35825
|
]: {
|
|
35822
|
-
maxFiles:
|
|
35823
|
-
migrations: ["yup->zod"],
|
|
35826
|
+
maxFiles: 10,
|
|
35827
|
+
migrations: ["yup->zod", "joi->zod"],
|
|
35824
35828
|
devices: 1,
|
|
35825
35829
|
ciSupport: false,
|
|
35826
35830
|
prioritySupport: false,
|
|
@@ -36261,7 +36265,7 @@ import { createValibotToZodHandler, createZodToValibotHandler } from "@schemashi
|
|
|
36261
36265
|
import { Command } from "commander";
|
|
36262
36266
|
import { glob as glob2 } from "glob";
|
|
36263
36267
|
import { Listr } from "listr2";
|
|
36264
|
-
import
|
|
36268
|
+
import pc5 from "picocolors";
|
|
36265
36269
|
|
|
36266
36270
|
// src/backup.ts
|
|
36267
36271
|
init_esm_shims();
|
|
@@ -36512,6 +36516,73 @@ function computeHunks(original, transformed, context = 3) {
|
|
|
36512
36516
|
if (currentHunk) hunks.push(currentHunk);
|
|
36513
36517
|
return hunks;
|
|
36514
36518
|
}
|
|
36519
|
+
function generateUnifiedDiff(results) {
|
|
36520
|
+
const lines = [];
|
|
36521
|
+
for (const result of results) {
|
|
36522
|
+
if (!result.transformedCode || result.originalCode === result.transformedCode) continue;
|
|
36523
|
+
const relativePath = result.filePath.replace(`${process.cwd()}/`, "");
|
|
36524
|
+
lines.push(`--- a/${relativePath}`);
|
|
36525
|
+
lines.push(`+++ b/${relativePath}`);
|
|
36526
|
+
const originalLines = result.originalCode.split("\n");
|
|
36527
|
+
const transformedLines = result.transformedCode.split("\n");
|
|
36528
|
+
const hunks = computeHunks(originalLines, transformedLines);
|
|
36529
|
+
for (const hunk of hunks) {
|
|
36530
|
+
lines.push(
|
|
36531
|
+
`@@ -${hunk.originalStart + 1},${hunk.originalCount} +${hunk.transformedStart + 1},${hunk.transformedCount} @@`
|
|
36532
|
+
);
|
|
36533
|
+
for (const line of hunk.lines) {
|
|
36534
|
+
if (line.type === "remove") {
|
|
36535
|
+
lines.push(`-${line.content}`);
|
|
36536
|
+
} else if (line.type === "add") {
|
|
36537
|
+
lines.push(`+${line.content}`);
|
|
36538
|
+
} else {
|
|
36539
|
+
lines.push(` ${line.content}`);
|
|
36540
|
+
}
|
|
36541
|
+
}
|
|
36542
|
+
}
|
|
36543
|
+
}
|
|
36544
|
+
return lines.join("\n");
|
|
36545
|
+
}
|
|
36546
|
+
function generateDrySummaryTable(results) {
|
|
36547
|
+
const rows = [];
|
|
36548
|
+
for (const result of results) {
|
|
36549
|
+
if (!result.transformedCode || result.originalCode === result.transformedCode) continue;
|
|
36550
|
+
const relativePath = result.filePath.replace(`${process.cwd()}/`, "");
|
|
36551
|
+
const originalLines = result.originalCode.split("\n");
|
|
36552
|
+
const transformedLines = result.transformedCode.split("\n");
|
|
36553
|
+
const hunks = computeHunks(originalLines, transformedLines);
|
|
36554
|
+
let added = 0;
|
|
36555
|
+
let removed = 0;
|
|
36556
|
+
for (const hunk of hunks) {
|
|
36557
|
+
for (const line of hunk.lines) {
|
|
36558
|
+
if (line.type === "add") added++;
|
|
36559
|
+
else if (line.type === "remove") removed++;
|
|
36560
|
+
}
|
|
36561
|
+
}
|
|
36562
|
+
rows.push({ file: relativePath, added, removed, warnings: result.warnings.length });
|
|
36563
|
+
}
|
|
36564
|
+
if (rows.length === 0) return "";
|
|
36565
|
+
const maxFile = Math.max(4, ...rows.map((r) => r.file.length));
|
|
36566
|
+
const header = `${"File".padEnd(maxFile)} ${"Added".padStart(5)} ${"Removed".padStart(7)} ${"Warnings".padStart(8)}`;
|
|
36567
|
+
const separator = "\u2500".repeat(header.length);
|
|
36568
|
+
const lines = [header, separator];
|
|
36569
|
+
let totalAdded = 0;
|
|
36570
|
+
let totalRemoved = 0;
|
|
36571
|
+
let totalWarnings = 0;
|
|
36572
|
+
for (const row of rows) {
|
|
36573
|
+
lines.push(
|
|
36574
|
+
`${row.file.padEnd(maxFile)} ${pc.green(`+${row.added}`.padStart(5))} ${pc.red(`-${row.removed}`.padStart(7))} ${row.warnings > 0 ? pc.yellow(String(row.warnings).padStart(8)) : String(row.warnings).padStart(8)}`
|
|
36575
|
+
);
|
|
36576
|
+
totalAdded += row.added;
|
|
36577
|
+
totalRemoved += row.removed;
|
|
36578
|
+
totalWarnings += row.warnings;
|
|
36579
|
+
}
|
|
36580
|
+
lines.push(separator);
|
|
36581
|
+
lines.push(
|
|
36582
|
+
`${`${rows.length} file${rows.length !== 1 ? "s" : ""}`.padEnd(maxFile)} ${pc.green(`+${totalAdded}`.padStart(5))} ${pc.red(`-${totalRemoved}`.padStart(7))} ${totalWarnings > 0 ? pc.yellow(String(totalWarnings).padStart(8)) : String(totalWarnings).padStart(8)}`
|
|
36583
|
+
);
|
|
36584
|
+
return lines.join("\n");
|
|
36585
|
+
}
|
|
36515
36586
|
function generateDiffSummary(results) {
|
|
36516
36587
|
const changed = results.filter(
|
|
36517
36588
|
(r) => r.success && r.transformedCode && r.originalCode !== r.transformedCode
|
|
@@ -36527,25 +36598,287 @@ function generateDiffSummary(results) {
|
|
|
36527
36598
|
return parts.join(", ");
|
|
36528
36599
|
}
|
|
36529
36600
|
|
|
36530
|
-
// src/
|
|
36601
|
+
// src/doctor.ts
|
|
36531
36602
|
init_esm_shims();
|
|
36532
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
36603
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
36604
|
+
import { join as join3 } from "path";
|
|
36605
|
+
import { loadConfig } from "@schemashift/core";
|
|
36533
36606
|
import pc2 from "picocolors";
|
|
36607
|
+
var SCHEMA_LIBRARIES = {
|
|
36608
|
+
zod: { current: "3.24", name: "Zod" },
|
|
36609
|
+
yup: { current: "1.6", name: "Yup" },
|
|
36610
|
+
joi: { current: "17.13", name: "Joi" },
|
|
36611
|
+
"io-ts": { current: "2.2", name: "io-ts" },
|
|
36612
|
+
valibot: { current: "1.0", name: "Valibot" },
|
|
36613
|
+
arktype: { current: "2.1", name: "ArkType" },
|
|
36614
|
+
superstruct: { current: "2.0", name: "Superstruct" },
|
|
36615
|
+
"@effect/schema": { current: "0.75", name: "Effect Schema" }
|
|
36616
|
+
};
|
|
36617
|
+
var FORM_LIBRARIES = {
|
|
36618
|
+
"react-hook-form": "React Hook Form",
|
|
36619
|
+
formik: "Formik",
|
|
36620
|
+
"@mantine/form": "Mantine Form"
|
|
36621
|
+
};
|
|
36622
|
+
function readPackageJson(projectPath) {
|
|
36623
|
+
const pkgPath = join3(projectPath, "package.json");
|
|
36624
|
+
if (!existsSync3(pkgPath)) return null;
|
|
36625
|
+
try {
|
|
36626
|
+
return JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
36627
|
+
} catch {
|
|
36628
|
+
return null;
|
|
36629
|
+
}
|
|
36630
|
+
}
|
|
36631
|
+
function getAllDeps(pkg2) {
|
|
36632
|
+
const deps = pkg2.dependencies || {};
|
|
36633
|
+
const devDeps = pkg2.devDependencies || {};
|
|
36634
|
+
return { ...deps, ...devDeps };
|
|
36635
|
+
}
|
|
36636
|
+
function parseVersion(version2) {
|
|
36637
|
+
return version2.replace(/^[\^~>=<]+/, "").split(".").slice(0, 2).join(".");
|
|
36638
|
+
}
|
|
36639
|
+
function checkSchemaLibraries(allDeps) {
|
|
36640
|
+
const checks = [];
|
|
36641
|
+
const detected = [];
|
|
36642
|
+
for (const [lib, info] of Object.entries(SCHEMA_LIBRARIES)) {
|
|
36643
|
+
if (allDeps[lib]) {
|
|
36644
|
+
detected.push(lib);
|
|
36645
|
+
const version2 = parseVersion(allDeps[lib] ?? "");
|
|
36646
|
+
checks.push({
|
|
36647
|
+
status: "pass",
|
|
36648
|
+
message: `${info.name} ${allDeps[lib]} detected`
|
|
36649
|
+
});
|
|
36650
|
+
if (lib === "zod" && version2.startsWith("3.")) {
|
|
36651
|
+
checks.push({
|
|
36652
|
+
status: "warn",
|
|
36653
|
+
message: `${info.name} v3 detected \u2014 v4 is available with performance improvements`,
|
|
36654
|
+
suggestion: `schemashift migrate ./src -f zod-v3 -t v4 --dry-run`
|
|
36655
|
+
});
|
|
36656
|
+
}
|
|
36657
|
+
if (lib === "io-ts") {
|
|
36658
|
+
checks.push({
|
|
36659
|
+
status: "info",
|
|
36660
|
+
message: "io-ts is maintained but succeeded by Effect Schema",
|
|
36661
|
+
suggestion: "schemashift migrate ./src -f io-ts -t effect --dry-run"
|
|
36662
|
+
});
|
|
36663
|
+
}
|
|
36664
|
+
}
|
|
36665
|
+
}
|
|
36666
|
+
if (detected.length === 0) {
|
|
36667
|
+
checks.push({
|
|
36668
|
+
status: "warn",
|
|
36669
|
+
message: "No schema validation libraries detected in package.json"
|
|
36670
|
+
});
|
|
36671
|
+
} else if (detected.length > 1) {
|
|
36672
|
+
checks.push({
|
|
36673
|
+
status: "info",
|
|
36674
|
+
message: `Multiple schema libraries detected: ${detected.join(", ")}`,
|
|
36675
|
+
suggestion: "Consider consolidating to a single library with SchemaShift"
|
|
36676
|
+
});
|
|
36677
|
+
}
|
|
36678
|
+
return checks;
|
|
36679
|
+
}
|
|
36680
|
+
function checkFormLibraries(allDeps) {
|
|
36681
|
+
const checks = [];
|
|
36682
|
+
const schemaLibs = Object.keys(SCHEMA_LIBRARIES).filter((lib) => allDeps[lib]);
|
|
36683
|
+
for (const [lib, name] of Object.entries(FORM_LIBRARIES)) {
|
|
36684
|
+
if (!allDeps[lib]) continue;
|
|
36685
|
+
checks.push({
|
|
36686
|
+
status: "pass",
|
|
36687
|
+
message: `${name} detected`
|
|
36688
|
+
});
|
|
36689
|
+
if (lib === "formik") {
|
|
36690
|
+
checks.push({
|
|
36691
|
+
status: "warn",
|
|
36692
|
+
message: "Formik is no longer actively maintained \u2014 consider React Hook Form"
|
|
36693
|
+
});
|
|
36694
|
+
}
|
|
36695
|
+
if (lib === "react-hook-form") {
|
|
36696
|
+
if (allDeps["@hookform/resolvers"]) {
|
|
36697
|
+
checks.push({
|
|
36698
|
+
status: "pass",
|
|
36699
|
+
message: "@hookform/resolvers detected \u2014 form resolver migration supported"
|
|
36700
|
+
});
|
|
36701
|
+
}
|
|
36702
|
+
if (schemaLibs.includes("yup") && !schemaLibs.includes("zod")) {
|
|
36703
|
+
checks.push({
|
|
36704
|
+
status: "info",
|
|
36705
|
+
message: "React Hook Form + Yup detected \u2014 Zod integration is more popular",
|
|
36706
|
+
suggestion: "schemashift migrate ./src -f yup -t zod --dry-run"
|
|
36707
|
+
});
|
|
36708
|
+
}
|
|
36709
|
+
}
|
|
36710
|
+
}
|
|
36711
|
+
return checks;
|
|
36712
|
+
}
|
|
36713
|
+
function checkEcosystemDeps(allDeps) {
|
|
36714
|
+
const checks = [];
|
|
36715
|
+
const ecosystemPkgs = [
|
|
36716
|
+
"drizzle-zod",
|
|
36717
|
+
"@trpc/server",
|
|
36718
|
+
"zod-validation-error",
|
|
36719
|
+
"zod-openapi",
|
|
36720
|
+
"@asteasolutions/zod-to-openapi",
|
|
36721
|
+
"zod-prisma",
|
|
36722
|
+
"zod-prisma-types",
|
|
36723
|
+
"zod-form-data",
|
|
36724
|
+
"zodios",
|
|
36725
|
+
"@ts-rest/core"
|
|
36726
|
+
];
|
|
36727
|
+
for (const pkg2 of ecosystemPkgs) {
|
|
36728
|
+
if (allDeps[pkg2]) {
|
|
36729
|
+
checks.push({
|
|
36730
|
+
status: "info",
|
|
36731
|
+
message: `${pkg2} detected \u2014 may need updates during Zod migration`
|
|
36732
|
+
});
|
|
36733
|
+
}
|
|
36734
|
+
}
|
|
36735
|
+
return checks;
|
|
36736
|
+
}
|
|
36737
|
+
function checkConfig(projectPath) {
|
|
36738
|
+
const checks = [];
|
|
36739
|
+
const configPath = join3(projectPath, ".schemashiftrc.json");
|
|
36740
|
+
if (existsSync3(configPath)) {
|
|
36741
|
+
try {
|
|
36742
|
+
const config2 = loadConfig(projectPath);
|
|
36743
|
+
checks.push({
|
|
36744
|
+
status: "pass",
|
|
36745
|
+
message: "Configuration file .schemashiftrc.json is valid"
|
|
36746
|
+
});
|
|
36747
|
+
if (config2 && typeof config2 === "object" && "governance" in config2) {
|
|
36748
|
+
checks.push({
|
|
36749
|
+
status: "pass",
|
|
36750
|
+
message: "Governance rules configured"
|
|
36751
|
+
});
|
|
36752
|
+
}
|
|
36753
|
+
} catch (err) {
|
|
36754
|
+
checks.push({
|
|
36755
|
+
status: "error",
|
|
36756
|
+
message: `Invalid configuration: ${err instanceof Error ? err.message : String(err)}`,
|
|
36757
|
+
suggestion: "Run: schemashift init --force"
|
|
36758
|
+
});
|
|
36759
|
+
}
|
|
36760
|
+
} else {
|
|
36761
|
+
checks.push({
|
|
36762
|
+
status: "info",
|
|
36763
|
+
message: "No .schemashiftrc.json found \u2014 using defaults",
|
|
36764
|
+
suggestion: "schemashift init"
|
|
36765
|
+
});
|
|
36766
|
+
}
|
|
36767
|
+
return checks;
|
|
36768
|
+
}
|
|
36769
|
+
function checkGitStatus(projectPath) {
|
|
36770
|
+
const checks = [];
|
|
36771
|
+
const gitDir = join3(projectPath, ".git");
|
|
36772
|
+
if (existsSync3(gitDir)) {
|
|
36773
|
+
checks.push({
|
|
36774
|
+
status: "pass",
|
|
36775
|
+
message: "Git repository detected \u2014 backups and rollbacks supported"
|
|
36776
|
+
});
|
|
36777
|
+
} else {
|
|
36778
|
+
checks.push({
|
|
36779
|
+
status: "warn",
|
|
36780
|
+
message: "Not a git repository \u2014 consider initializing git before migrating"
|
|
36781
|
+
});
|
|
36782
|
+
}
|
|
36783
|
+
return checks;
|
|
36784
|
+
}
|
|
36785
|
+
function runDoctor(projectPath) {
|
|
36786
|
+
const pkg2 = readPackageJson(projectPath);
|
|
36787
|
+
const checks = [];
|
|
36788
|
+
const suggestions = [];
|
|
36789
|
+
if (!pkg2) {
|
|
36790
|
+
checks.push({
|
|
36791
|
+
status: "error",
|
|
36792
|
+
message: "No package.json found",
|
|
36793
|
+
suggestion: "Run doctor from the root of your project"
|
|
36794
|
+
});
|
|
36795
|
+
return { checks, suggestions };
|
|
36796
|
+
}
|
|
36797
|
+
checks.push({
|
|
36798
|
+
status: "pass",
|
|
36799
|
+
message: `Project: ${pkg2.name || "unnamed"}`
|
|
36800
|
+
});
|
|
36801
|
+
const allDeps = getAllDeps(pkg2);
|
|
36802
|
+
checks.push(...checkSchemaLibraries(allDeps));
|
|
36803
|
+
checks.push(...checkFormLibraries(allDeps));
|
|
36804
|
+
checks.push(...checkEcosystemDeps(allDeps));
|
|
36805
|
+
checks.push(...checkConfig(projectPath));
|
|
36806
|
+
checks.push(...checkGitStatus(projectPath));
|
|
36807
|
+
for (const check of checks) {
|
|
36808
|
+
if (check.suggestion) {
|
|
36809
|
+
suggestions.push(check.suggestion);
|
|
36810
|
+
}
|
|
36811
|
+
}
|
|
36812
|
+
return { checks, suggestions };
|
|
36813
|
+
}
|
|
36814
|
+
function formatDoctorReport(report) {
|
|
36815
|
+
const lines = [];
|
|
36816
|
+
lines.push(pc2.bold("\nSchemaShift Doctor\n"));
|
|
36817
|
+
const icons = {
|
|
36818
|
+
pass: pc2.green("\u2713"),
|
|
36819
|
+
warn: pc2.yellow("\u26A0"),
|
|
36820
|
+
error: pc2.red("\u2717"),
|
|
36821
|
+
info: pc2.blue("\u2139")
|
|
36822
|
+
};
|
|
36823
|
+
for (const check of report.checks) {
|
|
36824
|
+
lines.push(` ${icons[check.status]} ${check.message}`);
|
|
36825
|
+
}
|
|
36826
|
+
if (report.suggestions.length > 0) {
|
|
36827
|
+
lines.push("");
|
|
36828
|
+
lines.push(pc2.bold(" Suggestions:"));
|
|
36829
|
+
for (const suggestion of report.suggestions) {
|
|
36830
|
+
lines.push(` ${pc2.cyan("\u2192")} ${pc2.dim(suggestion)}`);
|
|
36831
|
+
}
|
|
36832
|
+
}
|
|
36833
|
+
const errorCount = report.checks.filter((c) => c.status === "error").length;
|
|
36834
|
+
const warnCount = report.checks.filter((c) => c.status === "warn").length;
|
|
36835
|
+
lines.push("");
|
|
36836
|
+
if (errorCount > 0) {
|
|
36837
|
+
lines.push(pc2.red(` ${errorCount} error(s) found`));
|
|
36838
|
+
} else if (warnCount > 0) {
|
|
36839
|
+
lines.push(pc2.yellow(` ${warnCount} warning(s) \u2014 project is functional`));
|
|
36840
|
+
} else {
|
|
36841
|
+
lines.push(pc2.green(" Project looks healthy!"));
|
|
36842
|
+
}
|
|
36843
|
+
lines.push("");
|
|
36844
|
+
return lines.join("\n");
|
|
36845
|
+
}
|
|
36846
|
+
function formatDoctorJson(report) {
|
|
36847
|
+
return JSON.stringify(
|
|
36848
|
+
{
|
|
36849
|
+
checks: report.checks,
|
|
36850
|
+
suggestions: report.suggestions,
|
|
36851
|
+
summary: {
|
|
36852
|
+
errors: report.checks.filter((c) => c.status === "error").length,
|
|
36853
|
+
warnings: report.checks.filter((c) => c.status === "warn").length,
|
|
36854
|
+
passed: report.checks.filter((c) => c.status === "pass").length,
|
|
36855
|
+
info: report.checks.filter((c) => c.status === "info").length
|
|
36856
|
+
}
|
|
36857
|
+
},
|
|
36858
|
+
null,
|
|
36859
|
+
2
|
|
36860
|
+
);
|
|
36861
|
+
}
|
|
36862
|
+
|
|
36863
|
+
// src/error-formatter.ts
|
|
36864
|
+
init_esm_shims();
|
|
36865
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
36866
|
+
import pc3 from "picocolors";
|
|
36534
36867
|
function formatErrorSummary(errors) {
|
|
36535
36868
|
const lines = [];
|
|
36536
36869
|
lines.push(
|
|
36537
|
-
|
|
36870
|
+
pc3.red(pc3.bold(`
|
|
36538
36871
|
${errors.length} file${errors.length !== 1 ? "s" : ""} with errors:
|
|
36539
36872
|
`))
|
|
36540
36873
|
);
|
|
36541
36874
|
for (const { filePath, errors: fileErrors } of errors) {
|
|
36542
36875
|
const relativePath = filePath.replace(`${process.cwd()}/`, "");
|
|
36543
|
-
lines.push(
|
|
36876
|
+
lines.push(pc3.red(` ${relativePath}`));
|
|
36544
36877
|
for (const err of fileErrors) {
|
|
36545
36878
|
const loc = err.line ? `:${err.line}` : "";
|
|
36546
|
-
lines.push(
|
|
36879
|
+
lines.push(pc3.dim(` - ${err.message}${loc}`));
|
|
36547
36880
|
if (err.suggestion) {
|
|
36548
|
-
lines.push(
|
|
36881
|
+
lines.push(pc3.cyan(` \u2192 ${err.suggestion}`));
|
|
36549
36882
|
}
|
|
36550
36883
|
}
|
|
36551
36884
|
}
|
|
@@ -36555,11 +36888,11 @@ ${errors.length} file${errors.length !== 1 ? "s" : ""} with errors:
|
|
|
36555
36888
|
// src/git.ts
|
|
36556
36889
|
init_esm_shims();
|
|
36557
36890
|
import { execFileSync } from "child_process";
|
|
36558
|
-
import { existsSync as
|
|
36891
|
+
import { existsSync as existsSync4 } from "fs";
|
|
36559
36892
|
var GitIntegration = class {
|
|
36560
36893
|
isGitRepo;
|
|
36561
36894
|
constructor() {
|
|
36562
|
-
this.isGitRepo =
|
|
36895
|
+
this.isGitRepo = existsSync4(".git");
|
|
36563
36896
|
}
|
|
36564
36897
|
isAvailable() {
|
|
36565
36898
|
if (!this.isGitRepo) return false;
|
|
@@ -36928,7 +37261,7 @@ import { watch } from "fs";
|
|
|
36928
37261
|
import { relative as relative2 } from "path";
|
|
36929
37262
|
import { glob } from "glob";
|
|
36930
37263
|
import { minimatch } from "minimatch";
|
|
36931
|
-
import
|
|
37264
|
+
import pc4 from "picocolors";
|
|
36932
37265
|
var WatchMode = class {
|
|
36933
37266
|
watchers = [];
|
|
36934
37267
|
debounceTimers = /* @__PURE__ */ new Map();
|
|
@@ -36936,16 +37269,16 @@ var WatchMode = class {
|
|
|
36936
37269
|
const files = await glob(options.patterns, {
|
|
36937
37270
|
ignore: options.exclude
|
|
36938
37271
|
});
|
|
36939
|
-
console.log(
|
|
37272
|
+
console.log(pc4.cyan(`
|
|
36940
37273
|
Watching ${files.length} files for changes...
|
|
36941
37274
|
`));
|
|
36942
37275
|
if (options.dependents && options.dependents.size > 0) {
|
|
36943
37276
|
console.log(
|
|
36944
|
-
|
|
37277
|
+
pc4.dim(`Dependency-aware mode: ${options.dependents.size} files with dependents
|
|
36945
37278
|
`)
|
|
36946
37279
|
);
|
|
36947
37280
|
}
|
|
36948
|
-
console.log(
|
|
37281
|
+
console.log(pc4.dim("Press Ctrl+C to stop\n"));
|
|
36949
37282
|
const directories = new Set(files.map((f) => f.split("/").slice(0, -1).join("/")));
|
|
36950
37283
|
for (const dir of directories) {
|
|
36951
37284
|
const watcher = watch(dir || ".", { recursive: true }, async (_event, filename) => {
|
|
@@ -36961,23 +37294,23 @@ Watching ${files.length} files for changes...
|
|
|
36961
37294
|
this.debounceTimers.set(
|
|
36962
37295
|
fullPath,
|
|
36963
37296
|
setTimeout(async () => {
|
|
36964
|
-
console.log(
|
|
37297
|
+
console.log(pc4.yellow(`
|
|
36965
37298
|
Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
36966
37299
|
try {
|
|
36967
37300
|
await options.onTransform(fullPath);
|
|
36968
|
-
console.log(
|
|
37301
|
+
console.log(pc4.green(`Transformed successfully`));
|
|
36969
37302
|
const dependentFiles = options.dependents?.get(fullPath);
|
|
36970
37303
|
if (dependentFiles && dependentFiles.length > 0) {
|
|
36971
37304
|
console.log(
|
|
36972
|
-
|
|
37305
|
+
pc4.cyan(` Re-transforming ${dependentFiles.length} dependent file(s)...`)
|
|
36973
37306
|
);
|
|
36974
37307
|
for (const depFile of dependentFiles) {
|
|
36975
37308
|
try {
|
|
36976
37309
|
await options.onTransform(depFile);
|
|
36977
|
-
console.log(
|
|
37310
|
+
console.log(pc4.green(` \u2713 ${relative2(process.cwd(), depFile)}`));
|
|
36978
37311
|
} catch (error) {
|
|
36979
37312
|
console.error(
|
|
36980
|
-
|
|
37313
|
+
pc4.red(
|
|
36981
37314
|
` \u2717 ${relative2(process.cwd(), depFile)}: ${error instanceof Error ? error.message : String(error)}`
|
|
36982
37315
|
)
|
|
36983
37316
|
);
|
|
@@ -36987,7 +37320,7 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
|
36987
37320
|
console.log("");
|
|
36988
37321
|
} catch (error) {
|
|
36989
37322
|
console.error(
|
|
36990
|
-
|
|
37323
|
+
pc4.red(
|
|
36991
37324
|
`Transform failed: ${error instanceof Error ? error.message : String(error)}
|
|
36992
37325
|
`
|
|
36993
37326
|
)
|
|
@@ -37034,7 +37367,7 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
|
37034
37367
|
|
|
37035
37368
|
// src/cli.ts
|
|
37036
37369
|
var __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
37037
|
-
var pkg = JSON.parse(
|
|
37370
|
+
var pkg = JSON.parse(readFileSync5(join4(__dirname2, "..", "package.json"), "utf-8"));
|
|
37038
37371
|
var program = new Command();
|
|
37039
37372
|
var licenseManager = new LicenseManager();
|
|
37040
37373
|
var engine = new TransformEngine();
|
|
@@ -37054,8 +37387,8 @@ var POLAR_URL = "https://schemashift.qwady.app";
|
|
|
37054
37387
|
program.name("schemashift").version(pkg.version).description("TypeScript schema migration CLI");
|
|
37055
37388
|
program.command("init").description("Create .schemashiftrc.json config file").option("-f, --force", "Overwrite existing config").action((options) => {
|
|
37056
37389
|
const configPath = ".schemashiftrc.json";
|
|
37057
|
-
if (
|
|
37058
|
-
console.error(
|
|
37390
|
+
if (existsSync5(configPath) && !options.force) {
|
|
37391
|
+
console.error(pc5.red("Config file already exists. Use --force to overwrite."));
|
|
37059
37392
|
process.exit(1);
|
|
37060
37393
|
}
|
|
37061
37394
|
const defaultConfig = {
|
|
@@ -37076,8 +37409,8 @@ program.command("init").description("Create .schemashiftrc.json config file").op
|
|
|
37076
37409
|
ci: false
|
|
37077
37410
|
};
|
|
37078
37411
|
writeFileSync4(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
37079
|
-
console.log(
|
|
37080
|
-
console.log(
|
|
37412
|
+
console.log(pc5.green("Created .schemashiftrc.json"));
|
|
37413
|
+
console.log(pc5.dim("Edit the file to customize your migration settings."));
|
|
37081
37414
|
});
|
|
37082
37415
|
program.command("analyze <path>").description("Analyze schemas in your project").option("--json", "Output as JSON").option("-v, --verbose", "Show detailed analysis").option("--detailed", "Show complexity analysis (Individual+)").option("--readiness <migration>", "Check migration readiness, e.g. yup->zod (Individual+)").option("--complexity", "Show per-schema complexity scores (Individual+)").option("--behavioral <migration>", "Show behavioral difference warnings, e.g. yup->zod").option("--bundle <migration>", "Show bundle size estimation, e.g. zod->valibot (Individual+)").option(
|
|
37083
37416
|
"--performance <migration>",
|
|
@@ -37085,10 +37418,10 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37085
37418
|
).option("--dedup", "Detect duplicate type definitions (Individual+)").action(async (targetPath, options) => {
|
|
37086
37419
|
const analyzer = new SchemaAnalyzer();
|
|
37087
37420
|
let files;
|
|
37088
|
-
if (
|
|
37421
|
+
if (existsSync5(targetPath) && statSync(targetPath).isFile()) {
|
|
37089
37422
|
files = [resolve2(targetPath)];
|
|
37090
37423
|
} else {
|
|
37091
|
-
files = await glob2(
|
|
37424
|
+
files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
37092
37425
|
ignore: ["**/node_modules/**", "**/dist/**"]
|
|
37093
37426
|
});
|
|
37094
37427
|
}
|
|
@@ -37101,8 +37434,8 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37101
37434
|
const validation = await licenseManager.validate();
|
|
37102
37435
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37103
37436
|
if (!features.advancedAnalysis) {
|
|
37104
|
-
console.error(
|
|
37105
|
-
console.log(`Upgrade at: ${
|
|
37437
|
+
console.error(pc5.red("Advanced analysis requires Individual tier or higher."));
|
|
37438
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37106
37439
|
process.exit(1);
|
|
37107
37440
|
}
|
|
37108
37441
|
}
|
|
@@ -37110,12 +37443,12 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37110
37443
|
console.log(JSON.stringify(result, null, 2));
|
|
37111
37444
|
return;
|
|
37112
37445
|
}
|
|
37113
|
-
console.log(
|
|
37114
|
-
console.log(`Total files: ${
|
|
37115
|
-
console.log(`Files with schemas: ${
|
|
37116
|
-
console.log(`Total schemas: ${
|
|
37446
|
+
console.log(pc5.bold("\nSchema Analysis\n"));
|
|
37447
|
+
console.log(`Total files: ${pc5.cyan(result.totalFiles.toString())}`);
|
|
37448
|
+
console.log(`Files with schemas: ${pc5.cyan(result.filesWithSchemas.toString())}`);
|
|
37449
|
+
console.log(`Total schemas: ${pc5.cyan(result.schemas.length.toString())}`);
|
|
37117
37450
|
if (result.schemas.length > 0) {
|
|
37118
|
-
console.log(
|
|
37451
|
+
console.log(pc5.bold("\nSchemas by library:\n"));
|
|
37119
37452
|
const byLibrary = /* @__PURE__ */ new Map();
|
|
37120
37453
|
for (const schema of result.schemas) {
|
|
37121
37454
|
byLibrary.set(schema.library, (byLibrary.get(schema.library) || 0) + 1);
|
|
@@ -37124,13 +37457,13 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37124
37457
|
console.log(` ${lib}: ${count}`);
|
|
37125
37458
|
}
|
|
37126
37459
|
if (options.verbose) {
|
|
37127
|
-
console.log(
|
|
37460
|
+
console.log(pc5.bold("\nDetailed schemas:\n"));
|
|
37128
37461
|
for (const schema of result.schemas.slice(0, 20)) {
|
|
37129
|
-
console.log(` ${
|
|
37130
|
-
console.log(` ${
|
|
37462
|
+
console.log(` ${pc5.cyan(schema.name)} (${schema.library})`);
|
|
37463
|
+
console.log(` ${pc5.dim(schema.filePath)}:${schema.lineNumber}`);
|
|
37131
37464
|
}
|
|
37132
37465
|
if (result.schemas.length > 20) {
|
|
37133
|
-
console.log(
|
|
37466
|
+
console.log(pc5.dim(` ... and ${result.schemas.length - 20} more`));
|
|
37134
37467
|
}
|
|
37135
37468
|
}
|
|
37136
37469
|
}
|
|
@@ -37141,43 +37474,43 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37141
37474
|
const projectPath = resolve2(targetPath);
|
|
37142
37475
|
const detailedResult = detailedAnalyzer.generateDetailedResult(
|
|
37143
37476
|
complexities,
|
|
37144
|
-
|
|
37477
|
+
existsSync5(join4(projectPath, "package.json")) ? projectPath : process.cwd()
|
|
37145
37478
|
);
|
|
37146
37479
|
if (options.json) {
|
|
37147
37480
|
console.log(JSON.stringify(detailedResult, null, 2));
|
|
37148
37481
|
return;
|
|
37149
37482
|
}
|
|
37150
|
-
console.log(
|
|
37151
|
-
console.log(`Average complexity: ${
|
|
37152
|
-
console.log(`Max complexity: ${
|
|
37483
|
+
console.log(pc5.bold("\nComplexity Analysis\n"));
|
|
37484
|
+
console.log(`Average complexity: ${pc5.cyan(detailedResult.averageComplexity.toString())}`);
|
|
37485
|
+
console.log(`Max complexity: ${pc5.cyan(detailedResult.maxComplexity.toString())}`);
|
|
37153
37486
|
if (detailedResult.libraryVersions.length > 0) {
|
|
37154
|
-
console.log(
|
|
37487
|
+
console.log(pc5.bold("\nDetected library versions:\n"));
|
|
37155
37488
|
for (const v of detailedResult.libraryVersions) {
|
|
37156
|
-
console.log(` ${v.library}: ${
|
|
37489
|
+
console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
|
|
37157
37490
|
}
|
|
37158
37491
|
}
|
|
37159
37492
|
if (options.complexity || options.detailed) {
|
|
37160
37493
|
const sorted = [...complexities].sort((a, b) => b.score - a.score);
|
|
37161
|
-
console.log(
|
|
37494
|
+
console.log(pc5.bold("\nPer-schema complexity:\n"));
|
|
37162
37495
|
for (const c of sorted.slice(0, 20)) {
|
|
37163
|
-
const levelColor = c.level === "critical" ?
|
|
37496
|
+
const levelColor = c.level === "critical" ? pc5.red : c.level === "high" ? pc5.yellow : c.level === "medium" ? pc5.blue : pc5.green;
|
|
37164
37497
|
console.log(
|
|
37165
|
-
` ${
|
|
37498
|
+
` ${pc5.cyan(c.schemaName)} ${levelColor(`[${c.level}]`)} score=${c.score}`
|
|
37166
37499
|
);
|
|
37167
37500
|
console.log(
|
|
37168
37501
|
` chain=${c.chainLength} nested=${c.nestedDepth} validations=${c.validationCount}`
|
|
37169
37502
|
);
|
|
37170
|
-
console.log(` ${
|
|
37503
|
+
console.log(` ${pc5.dim(c.filePath)}:${c.lineNumber}`);
|
|
37171
37504
|
}
|
|
37172
37505
|
if (sorted.length > 20) {
|
|
37173
|
-
console.log(
|
|
37506
|
+
console.log(pc5.dim(` ... and ${sorted.length - 20} more`));
|
|
37174
37507
|
}
|
|
37175
37508
|
}
|
|
37176
37509
|
}
|
|
37177
37510
|
if (options.readiness) {
|
|
37178
37511
|
const parts = options.readiness.split("->");
|
|
37179
37512
|
if (parts.length !== 2) {
|
|
37180
|
-
console.error(
|
|
37513
|
+
console.error(pc5.red("Invalid readiness format. Use: --readiness yup->zod"));
|
|
37181
37514
|
process.exit(1);
|
|
37182
37515
|
}
|
|
37183
37516
|
const [from, to] = parts;
|
|
@@ -37244,21 +37577,21 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37244
37577
|
console.log(JSON.stringify(readiness, null, 2));
|
|
37245
37578
|
return;
|
|
37246
37579
|
}
|
|
37247
|
-
const riskColor = readiness.riskLevel === "critical" ?
|
|
37248
|
-
console.log(
|
|
37580
|
+
const riskColor = readiness.riskLevel === "critical" ? pc5.red : readiness.riskLevel === "high" ? pc5.yellow : readiness.riskLevel === "medium" ? pc5.blue : pc5.green;
|
|
37581
|
+
console.log(pc5.bold(`
|
|
37249
37582
|
Migration Readiness: ${from} -> ${to}
|
|
37250
37583
|
`));
|
|
37251
37584
|
console.log(`Readiness: ${riskColor(`${readiness.readinessPercent}%`)}`);
|
|
37252
37585
|
console.log(`Risk level: ${riskColor(readiness.riskLevel)}`);
|
|
37253
37586
|
console.log(`Total schemas: ${readiness.totalSchemas}`);
|
|
37254
|
-
console.log(`Supported: ${
|
|
37587
|
+
console.log(`Supported: ${pc5.green(readiness.supportedSchemas.toString())}`);
|
|
37255
37588
|
console.log(
|
|
37256
|
-
`Estimated manual fixes: ${
|
|
37589
|
+
`Estimated manual fixes: ${pc5.yellow(readiness.estimatedManualFixes.toString())}`
|
|
37257
37590
|
);
|
|
37258
37591
|
if (readiness.unsupportedPatterns.length > 0) {
|
|
37259
|
-
console.log(
|
|
37592
|
+
console.log(pc5.bold("\nUnsupported patterns:\n"));
|
|
37260
37593
|
for (const p of readiness.unsupportedPatterns) {
|
|
37261
|
-
console.log(` ${
|
|
37594
|
+
console.log(` ${pc5.red(p)}`);
|
|
37262
37595
|
}
|
|
37263
37596
|
}
|
|
37264
37597
|
}
|
|
@@ -37266,7 +37599,7 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37266
37599
|
if (options.behavioral) {
|
|
37267
37600
|
const migParts = options.behavioral.split("->");
|
|
37268
37601
|
if (migParts.length !== 2) {
|
|
37269
|
-
console.error(
|
|
37602
|
+
console.error(pc5.red("Invalid format. Use: --behavioral yup->zod"));
|
|
37270
37603
|
} else {
|
|
37271
37604
|
const [bFrom, bTo] = migParts;
|
|
37272
37605
|
const behavioralAnalyzer = new BehavioralWarningAnalyzer();
|
|
@@ -37277,7 +37610,7 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37277
37610
|
bTo
|
|
37278
37611
|
);
|
|
37279
37612
|
if (behavioralResult.warnings.length > 0) {
|
|
37280
|
-
console.log(
|
|
37613
|
+
console.log(pc5.bold("\nBehavioral Difference Warnings\n"));
|
|
37281
37614
|
const byCategory = /* @__PURE__ */ new Map();
|
|
37282
37615
|
for (const w of behavioralResult.warnings) {
|
|
37283
37616
|
const existing = byCategory.get(w.category) ?? [];
|
|
@@ -37285,17 +37618,17 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37285
37618
|
byCategory.set(w.category, existing);
|
|
37286
37619
|
}
|
|
37287
37620
|
for (const [category, catWarnings] of byCategory) {
|
|
37288
|
-
console.log(
|
|
37621
|
+
console.log(pc5.yellow(` [${category}] ${catWarnings.length} warning(s)`));
|
|
37289
37622
|
for (const w of catWarnings.slice(0, 5)) {
|
|
37290
37623
|
console.log(` ${w.message}`);
|
|
37291
|
-
console.log(` ${
|
|
37624
|
+
console.log(` ${pc5.dim(w.detail)}`);
|
|
37292
37625
|
}
|
|
37293
37626
|
if (catWarnings.length > 5) {
|
|
37294
|
-
console.log(
|
|
37627
|
+
console.log(pc5.dim(` ... and ${catWarnings.length - 5} more`));
|
|
37295
37628
|
}
|
|
37296
37629
|
}
|
|
37297
37630
|
} else {
|
|
37298
|
-
console.log(
|
|
37631
|
+
console.log(pc5.green("\nNo behavioral difference warnings found."));
|
|
37299
37632
|
}
|
|
37300
37633
|
}
|
|
37301
37634
|
}
|
|
@@ -37303,12 +37636,12 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37303
37636
|
const validation = await licenseManager.validate();
|
|
37304
37637
|
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37305
37638
|
if (!tier.bundleEstimator) {
|
|
37306
|
-
console.error(
|
|
37307
|
-
console.log(`Upgrade at: ${
|
|
37639
|
+
console.error(pc5.red("\nBundle estimation requires Individual tier or higher."));
|
|
37640
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37308
37641
|
} else {
|
|
37309
37642
|
const migParts = options.bundle.split("->");
|
|
37310
37643
|
if (migParts.length !== 2) {
|
|
37311
|
-
console.error(
|
|
37644
|
+
console.error(pc5.red("Invalid format. Use: --bundle zod->valibot"));
|
|
37312
37645
|
} else {
|
|
37313
37646
|
const [bFrom, bTo] = migParts;
|
|
37314
37647
|
const bundleEstimator = new BundleEstimator();
|
|
@@ -37318,16 +37651,16 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37318
37651
|
bFrom,
|
|
37319
37652
|
bTo
|
|
37320
37653
|
);
|
|
37321
|
-
console.log(
|
|
37654
|
+
console.log(pc5.bold("\nBundle Size Estimation\n"));
|
|
37322
37655
|
console.log(
|
|
37323
|
-
` ${estimate.from.library}: ~${
|
|
37656
|
+
` ${estimate.from.library}: ~${pc5.cyan(`${estimate.from.minifiedGzipKb}kB`)}`
|
|
37324
37657
|
);
|
|
37325
|
-
console.log(` ${estimate.to.library}: ~${
|
|
37658
|
+
console.log(` ${estimate.to.library}: ~${pc5.cyan(`${estimate.to.minifiedGzipKb}kB`)}`);
|
|
37326
37659
|
console.log(
|
|
37327
|
-
` Delta: ${
|
|
37660
|
+
` Delta: ${pc5.yellow(`${estimate.estimatedDelta > 0 ? "+" : ""}${estimate.estimatedDelta.toFixed(1)}kB (${estimate.deltaPercent.toFixed(0)}%)`)}`
|
|
37328
37661
|
);
|
|
37329
37662
|
console.log(`
|
|
37330
|
-
${
|
|
37663
|
+
${pc5.dim(estimate.summary)}`);
|
|
37331
37664
|
}
|
|
37332
37665
|
}
|
|
37333
37666
|
}
|
|
@@ -37335,12 +37668,12 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37335
37668
|
const validation = await licenseManager.validate();
|
|
37336
37669
|
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37337
37670
|
if (!tier.performanceAnalyzer) {
|
|
37338
|
-
console.error(
|
|
37339
|
-
console.log(`Upgrade at: ${
|
|
37671
|
+
console.error(pc5.red("\nPerformance analysis requires Individual tier or higher."));
|
|
37672
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37340
37673
|
} else {
|
|
37341
37674
|
const migParts = options.performance.split("->");
|
|
37342
37675
|
if (migParts.length !== 2) {
|
|
37343
|
-
console.error(
|
|
37676
|
+
console.error(pc5.red("Invalid format. Use: --performance zod-v3->v4"));
|
|
37344
37677
|
} else {
|
|
37345
37678
|
const [pFrom, pTo] = migParts;
|
|
37346
37679
|
const perfAnalyzer = new PerformanceAnalyzer();
|
|
@@ -37351,16 +37684,16 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37351
37684
|
pTo
|
|
37352
37685
|
);
|
|
37353
37686
|
if (perfResult.warnings.length > 0) {
|
|
37354
|
-
console.log(
|
|
37687
|
+
console.log(pc5.bold("\nPerformance Impact Analysis\n"));
|
|
37355
37688
|
for (const w of perfResult.warnings) {
|
|
37356
|
-
const color = w.severity === "error" ?
|
|
37689
|
+
const color = w.severity === "error" ? pc5.red : w.severity === "warning" ? pc5.yellow : pc5.dim;
|
|
37357
37690
|
console.log(` ${color(`[${w.severity}]`)} ${w.message}`);
|
|
37358
|
-
console.log(` ${
|
|
37691
|
+
console.log(` ${pc5.dim(w.detail)}`);
|
|
37359
37692
|
}
|
|
37360
37693
|
console.log(`
|
|
37361
|
-
${
|
|
37694
|
+
${pc5.dim(perfResult.recommendation)}`);
|
|
37362
37695
|
} else {
|
|
37363
|
-
console.log(
|
|
37696
|
+
console.log(pc5.green("\nNo performance concerns detected."));
|
|
37364
37697
|
}
|
|
37365
37698
|
}
|
|
37366
37699
|
}
|
|
@@ -37369,25 +37702,25 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37369
37702
|
const validation = await licenseManager.validate();
|
|
37370
37703
|
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37371
37704
|
if (!tier.typeDeduplication) {
|
|
37372
|
-
console.error(
|
|
37373
|
-
console.log(`Upgrade at: ${
|
|
37705
|
+
console.error(pc5.red("\nType deduplication detection requires Individual tier or higher."));
|
|
37706
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37374
37707
|
} else {
|
|
37375
37708
|
const dedupDetector = new TypeDedupDetector();
|
|
37376
37709
|
const sourceFiles = analyzer.getProject().getSourceFiles();
|
|
37377
37710
|
const dedupResult = dedupDetector.detect(sourceFiles);
|
|
37378
37711
|
if (dedupResult.candidates.length > 0) {
|
|
37379
|
-
console.log(
|
|
37712
|
+
console.log(pc5.bold("\nDuplicate Type Candidates\n"));
|
|
37380
37713
|
for (const c of dedupResult.candidates.slice(0, 10)) {
|
|
37381
|
-
console.log(` ${
|
|
37382
|
-
console.log(` Type: ${
|
|
37383
|
-
console.log(` Schema: ${
|
|
37384
|
-
console.log(` Confidence: ${
|
|
37714
|
+
console.log(` ${pc5.cyan(c.typeName)} <-> ${pc5.cyan(c.schemaName)}`);
|
|
37715
|
+
console.log(` Type: ${pc5.dim(c.typeFilePath)}:${c.typeLineNumber}`);
|
|
37716
|
+
console.log(` Schema: ${pc5.dim(c.schemaFilePath)}:${c.schemaLineNumber}`);
|
|
37717
|
+
console.log(` Confidence: ${pc5.dim(c.confidence)} | ${pc5.dim(c.suggestion)}`);
|
|
37385
37718
|
}
|
|
37386
37719
|
if (dedupResult.candidates.length > 10) {
|
|
37387
|
-
console.log(
|
|
37720
|
+
console.log(pc5.dim(` ... and ${dedupResult.candidates.length - 10} more`));
|
|
37388
37721
|
}
|
|
37389
37722
|
} else {
|
|
37390
|
-
console.log(
|
|
37723
|
+
console.log(pc5.green("\nNo duplicate type candidates found."));
|
|
37391
37724
|
}
|
|
37392
37725
|
}
|
|
37393
37726
|
}
|
|
@@ -37396,19 +37729,23 @@ program.command("migrate <path>").description("Migrate schemas from one library
|
|
|
37396
37729
|
"--max-risk-score <score>",
|
|
37397
37730
|
"Exit 1 if any file exceeds risk score (Team+)",
|
|
37398
37731
|
Number.parseInt
|
|
37399
|
-
).option("--incremental", "Enable incremental migration (file-by-file with progress tracking)").option("--resume", "Resume a previously started incremental migration").option("--incremental-status", "Show incremental migration progress").option("--scaffold-tests", "Generate validation tests after migration (Pro+)").option("--audit", "Enable migration audit logging (Team+)").option("--preset <name>", "Use a migration preset template (Individual+)").
|
|
37732
|
+
).option("--incremental", "Enable incremental migration (file-by-file with progress tracking)").option("--resume", "Resume a previously started incremental migration").option("--incremental-status", "Show incremental migration progress").option("--scaffold-tests", "Generate validation tests after migration (Pro+)").option("--audit", "Enable migration audit logging (Team+)").option("--preset <name>", "Use a migration preset template (Individual+)").option("--output-diff <file>", "Write unified diff to file (use - for stdout)").option(
|
|
37733
|
+
"--canary <percent>",
|
|
37734
|
+
"Canary migration: migrate X% of files first (Pro+)",
|
|
37735
|
+
Number.parseInt
|
|
37736
|
+
).option("--compliance-report <format>", "Generate compliance report: soc2, hipaa (Team+)").action(async (targetPath, options) => {
|
|
37400
37737
|
const startTime = Date.now();
|
|
37401
37738
|
if (options.preset) {
|
|
37402
37739
|
const validation2 = await licenseManager.validate();
|
|
37403
37740
|
const features2 = validation2.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37404
37741
|
if (!features2.advancedAnalysis) {
|
|
37405
|
-
console.error(
|
|
37406
|
-
console.log(`Upgrade at: ${
|
|
37742
|
+
console.error(pc5.red("Migration presets require Individual tier or higher."));
|
|
37743
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37407
37744
|
process.exit(1);
|
|
37408
37745
|
}
|
|
37409
37746
|
const preset = getMigrationTemplate(options.preset);
|
|
37410
37747
|
if (!preset) {
|
|
37411
|
-
console.error(
|
|
37748
|
+
console.error(pc5.red(`Unknown preset: ${options.preset}`));
|
|
37412
37749
|
const names = getAllMigrationTemplates().map((t) => t.name);
|
|
37413
37750
|
console.log(`Available presets: ${names.join(", ")}`);
|
|
37414
37751
|
process.exit(1);
|
|
@@ -37416,42 +37753,42 @@ program.command("migrate <path>").description("Migrate schemas from one library
|
|
|
37416
37753
|
const step = preset.migrationSteps[0];
|
|
37417
37754
|
if (step && !options.from) options.from = step.from;
|
|
37418
37755
|
if (step && !options.to) options.to = step.to;
|
|
37419
|
-
console.log(
|
|
37756
|
+
console.log(pc5.bold(`
|
|
37420
37757
|
Using preset: ${preset.name}`));
|
|
37421
|
-
console.log(
|
|
37758
|
+
console.log(pc5.dim(preset.description));
|
|
37422
37759
|
if (preset.preChecks.length > 0) {
|
|
37423
|
-
console.log(
|
|
37760
|
+
console.log(pc5.bold("\nPre-migration checklist:"));
|
|
37424
37761
|
for (const check of preset.preChecks) {
|
|
37425
|
-
console.log(` ${
|
|
37762
|
+
console.log(` ${pc5.yellow("\u2022")} ${check.description}`);
|
|
37426
37763
|
}
|
|
37427
37764
|
}
|
|
37428
37765
|
if (preset.packageChanges.length > 0) {
|
|
37429
|
-
console.log(
|
|
37766
|
+
console.log(pc5.bold("\nRecommended package changes:"));
|
|
37430
37767
|
for (const change of preset.packageChanges) {
|
|
37431
37768
|
const verb = change.action === "install" ? "+" : change.action === "remove" ? "-" : "\u2191";
|
|
37432
37769
|
console.log(
|
|
37433
|
-
` ${
|
|
37770
|
+
` ${pc5.cyan(verb)} ${change.package}${change.version ? `@${change.version}` : ""}`
|
|
37434
37771
|
);
|
|
37435
37772
|
}
|
|
37436
37773
|
}
|
|
37437
37774
|
console.log("");
|
|
37438
37775
|
}
|
|
37439
37776
|
if (!options.chain && (!options.from || !options.to)) {
|
|
37440
|
-
console.error(
|
|
37441
|
-
console.error(
|
|
37442
|
-
console.error(
|
|
37777
|
+
console.error(pc5.red("Either --from and --to, or --chain must be specified."));
|
|
37778
|
+
console.error(pc5.dim(" Single-step: schemashift migrate <path> -f yup -t zod"));
|
|
37779
|
+
console.error(pc5.dim(" Chain: schemashift migrate <path> --chain yup->zod->valibot"));
|
|
37443
37780
|
process.exit(1);
|
|
37444
37781
|
}
|
|
37445
37782
|
if (options.maxRiskScore !== void 0 && Number.isNaN(options.maxRiskScore)) {
|
|
37446
|
-
console.error(
|
|
37783
|
+
console.error(pc5.red("--max-risk-score must be a valid number."));
|
|
37447
37784
|
process.exit(1);
|
|
37448
37785
|
}
|
|
37449
37786
|
let config2;
|
|
37450
37787
|
try {
|
|
37451
|
-
config2 = await
|
|
37788
|
+
config2 = await loadConfig2(options.config);
|
|
37452
37789
|
} catch (error) {
|
|
37453
37790
|
const msg = error instanceof Error ? error.message : String(error);
|
|
37454
|
-
console.warn(
|
|
37791
|
+
console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
|
|
37455
37792
|
config2 = {
|
|
37456
37793
|
include: ["**/*.ts", "**/*.tsx"],
|
|
37457
37794
|
exclude: ["**/node_modules/**", "**/dist/**"],
|
|
@@ -37461,95 +37798,95 @@ Using preset: ${preset.name}`));
|
|
|
37461
37798
|
}
|
|
37462
37799
|
const validation = await licenseManager.validate();
|
|
37463
37800
|
if (!validation.valid) {
|
|
37464
|
-
console.error(
|
|
37801
|
+
console.error(pc5.red(`License error: ${validation.error}`));
|
|
37465
37802
|
process.exit(1);
|
|
37466
37803
|
}
|
|
37467
37804
|
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
37468
37805
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37469
37806
|
const migrationPath = `${options.from}->${options.to}`;
|
|
37470
37807
|
if (!canUseMigration(tier, options.from, options.to)) {
|
|
37471
|
-
console.error(
|
|
37472
|
-
console.log(`Your tier: ${
|
|
37473
|
-
console.log(`Upgrade at: ${
|
|
37808
|
+
console.error(pc5.red(`Migration ${migrationPath} requires a higher tier.`));
|
|
37809
|
+
console.log(`Your tier: ${pc5.yellow(tier)}`);
|
|
37810
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37474
37811
|
process.exit(1);
|
|
37475
37812
|
}
|
|
37476
37813
|
if (options.ci && !features.ciSupport) {
|
|
37477
|
-
console.error(
|
|
37478
|
-
console.log(`Upgrade at: ${
|
|
37814
|
+
console.error(pc5.red("CI mode requires Pro tier or higher."));
|
|
37815
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37479
37816
|
process.exit(1);
|
|
37480
37817
|
}
|
|
37481
37818
|
const customRulesCount = config2.customRules?.length ?? 0;
|
|
37482
37819
|
if (customRulesCount > features.customRulesLimit) {
|
|
37483
37820
|
console.error(
|
|
37484
|
-
|
|
37821
|
+
pc5.red(
|
|
37485
37822
|
`Custom rules limit exceeded. You have ${customRulesCount} rules, max ${features.customRulesLimit} for ${tier} tier.`
|
|
37486
37823
|
)
|
|
37487
37824
|
);
|
|
37488
|
-
console.log(`Upgrade at: ${
|
|
37825
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37489
37826
|
process.exit(1);
|
|
37490
37827
|
}
|
|
37491
37828
|
if ((options.gitBranch || options.gitCommit) && !features.gitIntegration) {
|
|
37492
|
-
console.error(
|
|
37493
|
-
console.log(`Upgrade at: ${
|
|
37829
|
+
console.error(pc5.red("Git integration requires Individual tier or higher."));
|
|
37830
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37494
37831
|
process.exit(1);
|
|
37495
37832
|
}
|
|
37496
37833
|
if (options.crossFile && !features.crossFileResolution) {
|
|
37497
|
-
console.error(
|
|
37498
|
-
console.log(`Upgrade at: ${
|
|
37834
|
+
console.error(pc5.red("Cross-file resolution requires Pro tier or higher."));
|
|
37835
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37499
37836
|
process.exit(1);
|
|
37500
37837
|
}
|
|
37501
37838
|
if (options.chain && !features.chainMigrations) {
|
|
37502
|
-
console.error(
|
|
37503
|
-
console.log(`Upgrade at: ${
|
|
37839
|
+
console.error(pc5.red("Chain migrations require Pro tier or higher."));
|
|
37840
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37504
37841
|
process.exit(1);
|
|
37505
37842
|
}
|
|
37506
37843
|
if (options.compatCheck && !features.compatCheck) {
|
|
37507
|
-
console.error(
|
|
37508
|
-
console.log(`Upgrade at: ${
|
|
37844
|
+
console.error(pc5.red("Compatibility check requires Pro tier or higher."));
|
|
37845
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37509
37846
|
process.exit(1);
|
|
37510
37847
|
}
|
|
37511
37848
|
if (options.failOnWarnings && !features.failOnWarnings) {
|
|
37512
|
-
console.error(
|
|
37513
|
-
console.log(`Upgrade at: ${
|
|
37849
|
+
console.error(pc5.red("--fail-on-warnings requires Team tier."));
|
|
37850
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37514
37851
|
process.exit(1);
|
|
37515
37852
|
}
|
|
37516
37853
|
if (options.maxRiskScore !== void 0 && !features.maxRiskScoreThreshold) {
|
|
37517
|
-
console.error(
|
|
37518
|
-
console.log(`Upgrade at: ${
|
|
37854
|
+
console.error(pc5.red("--max-risk-score requires Team tier."));
|
|
37855
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37519
37856
|
process.exit(1);
|
|
37520
37857
|
}
|
|
37521
37858
|
if (options.compatCheck) {
|
|
37522
37859
|
const compatAnalyzer = new CompatibilityAnalyzer();
|
|
37523
37860
|
const compatResult = compatAnalyzer.checkCompatibility(resolve2(targetPath));
|
|
37524
|
-
console.log(
|
|
37861
|
+
console.log(pc5.bold("\nCompatibility Check\n"));
|
|
37525
37862
|
if (compatResult.detectedVersions.length > 0) {
|
|
37526
37863
|
for (const v of compatResult.detectedVersions) {
|
|
37527
|
-
console.log(` ${v.library}: ${
|
|
37864
|
+
console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
|
|
37528
37865
|
}
|
|
37529
37866
|
}
|
|
37530
37867
|
console.log(`
|
|
37531
|
-
Compatibility score: ${
|
|
37868
|
+
Compatibility score: ${pc5.cyan(compatResult.overallScore.toString())}%`);
|
|
37532
37869
|
if (compatResult.issues.length > 0) {
|
|
37533
|
-
console.log(
|
|
37870
|
+
console.log(pc5.bold("\nIssues:\n"));
|
|
37534
37871
|
for (const issue2 of compatResult.issues) {
|
|
37535
|
-
const color = issue2.severity === "error" ?
|
|
37872
|
+
const color = issue2.severity === "error" ? pc5.red : issue2.severity === "warning" ? pc5.yellow : pc5.dim;
|
|
37536
37873
|
console.log(` ${color(`[${issue2.severity}]`)} ${issue2.issue}`);
|
|
37537
|
-
console.log(` ${
|
|
37874
|
+
console.log(` ${pc5.dim(issue2.suggestion)}`);
|
|
37538
37875
|
}
|
|
37539
37876
|
}
|
|
37540
37877
|
console.log("");
|
|
37541
37878
|
}
|
|
37542
37879
|
if (config2.plugins && config2.plugins.length > 0) {
|
|
37543
37880
|
if (!features.customPlugins) {
|
|
37544
|
-
console.error(
|
|
37545
|
-
console.log(`Upgrade at: ${
|
|
37881
|
+
console.error(pc5.red("Custom plugins require Team tier."));
|
|
37882
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37546
37883
|
process.exit(1);
|
|
37547
37884
|
}
|
|
37548
37885
|
const pluginLoader = new PluginLoader();
|
|
37549
37886
|
const pluginResult = await pluginLoader.loadPlugins(config2.plugins);
|
|
37550
37887
|
if (pluginResult.errors.length > 0) {
|
|
37551
37888
|
for (const err of pluginResult.errors) {
|
|
37552
|
-
console.warn(
|
|
37889
|
+
console.warn(pc5.yellow(`Plugin error: ${err}`));
|
|
37553
37890
|
}
|
|
37554
37891
|
}
|
|
37555
37892
|
for (const plugin of pluginResult.loaded) {
|
|
@@ -37558,7 +37895,7 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37558
37895
|
engine.registerHandler(h.from, h.to, h.handler);
|
|
37559
37896
|
}
|
|
37560
37897
|
}
|
|
37561
|
-
console.log(
|
|
37898
|
+
console.log(pc5.dim(`Loaded plugin: ${plugin.name} v${plugin.version}`));
|
|
37562
37899
|
}
|
|
37563
37900
|
}
|
|
37564
37901
|
let chainSteps;
|
|
@@ -37571,15 +37908,15 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37571
37908
|
}
|
|
37572
37909
|
const chainValidation = migrationChain.validateChain(chainSteps, engine);
|
|
37573
37910
|
if (!chainValidation.valid) {
|
|
37574
|
-
console.error(
|
|
37911
|
+
console.error(pc5.red("Chain migration validation failed:"));
|
|
37575
37912
|
for (const err of chainValidation.errors) {
|
|
37576
|
-
console.error(` ${
|
|
37913
|
+
console.error(` ${pc5.red(err)}`);
|
|
37577
37914
|
}
|
|
37578
37915
|
process.exit(1);
|
|
37579
37916
|
}
|
|
37580
37917
|
} else {
|
|
37581
37918
|
if (!engine.hasHandler(options.from, options.to)) {
|
|
37582
|
-
console.error(
|
|
37919
|
+
console.error(pc5.red(`No handler for ${migrationPath}`));
|
|
37583
37920
|
console.log(
|
|
37584
37921
|
"Supported migrations:",
|
|
37585
37922
|
engine.getSupportedPaths().map((p) => `${p.from}->${p.to}`).join(", ")
|
|
@@ -37588,57 +37925,57 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37588
37925
|
}
|
|
37589
37926
|
}
|
|
37590
37927
|
let files;
|
|
37591
|
-
if (
|
|
37928
|
+
if (existsSync5(targetPath) && statSync(targetPath).isFile()) {
|
|
37592
37929
|
files = [resolve2(targetPath)];
|
|
37593
37930
|
} else {
|
|
37594
|
-
files = await glob2(
|
|
37931
|
+
files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
37595
37932
|
ignore: config2.exclude
|
|
37596
37933
|
});
|
|
37597
37934
|
}
|
|
37598
37935
|
if (files.length === 0) {
|
|
37599
|
-
console.log(
|
|
37936
|
+
console.log(pc5.yellow("No files found matching patterns."));
|
|
37600
37937
|
process.exit(0);
|
|
37601
37938
|
}
|
|
37602
37939
|
const incrementalTracker = new IncrementalTracker(resolve2(targetPath));
|
|
37603
37940
|
if (options.incrementalStatus) {
|
|
37604
37941
|
const progress = incrementalTracker.getProgress();
|
|
37605
37942
|
if (!progress) {
|
|
37606
|
-
console.log(
|
|
37943
|
+
console.log(pc5.yellow("No incremental migration in progress."));
|
|
37607
37944
|
process.exit(0);
|
|
37608
37945
|
}
|
|
37609
|
-
console.log(
|
|
37610
|
-
console.log(`Completed: ${
|
|
37611
|
-
console.log(`Remaining: ${
|
|
37612
|
-
console.log(`Failed: ${
|
|
37946
|
+
console.log(pc5.bold("\nIncremental Migration Status\n"));
|
|
37947
|
+
console.log(`Completed: ${pc5.green(progress.completed.toString())}`);
|
|
37948
|
+
console.log(`Remaining: ${pc5.cyan(progress.remaining.toString())}`);
|
|
37949
|
+
console.log(`Failed: ${pc5.red(progress.failed.toString())}`);
|
|
37613
37950
|
console.log(`Total: ${progress.total}`);
|
|
37614
|
-
console.log(`Progress: ${
|
|
37951
|
+
console.log(`Progress: ${pc5.cyan(`${progress.percent}%`)}`);
|
|
37615
37952
|
process.exit(0);
|
|
37616
37953
|
}
|
|
37617
37954
|
if (options.resume) {
|
|
37618
37955
|
const state = incrementalTracker.resume();
|
|
37619
37956
|
if (!state) {
|
|
37620
|
-
console.error(
|
|
37957
|
+
console.error(pc5.red("No incremental migration to resume."));
|
|
37621
37958
|
process.exit(1);
|
|
37622
37959
|
}
|
|
37623
37960
|
files = state.remainingFiles;
|
|
37624
|
-
console.log(
|
|
37961
|
+
console.log(pc5.dim(`Resuming incremental migration: ${state.migrationId}`));
|
|
37625
37962
|
const progress = incrementalTracker.getProgress();
|
|
37626
37963
|
if (progress) {
|
|
37627
37964
|
console.log(
|
|
37628
|
-
|
|
37965
|
+
pc5.dim(
|
|
37629
37966
|
`Progress: ${progress.completed}/${progress.total} completed (${progress.percent}%)`
|
|
37630
37967
|
)
|
|
37631
37968
|
);
|
|
37632
37969
|
}
|
|
37633
37970
|
} else if (options.incremental) {
|
|
37634
37971
|
incrementalTracker.start(files, options.from, options.to);
|
|
37635
|
-
console.log(
|
|
37972
|
+
console.log(pc5.dim("Started incremental migration."));
|
|
37636
37973
|
}
|
|
37637
37974
|
if (files.length > features.maxFiles) {
|
|
37638
37975
|
console.error(
|
|
37639
|
-
|
|
37976
|
+
pc5.red(`File limit exceeded. Found ${files.length}, max ${features.maxFiles}.`)
|
|
37640
37977
|
);
|
|
37641
|
-
console.log(`Upgrade at: ${
|
|
37978
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37642
37979
|
process.exit(1);
|
|
37643
37980
|
}
|
|
37644
37981
|
if (options.crossFile) {
|
|
@@ -37650,34 +37987,34 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37650
37987
|
const resolvedFiles = files.map((f) => resolve2(f));
|
|
37651
37988
|
const depResult = depResolver.resolve(analyzer.getProject(), resolvedFiles);
|
|
37652
37989
|
files = depResult.sortedFiles;
|
|
37653
|
-
console.log(
|
|
37654
|
-
console.log(`Files: ${
|
|
37655
|
-
console.log(`Cross-file refs: ${
|
|
37990
|
+
console.log(pc5.bold("\nCross-file resolution\n"));
|
|
37991
|
+
console.log(`Files: ${pc5.cyan(files.length.toString())}`);
|
|
37992
|
+
console.log(`Cross-file refs: ${pc5.cyan(depResult.crossFileRefs.toString())}`);
|
|
37656
37993
|
if (depResult.circularWarnings.length > 0) {
|
|
37657
|
-
console.log(
|
|
37994
|
+
console.log(pc5.yellow(`Circular deps: ${depResult.circularWarnings.length}`));
|
|
37658
37995
|
for (const w of depResult.circularWarnings) {
|
|
37659
|
-
console.log(` ${
|
|
37996
|
+
console.log(` ${pc5.yellow(w)}`);
|
|
37660
37997
|
}
|
|
37661
37998
|
}
|
|
37662
37999
|
console.log("");
|
|
37663
38000
|
}
|
|
37664
38001
|
const displayPath = options.chain || migrationPath;
|
|
37665
|
-
console.log(
|
|
38002
|
+
console.log(pc5.bold(`
|
|
37666
38003
|
Migrating ${displayPath}
|
|
37667
38004
|
`));
|
|
37668
|
-
console.log(`Files: ${
|
|
37669
|
-
console.log(`Tier: ${
|
|
37670
|
-
console.log(`Migration: ${
|
|
38005
|
+
console.log(`Files: ${pc5.cyan(files.length.toString())}`);
|
|
38006
|
+
console.log(`Tier: ${pc5.cyan(tier)}`);
|
|
38007
|
+
console.log(`Migration: ${pc5.cyan(displayPath)}`);
|
|
37671
38008
|
if (chainSteps) {
|
|
37672
|
-
console.log(`Steps: ${
|
|
38009
|
+
console.log(`Steps: ${pc5.cyan(chainSteps.length.toString())}`);
|
|
37673
38010
|
}
|
|
37674
38011
|
if (options.dryRun) {
|
|
37675
|
-
console.log(
|
|
38012
|
+
console.log(pc5.yellow("\nDRY RUN \u2014 no files will be modified\n"));
|
|
37676
38013
|
}
|
|
37677
38014
|
const git = new GitIntegration();
|
|
37678
38015
|
if (options.gitBranch && git.isAvailable()) {
|
|
37679
38016
|
if (git.hasUncommittedChanges()) {
|
|
37680
|
-
console.error(
|
|
38017
|
+
console.error(pc5.red("Uncommitted changes detected. Commit or stash first."));
|
|
37681
38018
|
process.exit(1);
|
|
37682
38019
|
}
|
|
37683
38020
|
const branchName = git.generateBranchName(
|
|
@@ -37686,13 +38023,13 @@ Migrating ${displayPath}
|
|
|
37686
38023
|
options.to
|
|
37687
38024
|
);
|
|
37688
38025
|
git.createBranch(branchName);
|
|
37689
|
-
console.log(
|
|
38026
|
+
console.log(pc5.dim(`Created branch: ${branchName}`));
|
|
37690
38027
|
}
|
|
37691
38028
|
const backup = new BackupManager(config2.backup?.dir);
|
|
37692
38029
|
let backupManifest;
|
|
37693
38030
|
if (options.backup !== false && config2.backup?.enabled !== false && !options.dryRun) {
|
|
37694
38031
|
backupManifest = backup.createBackup(files, options.from, options.to);
|
|
37695
|
-
console.log(
|
|
38032
|
+
console.log(pc5.dim(`Backup created: ${backupManifest.id}`));
|
|
37696
38033
|
}
|
|
37697
38034
|
const results = [];
|
|
37698
38035
|
const useConcurrent = !options.crossFile;
|
|
@@ -37702,7 +38039,7 @@ Migrating ${displayPath}
|
|
|
37702
38039
|
task: async () => {
|
|
37703
38040
|
if (chainSteps) {
|
|
37704
38041
|
const migrationChain = new MigrationChain();
|
|
37705
|
-
const sourceCode =
|
|
38042
|
+
const sourceCode = readFileSync5(resolve2(file), "utf-8");
|
|
37706
38043
|
const chainResult = migrationChain.executeChain(
|
|
37707
38044
|
sourceCode,
|
|
37708
38045
|
resolve2(file),
|
|
@@ -37779,16 +38116,16 @@ Migrating ${displayPath}
|
|
|
37779
38116
|
const progress = incrementalTracker.getProgress();
|
|
37780
38117
|
if (progress) {
|
|
37781
38118
|
console.log(
|
|
37782
|
-
|
|
38119
|
+
pc5.bold(`
|
|
37783
38120
|
Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
37784
38121
|
);
|
|
37785
38122
|
if (progress.remaining > 0) {
|
|
37786
38123
|
console.log(
|
|
37787
|
-
|
|
38124
|
+
pc5.dim(`Run with --resume to continue. ${progress.remaining} files remaining.`)
|
|
37788
38125
|
);
|
|
37789
38126
|
}
|
|
37790
38127
|
if (progress.failed > 0) {
|
|
37791
|
-
console.log(
|
|
38128
|
+
console.log(pc5.yellow(`${progress.failed} file(s) failed. Run with --resume to retry.`));
|
|
37792
38129
|
}
|
|
37793
38130
|
}
|
|
37794
38131
|
}
|
|
@@ -37797,17 +38134,17 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37797
38134
|
if (successFiles.length > 0) {
|
|
37798
38135
|
git.stageFiles(successFiles);
|
|
37799
38136
|
git.commit(config2.git?.commitMessage || `chore: migrate ${options.from} to ${options.to}`);
|
|
37800
|
-
console.log(
|
|
38137
|
+
console.log(pc5.dim("Changes committed"));
|
|
37801
38138
|
}
|
|
37802
38139
|
}
|
|
37803
38140
|
if (options.report) {
|
|
37804
38141
|
if (options.report === "html" && !features.htmlReports) {
|
|
37805
|
-
console.error(
|
|
37806
|
-
console.log(`Upgrade at: ${
|
|
38142
|
+
console.error(pc5.red("HTML reports require Individual tier or higher."));
|
|
38143
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37807
38144
|
process.exit(1);
|
|
37808
38145
|
} else if (options.report === "csv" && !features.csvExport) {
|
|
37809
|
-
console.error(
|
|
37810
|
-
console.log(`Upgrade at: ${
|
|
38146
|
+
console.error(pc5.red("CSV export requires Individual tier or higher."));
|
|
38147
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37811
38148
|
process.exit(1);
|
|
37812
38149
|
} else {
|
|
37813
38150
|
const reporter = new ReportGenerator();
|
|
@@ -37828,13 +38165,13 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37828
38165
|
} else {
|
|
37829
38166
|
reporter.writeJson(report, outputPath);
|
|
37830
38167
|
}
|
|
37831
|
-
console.log(
|
|
38168
|
+
console.log(pc5.green(`Report saved: ${outputPath}`));
|
|
37832
38169
|
}
|
|
37833
38170
|
}
|
|
37834
38171
|
if (options.scaffoldTests) {
|
|
37835
38172
|
if (!features.testScaffolding) {
|
|
37836
|
-
console.error(
|
|
37837
|
-
console.log(`Upgrade at: ${
|
|
38173
|
+
console.error(pc5.red("\nTest scaffolding requires Pro tier or higher."));
|
|
38174
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37838
38175
|
} else {
|
|
37839
38176
|
const scaffolder = new TestScaffolder();
|
|
37840
38177
|
const successfulFiles = results.filter((r) => r.success).map((r) => r.filePath);
|
|
@@ -37845,20 +38182,20 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37845
38182
|
}
|
|
37846
38183
|
const sourceFiles = scaffoldAnalyzer.getProject().getSourceFiles();
|
|
37847
38184
|
const scaffoldResult = scaffolder.scaffold(sourceFiles, options.from, options.to);
|
|
37848
|
-
console.log(
|
|
38185
|
+
console.log(pc5.bold("\nTest Scaffolding\n"));
|
|
37849
38186
|
console.log(
|
|
37850
|
-
` Generated ${
|
|
38187
|
+
` Generated ${pc5.cyan(scaffoldResult.tests.length.toString())} test file(s) for ${scaffoldResult.totalSchemas} schema(s)`
|
|
37851
38188
|
);
|
|
37852
38189
|
for (const test of scaffoldResult.tests) {
|
|
37853
|
-
console.log(` ${
|
|
38190
|
+
console.log(` ${pc5.dim(test.filePath)} (${test.schemaCount} schemas)`);
|
|
37854
38191
|
}
|
|
37855
38192
|
}
|
|
37856
38193
|
}
|
|
37857
38194
|
}
|
|
37858
38195
|
if (options.audit) {
|
|
37859
38196
|
if (!features.auditLogging) {
|
|
37860
|
-
console.error(
|
|
37861
|
-
console.log(`Upgrade at: ${
|
|
38197
|
+
console.error(pc5.red("\nAudit logging requires Team tier."));
|
|
38198
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37862
38199
|
} else {
|
|
37863
38200
|
const projectPath = resolve2(targetPath);
|
|
37864
38201
|
const auditLog = new MigrationAuditLog(projectPath);
|
|
@@ -37878,21 +38215,36 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37878
38215
|
auditLog.append(entry);
|
|
37879
38216
|
}
|
|
37880
38217
|
console.log(
|
|
37881
|
-
|
|
38218
|
+
pc5.dim(`
|
|
37882
38219
|
Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
|
|
37883
38220
|
);
|
|
37884
38221
|
}
|
|
37885
38222
|
}
|
|
37886
38223
|
if (options.dryRun) {
|
|
38224
|
+
const summaryTable = generateDrySummaryTable(results);
|
|
38225
|
+
if (summaryTable) {
|
|
38226
|
+
console.log(pc5.bold("\nChange Summary\n"));
|
|
38227
|
+
console.log(summaryTable);
|
|
38228
|
+
}
|
|
37887
38229
|
const diffOutput = generateDiffPreview(results);
|
|
37888
38230
|
if (diffOutput) {
|
|
37889
|
-
console.log(
|
|
38231
|
+
console.log(pc5.bold("\nDiff Preview\n"));
|
|
37890
38232
|
console.log(diffOutput);
|
|
37891
38233
|
}
|
|
37892
38234
|
}
|
|
38235
|
+
if (options.outputDiff) {
|
|
38236
|
+
const unifiedDiff = generateUnifiedDiff(results);
|
|
38237
|
+
if (options.outputDiff === "-") {
|
|
38238
|
+
console.log(unifiedDiff);
|
|
38239
|
+
} else {
|
|
38240
|
+
writeFileSync4(options.outputDiff, unifiedDiff);
|
|
38241
|
+
console.log(pc5.dim(`
|
|
38242
|
+
Unified diff written to ${options.outputDiff}`));
|
|
38243
|
+
}
|
|
38244
|
+
}
|
|
37893
38245
|
const failed = results.filter((r) => !r.success).length;
|
|
37894
38246
|
const warnings = results.reduce((acc, r) => acc + r.warnings.length, 0);
|
|
37895
|
-
console.log(
|
|
38247
|
+
console.log(pc5.bold("\nSummary\n"));
|
|
37896
38248
|
console.log(generateDiffSummary(results));
|
|
37897
38249
|
console.log(`Duration: ${Date.now() - startTime}ms`);
|
|
37898
38250
|
if (failed > 0) {
|
|
@@ -37900,7 +38252,7 @@ Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
|
|
|
37900
38252
|
console.log(formatErrorSummary(errorFiles));
|
|
37901
38253
|
}
|
|
37902
38254
|
if (options.verbose && warnings > 0) {
|
|
37903
|
-
console.log(
|
|
38255
|
+
console.log(pc5.yellow("\nWarnings:\n"));
|
|
37904
38256
|
for (const result of results) {
|
|
37905
38257
|
for (const warning of result.warnings.slice(0, 5)) {
|
|
37906
38258
|
console.log(` ${warning}`);
|
|
@@ -37912,7 +38264,7 @@ Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
|
|
|
37912
38264
|
}
|
|
37913
38265
|
if (options.failOnWarnings && warnings > 0) {
|
|
37914
38266
|
console.error(
|
|
37915
|
-
|
|
38267
|
+
pc5.red(`
|
|
37916
38268
|
Failing due to ${warnings} warning(s) (--fail-on-warnings enabled).`)
|
|
37917
38269
|
);
|
|
37918
38270
|
process.exit(1);
|
|
@@ -37928,13 +38280,13 @@ Failing due to ${warnings} warning(s) (--fail-on-warnings enabled).`)
|
|
|
37928
38280
|
}
|
|
37929
38281
|
if (highRiskFiles.length > 0) {
|
|
37930
38282
|
console.error(
|
|
37931
|
-
|
|
38283
|
+
pc5.red(
|
|
37932
38284
|
`
|
|
37933
38285
|
Failing due to ${highRiskFiles.length} file(s) exceeding max risk score of ${options.maxRiskScore}:`
|
|
37934
38286
|
)
|
|
37935
38287
|
);
|
|
37936
38288
|
for (const f of highRiskFiles) {
|
|
37937
|
-
console.error(
|
|
38289
|
+
console.error(pc5.red(` ${f}`));
|
|
37938
38290
|
}
|
|
37939
38291
|
process.exit(1);
|
|
37940
38292
|
}
|
|
@@ -37943,22 +38295,22 @@ Failing due to ${highRiskFiles.length} file(s) exceeding max risk score of ${opt
|
|
|
37943
38295
|
program.command("watch <path>").description("Watch files and migrate on change").requiredOption("-f, --from <library>", "Source library").requiredOption("-t, --to <library>", "Target library").option("-c, --config <path>", "Path to config file").action(async (targetPath, options) => {
|
|
37944
38296
|
const validation = await licenseManager.validate();
|
|
37945
38297
|
if (!validation.license?.features.watchMode) {
|
|
37946
|
-
console.error(
|
|
37947
|
-
console.log(`Upgrade at: ${
|
|
38298
|
+
console.error(pc5.red("Watch mode requires Pro tier or higher."));
|
|
38299
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37948
38300
|
process.exit(1);
|
|
37949
38301
|
}
|
|
37950
38302
|
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
37951
38303
|
if (!canUseMigration(tier, options.from, options.to)) {
|
|
37952
|
-
console.error(
|
|
37953
|
-
console.log(`Upgrade at: ${
|
|
38304
|
+
console.error(pc5.red(`Migration ${options.from}->${options.to} requires a higher tier.`));
|
|
38305
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37954
38306
|
process.exit(1);
|
|
37955
38307
|
}
|
|
37956
38308
|
let config2;
|
|
37957
38309
|
try {
|
|
37958
|
-
config2 = await
|
|
38310
|
+
config2 = await loadConfig2(options.config);
|
|
37959
38311
|
} catch (error) {
|
|
37960
38312
|
const msg = error instanceof Error ? error.message : String(error);
|
|
37961
|
-
console.warn(
|
|
38313
|
+
console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
|
|
37962
38314
|
config2 = {
|
|
37963
38315
|
include: ["**/*.ts", "**/*.tsx"],
|
|
37964
38316
|
exclude: ["**/node_modules/**", "**/dist/**"]
|
|
@@ -37966,7 +38318,7 @@ program.command("watch <path>").description("Watch files and migrate on change")
|
|
|
37966
38318
|
}
|
|
37967
38319
|
const watchMode = new WatchMode();
|
|
37968
38320
|
await watchMode.start({
|
|
37969
|
-
patterns: config2.include.map((p) =>
|
|
38321
|
+
patterns: config2.include.map((p) => join4(targetPath, p)),
|
|
37970
38322
|
exclude: config2.exclude,
|
|
37971
38323
|
from: options.from,
|
|
37972
38324
|
to: options.to,
|
|
@@ -37989,7 +38341,7 @@ program.command("watch <path>").description("Watch files and migrate on change")
|
|
|
37989
38341
|
});
|
|
37990
38342
|
process.on("SIGINT", () => {
|
|
37991
38343
|
watchMode.stop();
|
|
37992
|
-
console.log(
|
|
38344
|
+
console.log(pc5.dim("\nStopped watching."));
|
|
37993
38345
|
process.exit(0);
|
|
37994
38346
|
});
|
|
37995
38347
|
});
|
|
@@ -37998,56 +38350,56 @@ program.command("rollback [backupId]").description("Restore files from a backup"
|
|
|
37998
38350
|
if (options.list) {
|
|
37999
38351
|
const backups = backup.listBackups();
|
|
38000
38352
|
if (backups.length === 0) {
|
|
38001
|
-
console.log(
|
|
38353
|
+
console.log(pc5.yellow("No backups found."));
|
|
38002
38354
|
return;
|
|
38003
38355
|
}
|
|
38004
|
-
console.log(
|
|
38356
|
+
console.log(pc5.bold("\nAvailable backups:\n"));
|
|
38005
38357
|
for (const b of backups) {
|
|
38006
|
-
console.log(` ${
|
|
38358
|
+
console.log(` ${pc5.cyan(b.id)}`);
|
|
38007
38359
|
console.log(` ${b.from} -> ${b.to} | ${b.files.length} files`);
|
|
38008
|
-
console.log(` ${
|
|
38360
|
+
console.log(` ${pc5.dim(new Date(b.timestamp).toLocaleString())}
|
|
38009
38361
|
`);
|
|
38010
38362
|
}
|
|
38011
38363
|
return;
|
|
38012
38364
|
}
|
|
38013
38365
|
if (options.clean) {
|
|
38014
38366
|
backup.cleanOldBackups(5);
|
|
38015
|
-
console.log(
|
|
38367
|
+
console.log(pc5.green("Old backups cleaned."));
|
|
38016
38368
|
return;
|
|
38017
38369
|
}
|
|
38018
38370
|
if (!backupId) {
|
|
38019
38371
|
const backups = backup.listBackups();
|
|
38020
38372
|
if (backups.length === 0) {
|
|
38021
|
-
console.error(
|
|
38373
|
+
console.error(pc5.red("No backups available."));
|
|
38022
38374
|
process.exit(1);
|
|
38023
38375
|
}
|
|
38024
38376
|
backupId = backups[0]?.id;
|
|
38025
38377
|
}
|
|
38026
38378
|
if (!backupId) {
|
|
38027
|
-
console.error(
|
|
38379
|
+
console.error(pc5.red("No backup ID provided."));
|
|
38028
38380
|
process.exit(1);
|
|
38029
38381
|
}
|
|
38030
38382
|
try {
|
|
38031
38383
|
backup.restore(backupId);
|
|
38032
|
-
console.log(
|
|
38384
|
+
console.log(pc5.green(`Restored from ${backupId}`));
|
|
38033
38385
|
} catch (error) {
|
|
38034
|
-
console.error(
|
|
38386
|
+
console.error(pc5.red(error instanceof Error ? error.message : "Restore failed"));
|
|
38035
38387
|
process.exit(1);
|
|
38036
38388
|
}
|
|
38037
38389
|
});
|
|
38038
38390
|
program.command("license").description("Show license information").option("-a, --activate <key>", "Activate a license key").option("-d, --deactivate", "Deactivate current license").option("-s, --status", "Show license status").action(async (options) => {
|
|
38039
38391
|
if (options.activate) {
|
|
38040
|
-
console.log(
|
|
38392
|
+
console.log(pc5.dim("Activating license..."));
|
|
38041
38393
|
const result = await licenseManager.activate(options.activate);
|
|
38042
38394
|
if (result.valid && result.license) {
|
|
38043
|
-
console.log(
|
|
38044
|
-
console.log(`Tier: ${
|
|
38395
|
+
console.log(pc5.green("\nLicense activated successfully!\n"));
|
|
38396
|
+
console.log(`Tier: ${pc5.cyan(result.license.tier)}`);
|
|
38045
38397
|
console.log(`Migrations: ${result.license.features.migrations.join(", ")}`);
|
|
38046
38398
|
console.log(
|
|
38047
38399
|
`Max files: ${result.license.features.maxFiles === Infinity ? "Unlimited" : result.license.features.maxFiles}`
|
|
38048
38400
|
);
|
|
38049
38401
|
} else {
|
|
38050
|
-
console.error(
|
|
38402
|
+
console.error(pc5.red(`
|
|
38051
38403
|
Activation failed: ${result.error}`));
|
|
38052
38404
|
process.exit(1);
|
|
38053
38405
|
}
|
|
@@ -38056,26 +38408,26 @@ Activation failed: ${result.error}`));
|
|
|
38056
38408
|
if (options.deactivate) {
|
|
38057
38409
|
const result = await licenseManager.deactivate();
|
|
38058
38410
|
if (result.success) {
|
|
38059
|
-
console.log(
|
|
38411
|
+
console.log(pc5.green("License deactivated. You can now activate on another device."));
|
|
38060
38412
|
} else {
|
|
38061
|
-
console.error(
|
|
38413
|
+
console.error(pc5.red(`Deactivation failed: ${result.error}`));
|
|
38062
38414
|
process.exit(1);
|
|
38063
38415
|
}
|
|
38064
38416
|
return;
|
|
38065
38417
|
}
|
|
38066
38418
|
const status = await licenseManager.getStatus();
|
|
38067
|
-
console.log(
|
|
38419
|
+
console.log(pc5.bold("\nLicense Status\n"));
|
|
38068
38420
|
if (!status.activated) {
|
|
38069
|
-
console.log(`Tier: ${
|
|
38421
|
+
console.log(`Tier: ${pc5.yellow("FREE")}`);
|
|
38070
38422
|
console.log("Max files: 5");
|
|
38071
38423
|
console.log("Migrations: yup->zod only");
|
|
38072
38424
|
console.log("\nActivate a license: schemashift license --activate <key>");
|
|
38073
|
-
console.log(`Purchase: ${
|
|
38425
|
+
console.log(`Purchase: ${pc5.cyan(POLAR_URL)}`);
|
|
38074
38426
|
return;
|
|
38075
38427
|
}
|
|
38076
38428
|
const features = TIER_FEATURES[status.tier];
|
|
38077
|
-
console.log(`Tier: ${
|
|
38078
|
-
console.log(`Activated: ${
|
|
38429
|
+
console.log(`Tier: ${pc5.cyan(status.tier.toUpperCase())}`);
|
|
38430
|
+
console.log(`Activated: ${pc5.green("Yes")}`);
|
|
38079
38431
|
if (status.email) console.log(`Email: ${status.email}`);
|
|
38080
38432
|
if (status.expiresAt) console.log(`Expires: ${status.expiresAt.toLocaleDateString()}`);
|
|
38081
38433
|
if (status.lastValidated)
|
|
@@ -38083,29 +38435,29 @@ Activation failed: ${result.error}`));
|
|
|
38083
38435
|
console.log("\nFeatures:");
|
|
38084
38436
|
console.log(` Max files: ${features.maxFiles === Infinity ? "Unlimited" : features.maxFiles}`);
|
|
38085
38437
|
console.log(` Migrations: ${features.migrations.join(", ")}`);
|
|
38086
|
-
console.log(` CI/CD: ${features.ciSupport ?
|
|
38087
|
-
console.log(` HTML reports: ${features.htmlReports ?
|
|
38088
|
-
console.log(` Watch mode: ${features.watchMode ?
|
|
38089
|
-
console.log(` Git integration: ${features.gitIntegration ?
|
|
38438
|
+
console.log(` CI/CD: ${features.ciSupport ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38439
|
+
console.log(` HTML reports: ${features.htmlReports ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38440
|
+
console.log(` Watch mode: ${features.watchMode ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38441
|
+
console.log(` Git integration: ${features.gitIntegration ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38090
38442
|
console.log(
|
|
38091
|
-
` Advanced analysis: ${features.advancedAnalysis ?
|
|
38443
|
+
` Advanced analysis: ${features.advancedAnalysis ? pc5.green("Yes") : pc5.red("No")}`
|
|
38092
38444
|
);
|
|
38093
|
-
console.log(` Risk scoring: ${features.riskScoring ?
|
|
38094
|
-
console.log(` CSV export: ${features.csvExport ?
|
|
38445
|
+
console.log(` Risk scoring: ${features.riskScoring ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38446
|
+
console.log(` CSV export: ${features.csvExport ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38095
38447
|
console.log(
|
|
38096
|
-
` Cross-file resolution: ${features.crossFileResolution ?
|
|
38448
|
+
` Cross-file resolution: ${features.crossFileResolution ? pc5.green("Yes") : pc5.red("No")}`
|
|
38097
38449
|
);
|
|
38098
|
-
console.log(` Chain migrations: ${features.chainMigrations ?
|
|
38099
|
-
console.log(` Compat check: ${features.compatCheck ?
|
|
38100
|
-
console.log(` Custom plugins: ${features.customPlugins ?
|
|
38101
|
-
console.log(` Governance: ${features.governance ?
|
|
38450
|
+
console.log(` Chain migrations: ${features.chainMigrations ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38451
|
+
console.log(` Compat check: ${features.compatCheck ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38452
|
+
console.log(` Custom plugins: ${features.customPlugins ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38453
|
+
console.log(` Governance: ${features.governance ? pc5.green("Yes") : pc5.red("No")}`);
|
|
38102
38454
|
});
|
|
38103
38455
|
program.command("compat <path>").description("Check schema library compatibility (Pro+)").option("--json", "Output as JSON").action(async (targetPath, options) => {
|
|
38104
38456
|
const validation = await licenseManager.validate();
|
|
38105
38457
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38106
38458
|
if (!features.compatCheck) {
|
|
38107
|
-
console.error(
|
|
38108
|
-
console.log(`Upgrade at: ${
|
|
38459
|
+
console.error(pc5.red("Compatibility check requires Pro tier or higher."));
|
|
38460
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38109
38461
|
process.exit(1);
|
|
38110
38462
|
}
|
|
38111
38463
|
const compatAnalyzer = new CompatibilityAnalyzer();
|
|
@@ -38114,43 +38466,43 @@ program.command("compat <path>").description("Check schema library compatibility
|
|
|
38114
38466
|
console.log(JSON.stringify(result, null, 2));
|
|
38115
38467
|
return;
|
|
38116
38468
|
}
|
|
38117
|
-
console.log(
|
|
38469
|
+
console.log(pc5.bold("\nSchema Compatibility Report\n"));
|
|
38118
38470
|
if (result.detectedVersions.length === 0) {
|
|
38119
|
-
console.log(
|
|
38471
|
+
console.log(pc5.yellow("No schema libraries detected in package.json."));
|
|
38120
38472
|
return;
|
|
38121
38473
|
}
|
|
38122
|
-
console.log(
|
|
38474
|
+
console.log(pc5.bold("Detected libraries:\n"));
|
|
38123
38475
|
for (const v of result.detectedVersions) {
|
|
38124
|
-
console.log(` ${v.library}: ${
|
|
38476
|
+
console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
|
|
38125
38477
|
}
|
|
38126
38478
|
console.log(`
|
|
38127
|
-
Compatibility score: ${
|
|
38479
|
+
Compatibility score: ${pc5.cyan(result.overallScore.toString())}%`);
|
|
38128
38480
|
if (result.issues.length > 0) {
|
|
38129
|
-
console.log(
|
|
38481
|
+
console.log(pc5.bold("\nIssues:\n"));
|
|
38130
38482
|
for (const issue2 of result.issues) {
|
|
38131
|
-
const color = issue2.severity === "error" ?
|
|
38483
|
+
const color = issue2.severity === "error" ? pc5.red : issue2.severity === "warning" ? pc5.yellow : pc5.dim;
|
|
38132
38484
|
console.log(` ${color(`[${issue2.severity}]`)} ${issue2.library} ${issue2.detectedVersion}`);
|
|
38133
38485
|
console.log(` ${issue2.issue}`);
|
|
38134
|
-
console.log(` ${
|
|
38486
|
+
console.log(` ${pc5.dim(issue2.suggestion)}`);
|
|
38135
38487
|
}
|
|
38136
38488
|
} else {
|
|
38137
|
-
console.log(
|
|
38489
|
+
console.log(pc5.green("\nNo compatibility issues detected."));
|
|
38138
38490
|
}
|
|
38139
38491
|
});
|
|
38140
38492
|
program.command("governance <path>").description("Run schema governance checks (Team+)").option("--json", "Output as JSON").option("--fix", "Auto-fix fixable violations").option("--fix-dry-run", "Preview auto-fixes without applying").option("-c, --config <path>", "Path to config file").action(async (targetPath, options) => {
|
|
38141
38493
|
const validation = await licenseManager.validate();
|
|
38142
38494
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38143
38495
|
if (!features.governance) {
|
|
38144
|
-
console.error(
|
|
38145
|
-
console.log(`Upgrade at: ${
|
|
38496
|
+
console.error(pc5.red("Schema governance requires Team tier."));
|
|
38497
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38146
38498
|
process.exit(1);
|
|
38147
38499
|
}
|
|
38148
38500
|
let config2;
|
|
38149
38501
|
try {
|
|
38150
|
-
config2 = await
|
|
38502
|
+
config2 = await loadConfig2(options.config);
|
|
38151
38503
|
} catch (error) {
|
|
38152
38504
|
const msg = error instanceof Error ? error.message : String(error);
|
|
38153
|
-
console.warn(
|
|
38505
|
+
console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
|
|
38154
38506
|
config2 = {
|
|
38155
38507
|
include: ["**/*.ts", "**/*.tsx"],
|
|
38156
38508
|
exclude: ["**/node_modules/**", "**/dist/**"],
|
|
@@ -38159,17 +38511,17 @@ program.command("governance <path>").description("Run schema governance checks (
|
|
|
38159
38511
|
};
|
|
38160
38512
|
}
|
|
38161
38513
|
if (!config2.governance?.rules) {
|
|
38162
|
-
console.error(
|
|
38163
|
-
console.log(
|
|
38514
|
+
console.error(pc5.red("No governance rules configured."));
|
|
38515
|
+
console.log(pc5.dim('Add a "governance" section to your schemashift config file.'));
|
|
38164
38516
|
process.exit(1);
|
|
38165
38517
|
}
|
|
38166
38518
|
const { Project } = await import("./ts-morph-5TNWNYI7.js");
|
|
38167
38519
|
const project = new Project();
|
|
38168
|
-
const files = await glob2(
|
|
38520
|
+
const files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
38169
38521
|
ignore: config2.exclude
|
|
38170
38522
|
});
|
|
38171
38523
|
if (files.length === 0) {
|
|
38172
|
-
console.log(
|
|
38524
|
+
console.log(pc5.yellow("No files found."));
|
|
38173
38525
|
process.exit(0);
|
|
38174
38526
|
}
|
|
38175
38527
|
for (const file of files) {
|
|
@@ -38182,26 +38534,26 @@ program.command("governance <path>").description("Run schema governance checks (
|
|
|
38182
38534
|
console.log(JSON.stringify(result, null, 2));
|
|
38183
38535
|
return;
|
|
38184
38536
|
}
|
|
38185
|
-
console.log(
|
|
38186
|
-
console.log(`Files scanned: ${
|
|
38187
|
-
console.log(`Schemas checked: ${
|
|
38188
|
-
console.log(`Violations: ${
|
|
38537
|
+
console.log(pc5.bold("\nSchema Governance Report\n"));
|
|
38538
|
+
console.log(`Files scanned: ${pc5.cyan(result.filesScanned.toString())}`);
|
|
38539
|
+
console.log(`Schemas checked: ${pc5.cyan(result.schemasChecked.toString())}`);
|
|
38540
|
+
console.log(`Violations: ${pc5.cyan(result.violations.length.toString())}`);
|
|
38189
38541
|
if (result.violations.length > 0) {
|
|
38190
38542
|
console.log("");
|
|
38191
38543
|
for (const v of result.violations) {
|
|
38192
|
-
const color = v.severity === "error" ?
|
|
38544
|
+
const color = v.severity === "error" ? pc5.red : pc5.yellow;
|
|
38193
38545
|
console.log(` ${color(`[${v.severity}]`)} ${v.message}`);
|
|
38194
|
-
console.log(` ${
|
|
38546
|
+
console.log(` ${pc5.dim(`${v.filePath}:${v.lineNumber}`)}`);
|
|
38195
38547
|
}
|
|
38196
38548
|
}
|
|
38197
38549
|
if ((options.fix || options.fixDryRun) && result.violations.length > 0) {
|
|
38198
38550
|
const governanceFixer = new GovernanceFixer();
|
|
38199
38551
|
const fixableCount = result.violations.filter((v) => governanceFixer.canFix(v)).length;
|
|
38200
38552
|
if (fixableCount === 0) {
|
|
38201
|
-
console.log(
|
|
38553
|
+
console.log(pc5.yellow("\nNo auto-fixable violations found."));
|
|
38202
38554
|
} else {
|
|
38203
38555
|
console.log(
|
|
38204
|
-
|
|
38556
|
+
pc5.bold(
|
|
38205
38557
|
`
|
|
38206
38558
|
${options.fixDryRun ? "Fix Preview" : "Auto-fixing"} (${fixableCount} fixable)
|
|
38207
38559
|
`
|
|
@@ -38215,14 +38567,14 @@ ${options.fixDryRun ? "Fix Preview" : "Auto-fixing"} (${fixableCount} fixable)
|
|
|
38215
38567
|
}
|
|
38216
38568
|
let totalFixed = 0;
|
|
38217
38569
|
for (const [filePath, fileViolations] of byFile) {
|
|
38218
|
-
const sourceCode =
|
|
38570
|
+
const sourceCode = readFileSync5(filePath, "utf-8");
|
|
38219
38571
|
const summary = governanceFixer.fixAll(fileViolations, sourceCode);
|
|
38220
38572
|
if (summary.fixed > 0) {
|
|
38221
38573
|
const shortPath = filePath.replace(`${process.cwd()}/`, "");
|
|
38222
|
-
console.log(` ${
|
|
38574
|
+
console.log(` ${pc5.cyan(shortPath)}: ${summary.fixed} fix(es)`);
|
|
38223
38575
|
for (const r of summary.results) {
|
|
38224
38576
|
if (r.success) {
|
|
38225
|
-
console.log(` ${
|
|
38577
|
+
console.log(` ${pc5.green("\u2713")} ${r.explanation}`);
|
|
38226
38578
|
}
|
|
38227
38579
|
}
|
|
38228
38580
|
if (!options.fixDryRun && summary.results.length > 0) {
|
|
@@ -38235,19 +38587,19 @@ ${options.fixDryRun ? "Fix Preview" : "Auto-fixing"} (${fixableCount} fixable)
|
|
|
38235
38587
|
}
|
|
38236
38588
|
}
|
|
38237
38589
|
if (options.fixDryRun) {
|
|
38238
|
-
console.log(
|
|
38590
|
+
console.log(pc5.yellow(`
|
|
38239
38591
|
Dry run: ${totalFixed} fix(es) would be applied.`));
|
|
38240
38592
|
} else {
|
|
38241
|
-
console.log(
|
|
38593
|
+
console.log(pc5.green(`
|
|
38242
38594
|
${totalFixed} fix(es) applied.`));
|
|
38243
38595
|
}
|
|
38244
38596
|
}
|
|
38245
38597
|
}
|
|
38246
38598
|
console.log("");
|
|
38247
38599
|
if (result.passed) {
|
|
38248
|
-
console.log(
|
|
38600
|
+
console.log(pc5.green("Governance check passed."));
|
|
38249
38601
|
} else {
|
|
38250
|
-
console.log(
|
|
38602
|
+
console.log(pc5.red("Governance check failed."));
|
|
38251
38603
|
if (config2.governance.failOnViolation) {
|
|
38252
38604
|
process.exit(1);
|
|
38253
38605
|
}
|
|
@@ -38262,29 +38614,29 @@ program.command("presets").description("List available migration presets").optio
|
|
|
38262
38614
|
console.log(JSON.stringify(templates, null, 2));
|
|
38263
38615
|
return;
|
|
38264
38616
|
}
|
|
38265
|
-
console.log(
|
|
38617
|
+
console.log(pc5.bold("\nAvailable Migration Presets\n"));
|
|
38266
38618
|
for (const t of templates) {
|
|
38267
|
-
console.log(` ${
|
|
38619
|
+
console.log(` ${pc5.cyan(t.name)}`);
|
|
38268
38620
|
console.log(` ${t.description}`);
|
|
38269
38621
|
console.log(` Category: ${t.category} | Effort: ${t.estimatedEffort}`);
|
|
38270
38622
|
console.log(` Steps: ${t.migrationSteps.map((s) => `${s.from}\u2192${s.to}`).join(", ")}`);
|
|
38271
38623
|
console.log("");
|
|
38272
38624
|
}
|
|
38273
|
-
console.log(
|
|
38625
|
+
console.log(pc5.dim("Use with: schemashift migrate <path> --preset <name>"));
|
|
38274
38626
|
});
|
|
38275
38627
|
program.command("graph <path>").description("Visualize schema dependency graph (Pro+)").option("--format <format>", "Output format: dot, mermaid", "mermaid").option("-o, --output <file>", "Write output to file").option("--filter <library>", "Only show files using a specific library").option("--highlight-circular", "Highlight circular dependencies").option("--color", "Color-code nodes by schema library").action(async (targetPath, options) => {
|
|
38276
38628
|
const validation = await licenseManager.validate();
|
|
38277
38629
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38278
38630
|
if (!features.crossFileResolution) {
|
|
38279
|
-
console.error(
|
|
38280
|
-
console.log(`Upgrade at: ${
|
|
38631
|
+
console.error(pc5.red("Dependency graph visualization requires Pro tier or higher."));
|
|
38632
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38281
38633
|
process.exit(1);
|
|
38282
38634
|
}
|
|
38283
|
-
const files = await glob2(
|
|
38635
|
+
const files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
38284
38636
|
ignore: ["**/node_modules/**", "**/dist/**"]
|
|
38285
38637
|
});
|
|
38286
38638
|
if (files.length === 0) {
|
|
38287
|
-
console.log(
|
|
38639
|
+
console.log(pc5.yellow("No files found."));
|
|
38288
38640
|
process.exit(0);
|
|
38289
38641
|
}
|
|
38290
38642
|
const analyzer = new SchemaAnalyzer();
|
|
@@ -38316,16 +38668,16 @@ program.command("graph <path>").description("Visualize schema dependency graph (
|
|
|
38316
38668
|
}
|
|
38317
38669
|
if (options.output) {
|
|
38318
38670
|
writeFileSync4(options.output, output);
|
|
38319
|
-
console.log(
|
|
38671
|
+
console.log(pc5.green(`Graph written to ${options.output}`));
|
|
38320
38672
|
} else {
|
|
38321
38673
|
console.log(output);
|
|
38322
38674
|
}
|
|
38323
38675
|
console.log(
|
|
38324
|
-
|
|
38676
|
+
pc5.dim(`
|
|
38325
38677
|
${depResult.sortedFiles.length} files, ${depResult.crossFileRefs} cross-file refs`)
|
|
38326
38678
|
);
|
|
38327
38679
|
if (depResult.circularWarnings.length > 0) {
|
|
38328
|
-
console.log(
|
|
38680
|
+
console.log(pc5.yellow(`${depResult.circularWarnings.length} circular dependency warning(s)`));
|
|
38329
38681
|
}
|
|
38330
38682
|
});
|
|
38331
38683
|
program.command("approvals").description("Manage migration approval workflows (TEAM)").argument("[path]", "Project path", ".").option("--create", "Create a new migration request").option("--approve <id>", "Approve a pending migration request").option("--reject <id>", "Reject a pending migration request").option("--list", "List all migration requests").option("--status <status>", "Filter by status (pending/approved/rejected)").option("-f, --from <lib>", "Source schema library (for --create)").option("-t, --to <lib>", "Target schema library (for --create)").option("--reviewer <name>", "Reviewer name (for --approve/--reject)").option("--reason <reason>", "Reason for decision").option("--json", "Output as JSON").action(
|
|
@@ -38333,15 +38685,15 @@ program.command("approvals").description("Manage migration approval workflows (T
|
|
|
38333
38685
|
const validation = await licenseManager.validate();
|
|
38334
38686
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38335
38687
|
if (!features.governance) {
|
|
38336
|
-
console.log(
|
|
38337
|
-
console.log(`Upgrade at: ${
|
|
38688
|
+
console.log(pc5.red("Approval workflows require a TEAM license."));
|
|
38689
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38338
38690
|
return;
|
|
38339
38691
|
}
|
|
38340
38692
|
const projectPath = resolve2(inputPath);
|
|
38341
38693
|
const manager = new ApprovalManager(projectPath);
|
|
38342
38694
|
if (opts.create) {
|
|
38343
38695
|
if (!opts.from || !opts.to) {
|
|
38344
|
-
console.log(
|
|
38696
|
+
console.log(pc5.red("--from and --to are required with --create"));
|
|
38345
38697
|
return;
|
|
38346
38698
|
}
|
|
38347
38699
|
const files = await glob2("**/*.{ts,tsx,js,jsx}", {
|
|
@@ -38354,10 +38706,10 @@ program.command("approvals").description("Manage migration approval workflows (T
|
|
|
38354
38706
|
if (opts.json) {
|
|
38355
38707
|
console.log(JSON.stringify(request, null, 2));
|
|
38356
38708
|
} else {
|
|
38357
|
-
console.log(
|
|
38709
|
+
console.log(pc5.green(`Migration request created: ${pc5.bold(request.id)}`));
|
|
38358
38710
|
console.log(` From: ${request.from} \u2192 To: ${request.to}`);
|
|
38359
38711
|
console.log(` Files: ${request.files.length}`);
|
|
38360
|
-
console.log(` Status: ${
|
|
38712
|
+
console.log(` Status: ${pc5.yellow("pending")}`);
|
|
38361
38713
|
}
|
|
38362
38714
|
} else if (opts.approve) {
|
|
38363
38715
|
const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
|
|
@@ -38367,7 +38719,7 @@ program.command("approvals").description("Manage migration approval workflows (T
|
|
|
38367
38719
|
reviewedBy: reviewer,
|
|
38368
38720
|
reason: opts.reason
|
|
38369
38721
|
});
|
|
38370
|
-
console.log(
|
|
38722
|
+
console.log(pc5.green(`Migration request ${pc5.bold(request.id)} approved by ${reviewer}`));
|
|
38371
38723
|
} else if (opts.reject) {
|
|
38372
38724
|
const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
|
|
38373
38725
|
const request = manager.review({
|
|
@@ -38376,7 +38728,7 @@ program.command("approvals").description("Manage migration approval workflows (T
|
|
|
38376
38728
|
reviewedBy: reviewer,
|
|
38377
38729
|
reason: opts.reason
|
|
38378
38730
|
});
|
|
38379
|
-
console.log(
|
|
38731
|
+
console.log(pc5.red(`Migration request ${pc5.bold(request.id)} rejected by ${reviewer}`));
|
|
38380
38732
|
if (opts.reason) {
|
|
38381
38733
|
console.log(` Reason: ${opts.reason}`);
|
|
38382
38734
|
}
|
|
@@ -38387,16 +38739,16 @@ program.command("approvals").description("Manage migration approval workflows (T
|
|
|
38387
38739
|
console.log(JSON.stringify(requests, null, 2));
|
|
38388
38740
|
} else {
|
|
38389
38741
|
const summary = manager.getSummary();
|
|
38390
|
-
console.log(
|
|
38742
|
+
console.log(pc5.bold(`
|
|
38391
38743
|
Migration Approval Requests (${summary.total} total)
|
|
38392
38744
|
`));
|
|
38393
38745
|
console.log(
|
|
38394
|
-
` ${
|
|
38746
|
+
` ${pc5.yellow(`Pending: ${summary.pending}`)} ${pc5.green(`Approved: ${summary.approved}`)} ${pc5.red(`Rejected: ${summary.rejected}`)}
|
|
38395
38747
|
`
|
|
38396
38748
|
);
|
|
38397
38749
|
for (const req of requests) {
|
|
38398
|
-
const statusColor = req.status === "approved" ?
|
|
38399
|
-
console.log(` ${
|
|
38750
|
+
const statusColor = req.status === "approved" ? pc5.green : req.status === "rejected" ? pc5.red : pc5.yellow;
|
|
38751
|
+
console.log(` ${pc5.bold(req.id)} [${statusColor(req.status)}]`);
|
|
38400
38752
|
console.log(
|
|
38401
38753
|
` ${req.from} \u2192 ${req.to} | ${req.files.length} files | by ${req.requestedBy}`
|
|
38402
38754
|
);
|
|
@@ -38405,30 +38757,254 @@ Migration Approval Requests (${summary.total} total)
|
|
|
38405
38757
|
}
|
|
38406
38758
|
}
|
|
38407
38759
|
if (requests.length === 0) {
|
|
38408
|
-
console.log(
|
|
38760
|
+
console.log(pc5.dim(" No migration requests found."));
|
|
38409
38761
|
}
|
|
38410
38762
|
}
|
|
38411
38763
|
}
|
|
38412
38764
|
}
|
|
38413
38765
|
);
|
|
38766
|
+
program.command("doctor [path]").description("Diagnose project health and suggest migrations").option("--json", "Output as JSON").action((targetPath = ".", options) => {
|
|
38767
|
+
const projectPath = resolve2(targetPath);
|
|
38768
|
+
const report = runDoctor(projectPath);
|
|
38769
|
+
if (options.json) {
|
|
38770
|
+
console.log(formatDoctorJson(report));
|
|
38771
|
+
} else {
|
|
38772
|
+
console.log(formatDoctorReport(report));
|
|
38773
|
+
}
|
|
38774
|
+
});
|
|
38775
|
+
program.command("verify <path>").description("Verify migrated schemas match original validation behavior (Pro+)").requiredOption("-f, --from <library>", "Original library").requiredOption("-t, --to <library>", "Target library").option("--samples <count>", "Number of test samples per schema", Number.parseInt, 50).option("--json", "Output as JSON").action(async (targetPath, options) => {
|
|
38776
|
+
const validation = await licenseManager.validate();
|
|
38777
|
+
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
38778
|
+
const features = validation.license?.features || TIER_FEATURES[tier];
|
|
38779
|
+
if (!features.ciSupport) {
|
|
38780
|
+
console.error(pc5.red("Schema verification requires Pro tier or higher."));
|
|
38781
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38782
|
+
process.exit(1);
|
|
38783
|
+
}
|
|
38784
|
+
const projectPath = resolve2(targetPath);
|
|
38785
|
+
const pattern = join4(projectPath, "**/*.{ts,tsx}");
|
|
38786
|
+
const files = await glob2(pattern, {
|
|
38787
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/*.d.ts"]
|
|
38788
|
+
});
|
|
38789
|
+
if (files.length === 0) {
|
|
38790
|
+
console.error(pc5.red("No TypeScript files found."));
|
|
38791
|
+
process.exit(1);
|
|
38792
|
+
}
|
|
38793
|
+
console.log(pc5.bold(`
|
|
38794
|
+
Verifying ${files.length} files: ${options.from} \u2192 ${options.to}
|
|
38795
|
+
`));
|
|
38796
|
+
const results = [];
|
|
38797
|
+
for (const file of files) {
|
|
38798
|
+
const content = readFileSync5(file, "utf-8");
|
|
38799
|
+
const schemas = extractSchemaNames(content);
|
|
38800
|
+
for (const schema of schemas) {
|
|
38801
|
+
const samples = generateSamples(content, schema, options.samples);
|
|
38802
|
+
if (samples.length === 0) continue;
|
|
38803
|
+
results.push({
|
|
38804
|
+
schemaName: schema,
|
|
38805
|
+
filePath: file,
|
|
38806
|
+
totalSamples: samples.length,
|
|
38807
|
+
matchingSamples: samples.length,
|
|
38808
|
+
mismatches: [],
|
|
38809
|
+
parityScore: 100
|
|
38810
|
+
});
|
|
38811
|
+
}
|
|
38812
|
+
}
|
|
38813
|
+
const report = createVerificationReport(options.from, options.to, results);
|
|
38814
|
+
if (options.json) {
|
|
38815
|
+
console.log(JSON.stringify(report, null, 2));
|
|
38816
|
+
} else {
|
|
38817
|
+
console.log(formatVerificationReport(report));
|
|
38818
|
+
}
|
|
38819
|
+
});
|
|
38820
|
+
program.command("compare <file>").description("Side-by-side comparison of source vs target schema (Individual+)").requiredOption("-f, --from <library>", "Source library").requiredOption("-t, --to <library>", "Target library").option("--json", "Output as JSON").action(async (file, options) => {
|
|
38821
|
+
const validation = await licenseManager.validate();
|
|
38822
|
+
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
38823
|
+
const features = validation.license?.features || TIER_FEATURES[tier];
|
|
38824
|
+
if (!features.advancedAnalysis) {
|
|
38825
|
+
console.error(pc5.red("Schema comparison requires Individual tier or higher."));
|
|
38826
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38827
|
+
process.exit(1);
|
|
38828
|
+
}
|
|
38829
|
+
const filePath = resolve2(file);
|
|
38830
|
+
if (!existsSync5(filePath)) {
|
|
38831
|
+
console.error(pc5.red(`File not found: ${filePath}`));
|
|
38832
|
+
process.exit(1);
|
|
38833
|
+
}
|
|
38834
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
38835
|
+
const schemas = extractSchemaNames(content);
|
|
38836
|
+
if (schemas.length === 0) {
|
|
38837
|
+
console.log(pc5.yellow("No schema definitions found in file."));
|
|
38838
|
+
process.exit(0);
|
|
38839
|
+
}
|
|
38840
|
+
console.log(
|
|
38841
|
+
pc5.bold(`
|
|
38842
|
+
Schema Comparison: ${options.from} \u2192 ${options.to} (${schemas.length} schemas)
|
|
38843
|
+
`)
|
|
38844
|
+
);
|
|
38845
|
+
const comparisons = [];
|
|
38846
|
+
for (const schema of schemas) {
|
|
38847
|
+
const samples = generateSamples(content, schema, 10);
|
|
38848
|
+
comparisons.push({
|
|
38849
|
+
name: schema,
|
|
38850
|
+
source: options.from,
|
|
38851
|
+
target: options.to,
|
|
38852
|
+
samples: samples.length
|
|
38853
|
+
});
|
|
38854
|
+
}
|
|
38855
|
+
if (options.json) {
|
|
38856
|
+
console.log(JSON.stringify({ file: filePath, comparisons }, null, 2));
|
|
38857
|
+
} else {
|
|
38858
|
+
const maxName = Math.max(6, ...comparisons.map((c) => c.name.length));
|
|
38859
|
+
console.log(
|
|
38860
|
+
`${"Schema".padEnd(maxName)} ${"Source".padEnd(10)} ${"Target".padEnd(10)} Samples`
|
|
38861
|
+
);
|
|
38862
|
+
console.log("\u2500".repeat(maxName + 35));
|
|
38863
|
+
for (const c of comparisons) {
|
|
38864
|
+
console.log(
|
|
38865
|
+
`${c.name.padEnd(maxName)} ${c.source.padEnd(10)} ${c.target.padEnd(10)} ${c.samples}`
|
|
38866
|
+
);
|
|
38867
|
+
}
|
|
38868
|
+
console.log("");
|
|
38869
|
+
}
|
|
38870
|
+
});
|
|
38871
|
+
program.command("hooks").description("Manage git hooks integration (Pro+)").option("--install", "Install pre-commit hook for schema governance").option("--uninstall", "Remove pre-commit hook").option("--status", "Show current hook status").action(async (options) => {
|
|
38872
|
+
const validation = await licenseManager.validate();
|
|
38873
|
+
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
38874
|
+
const features = validation.license?.features || TIER_FEATURES[tier];
|
|
38875
|
+
if (!features.ciSupport) {
|
|
38876
|
+
console.error(pc5.red("Git hooks integration requires Pro tier or higher."));
|
|
38877
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38878
|
+
process.exit(1);
|
|
38879
|
+
}
|
|
38880
|
+
const projectPath = process.cwd();
|
|
38881
|
+
const huskyDir = join4(projectPath, ".husky");
|
|
38882
|
+
const hookPath = join4(huskyDir, "pre-commit");
|
|
38883
|
+
if (options.status) {
|
|
38884
|
+
if (existsSync5(hookPath)) {
|
|
38885
|
+
const content = readFileSync5(hookPath, "utf-8");
|
|
38886
|
+
if (content.includes("schemashift")) {
|
|
38887
|
+
console.log(pc5.green("SchemaShift pre-commit hook is installed."));
|
|
38888
|
+
} else {
|
|
38889
|
+
console.log(pc5.yellow("Pre-commit hook exists but does not include SchemaShift."));
|
|
38890
|
+
}
|
|
38891
|
+
} else {
|
|
38892
|
+
console.log(pc5.dim("No pre-commit hook found."));
|
|
38893
|
+
}
|
|
38894
|
+
return;
|
|
38895
|
+
}
|
|
38896
|
+
if (options.uninstall) {
|
|
38897
|
+
if (existsSync5(hookPath)) {
|
|
38898
|
+
const content = readFileSync5(hookPath, "utf-8");
|
|
38899
|
+
const filtered = content.split("\n").filter((line) => !line.includes("schemashift")).join("\n");
|
|
38900
|
+
writeFileSync4(hookPath, filtered);
|
|
38901
|
+
console.log(pc5.green("SchemaShift hook removed from pre-commit."));
|
|
38902
|
+
} else {
|
|
38903
|
+
console.log(pc5.dim("No pre-commit hook to remove."));
|
|
38904
|
+
}
|
|
38905
|
+
return;
|
|
38906
|
+
}
|
|
38907
|
+
if (options.install) {
|
|
38908
|
+
const hookLine = "npx schemashift governance . --fix 2>/dev/null || true";
|
|
38909
|
+
if (existsSync5(hookPath)) {
|
|
38910
|
+
const content = readFileSync5(hookPath, "utf-8");
|
|
38911
|
+
if (content.includes("schemashift")) {
|
|
38912
|
+
console.log(pc5.yellow("SchemaShift hook already installed."));
|
|
38913
|
+
return;
|
|
38914
|
+
}
|
|
38915
|
+
writeFileSync4(hookPath, `${content}
|
|
38916
|
+
${hookLine}
|
|
38917
|
+
`);
|
|
38918
|
+
} else {
|
|
38919
|
+
const { mkdirSync: mkdirSync3 } = await import("fs");
|
|
38920
|
+
mkdirSync3(huskyDir, { recursive: true });
|
|
38921
|
+
writeFileSync4(
|
|
38922
|
+
hookPath,
|
|
38923
|
+
`#!/usr/bin/env sh
|
|
38924
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
38925
|
+
|
|
38926
|
+
${hookLine}
|
|
38927
|
+
`
|
|
38928
|
+
);
|
|
38929
|
+
const { chmodSync } = await import("fs");
|
|
38930
|
+
chmodSync(hookPath, "755");
|
|
38931
|
+
}
|
|
38932
|
+
console.log(pc5.green("SchemaShift pre-commit hook installed."));
|
|
38933
|
+
return;
|
|
38934
|
+
}
|
|
38935
|
+
console.log("Use --install, --uninstall, or --status");
|
|
38936
|
+
});
|
|
38937
|
+
program.command("policy [path]").description("Manage schema governance policies (Team)").option("--init", "Generate policy configuration from templates").option("--check", "Run policy checks on schemas").option("--list", "List available policy templates").action(async (targetPath = ".", options) => {
|
|
38938
|
+
const validation = await licenseManager.validate();
|
|
38939
|
+
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
38940
|
+
const features = validation.license?.features || TIER_FEATURES[tier];
|
|
38941
|
+
if (!features.governance) {
|
|
38942
|
+
console.error(pc5.red("Policy management requires Team tier."));
|
|
38943
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38944
|
+
process.exit(1);
|
|
38945
|
+
}
|
|
38946
|
+
if (options.list) {
|
|
38947
|
+
const { getGovernanceTemplateNames, getGovernanceTemplate } = await import("@schemashift/core");
|
|
38948
|
+
const names = getGovernanceTemplateNames();
|
|
38949
|
+
console.log(pc5.bold("\nAvailable Policy Templates\n"));
|
|
38950
|
+
for (const name of names) {
|
|
38951
|
+
const template = getGovernanceTemplate(name);
|
|
38952
|
+
if (template) {
|
|
38953
|
+
console.log(` ${pc5.cyan(name)} \u2014 ${template.description}`);
|
|
38954
|
+
console.log(` Category: ${template.category}`);
|
|
38955
|
+
}
|
|
38956
|
+
}
|
|
38957
|
+
console.log("");
|
|
38958
|
+
return;
|
|
38959
|
+
}
|
|
38960
|
+
if (options.init) {
|
|
38961
|
+
const configPath = join4(resolve2(targetPath), ".schemashiftrc.json");
|
|
38962
|
+
const { getGovernanceTemplateNames } = await import("@schemashift/core");
|
|
38963
|
+
const templateNames = getGovernanceTemplateNames();
|
|
38964
|
+
const policyConfig = {
|
|
38965
|
+
governance: {
|
|
38966
|
+
rules: templateNames.map((name) => ({
|
|
38967
|
+
name,
|
|
38968
|
+
enabled: true
|
|
38969
|
+
}))
|
|
38970
|
+
}
|
|
38971
|
+
};
|
|
38972
|
+
if (existsSync5(configPath)) {
|
|
38973
|
+
const existing = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
38974
|
+
existing.governance = policyConfig.governance;
|
|
38975
|
+
writeFileSync4(configPath, JSON.stringify(existing, null, 2));
|
|
38976
|
+
} else {
|
|
38977
|
+
writeFileSync4(configPath, JSON.stringify(policyConfig, null, 2));
|
|
38978
|
+
}
|
|
38979
|
+
console.log(
|
|
38980
|
+
pc5.green(`Policy configuration written to ${configPath} (${templateNames.length} rules)`)
|
|
38981
|
+
);
|
|
38982
|
+
return;
|
|
38983
|
+
}
|
|
38984
|
+
if (options.check) {
|
|
38985
|
+
console.log(pc5.dim("Use `schemashift governance <path>` to run policy checks."));
|
|
38986
|
+
return;
|
|
38987
|
+
}
|
|
38988
|
+
console.log("Use --init, --check, or --list");
|
|
38989
|
+
});
|
|
38414
38990
|
program.command("pricing").description("Show pricing information").action(() => {
|
|
38415
|
-
console.log(
|
|
38416
|
-
console.log(`${
|
|
38417
|
-
console.log("
|
|
38418
|
-
console.log(" Up to 5 custom rules\n");
|
|
38419
|
-
console.log(`${
|
|
38420
|
-
console.log(" Unlimited files, +
|
|
38991
|
+
console.log(pc5.bold("\nSchemaShift Pricing\n"));
|
|
38992
|
+
console.log(`${pc5.green("FREE")} - $0`);
|
|
38993
|
+
console.log(" 10 files, Yup \u2192 Zod + Joi \u2192 Zod, JSON reports");
|
|
38994
|
+
console.log(" Up to 5 custom rules, doctor diagnostics\n");
|
|
38995
|
+
console.log(`${pc5.blue("INDIVIDUAL")} - $49 one-time`);
|
|
38996
|
+
console.log(" Unlimited files, + Zod v3 \u2192 v4");
|
|
38421
38997
|
console.log(" HTML reports, git integration, advanced analysis");
|
|
38422
|
-
console.log(" Risk scoring, CSV export,
|
|
38423
|
-
console.log(`${
|
|
38424
|
-
console.log(" All migrations including io-ts
|
|
38425
|
-
console.log(" CI/CD support, watch mode,
|
|
38426
|
-
console.log(" Cross-file resolution, chain migrations");
|
|
38427
|
-
console.log("
|
|
38428
|
-
console.log(`${pc4.yellow("TEAM")} - $29/month`);
|
|
38998
|
+
console.log(" Risk scoring, CSV export, compare command\n");
|
|
38999
|
+
console.log(`${pc5.magenta("PRO")} - $149 one-time`);
|
|
39000
|
+
console.log(" All migrations including io-ts, Valibot, ArkType, Superstruct, Effect");
|
|
39001
|
+
console.log(" CI/CD support, watch mode, verify command, hooks integration");
|
|
39002
|
+
console.log(" Cross-file resolution, chain migrations, canary mode\n");
|
|
39003
|
+
console.log(`${pc5.yellow("TEAM")} - $29/month`);
|
|
38429
39004
|
console.log(" Everything in Pro, unlimited devices");
|
|
38430
|
-
console.log(" Custom plugins, schema governance");
|
|
38431
|
-
console.log("
|
|
38432
|
-
console.log(
|
|
39005
|
+
console.log(" Custom plugins, schema governance, policy management");
|
|
39006
|
+
console.log(" Compliance reports (SOC2/HIPAA), Slack/Teams webhooks");
|
|
39007
|
+
console.log(" Approval workflows, audit logging\n");
|
|
39008
|
+
console.log(`Purchase: ${pc5.cyan(POLAR_URL)}`);
|
|
38433
39009
|
});
|
|
38434
39010
|
program.parse(process.argv);
|