schemashift-cli 0.10.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 +1094 -268
- package/dist/index.cjs +22 -2
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +18 -3
- package/package.json +11 -11
package/dist/cli.js
CHANGED
|
@@ -6,18 +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
|
+
ApprovalManager,
|
|
13
14
|
BehavioralWarningAnalyzer,
|
|
14
15
|
BundleEstimator,
|
|
15
16
|
CompatibilityAnalyzer,
|
|
17
|
+
createVerificationReport,
|
|
16
18
|
DetailedAnalyzer,
|
|
17
19
|
detectFormLibraries,
|
|
20
|
+
extractSchemaNames,
|
|
21
|
+
formatVerificationReport,
|
|
18
22
|
GovernanceEngine,
|
|
23
|
+
GovernanceFixer,
|
|
24
|
+
GraphExporter,
|
|
25
|
+
generateSamples,
|
|
26
|
+
getAllMigrationTemplates,
|
|
27
|
+
getMigrationTemplate,
|
|
19
28
|
IncrementalTracker,
|
|
20
|
-
loadConfig,
|
|
29
|
+
loadConfig as loadConfig2,
|
|
21
30
|
MigrationAuditLog,
|
|
22
31
|
MigrationChain,
|
|
23
32
|
PerformanceAnalyzer,
|
|
@@ -35814,8 +35823,8 @@ var TIER_FEATURES = {
|
|
|
35814
35823
|
"free"
|
|
35815
35824
|
/* FREE */
|
|
35816
35825
|
]: {
|
|
35817
|
-
maxFiles:
|
|
35818
|
-
migrations: ["yup->zod"],
|
|
35826
|
+
maxFiles: 10,
|
|
35827
|
+
migrations: ["yup->zod", "joi->zod"],
|
|
35819
35828
|
devices: 1,
|
|
35820
35829
|
ciSupport: false,
|
|
35821
35830
|
prioritySupport: false,
|
|
@@ -36256,7 +36265,7 @@ import { createValibotToZodHandler, createZodToValibotHandler } from "@schemashi
|
|
|
36256
36265
|
import { Command } from "commander";
|
|
36257
36266
|
import { glob as glob2 } from "glob";
|
|
36258
36267
|
import { Listr } from "listr2";
|
|
36259
|
-
import
|
|
36268
|
+
import pc5 from "picocolors";
|
|
36260
36269
|
|
|
36261
36270
|
// src/backup.ts
|
|
36262
36271
|
init_esm_shims();
|
|
@@ -36507,6 +36516,73 @@ function computeHunks(original, transformed, context = 3) {
|
|
|
36507
36516
|
if (currentHunk) hunks.push(currentHunk);
|
|
36508
36517
|
return hunks;
|
|
36509
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
|
+
}
|
|
36510
36586
|
function generateDiffSummary(results) {
|
|
36511
36587
|
const changed = results.filter(
|
|
36512
36588
|
(r) => r.success && r.transformedCode && r.originalCode !== r.transformedCode
|
|
@@ -36522,25 +36598,287 @@ function generateDiffSummary(results) {
|
|
|
36522
36598
|
return parts.join(", ");
|
|
36523
36599
|
}
|
|
36524
36600
|
|
|
36525
|
-
// src/
|
|
36601
|
+
// src/doctor.ts
|
|
36526
36602
|
init_esm_shims();
|
|
36527
|
-
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";
|
|
36528
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";
|
|
36529
36867
|
function formatErrorSummary(errors) {
|
|
36530
36868
|
const lines = [];
|
|
36531
36869
|
lines.push(
|
|
36532
|
-
|
|
36870
|
+
pc3.red(pc3.bold(`
|
|
36533
36871
|
${errors.length} file${errors.length !== 1 ? "s" : ""} with errors:
|
|
36534
36872
|
`))
|
|
36535
36873
|
);
|
|
36536
36874
|
for (const { filePath, errors: fileErrors } of errors) {
|
|
36537
36875
|
const relativePath = filePath.replace(`${process.cwd()}/`, "");
|
|
36538
|
-
lines.push(
|
|
36876
|
+
lines.push(pc3.red(` ${relativePath}`));
|
|
36539
36877
|
for (const err of fileErrors) {
|
|
36540
36878
|
const loc = err.line ? `:${err.line}` : "";
|
|
36541
|
-
lines.push(
|
|
36879
|
+
lines.push(pc3.dim(` - ${err.message}${loc}`));
|
|
36542
36880
|
if (err.suggestion) {
|
|
36543
|
-
lines.push(
|
|
36881
|
+
lines.push(pc3.cyan(` \u2192 ${err.suggestion}`));
|
|
36544
36882
|
}
|
|
36545
36883
|
}
|
|
36546
36884
|
}
|
|
@@ -36550,11 +36888,11 @@ ${errors.length} file${errors.length !== 1 ? "s" : ""} with errors:
|
|
|
36550
36888
|
// src/git.ts
|
|
36551
36889
|
init_esm_shims();
|
|
36552
36890
|
import { execFileSync } from "child_process";
|
|
36553
|
-
import { existsSync as
|
|
36891
|
+
import { existsSync as existsSync4 } from "fs";
|
|
36554
36892
|
var GitIntegration = class {
|
|
36555
36893
|
isGitRepo;
|
|
36556
36894
|
constructor() {
|
|
36557
|
-
this.isGitRepo =
|
|
36895
|
+
this.isGitRepo = existsSync4(".git");
|
|
36558
36896
|
}
|
|
36559
36897
|
isAvailable() {
|
|
36560
36898
|
if (!this.isGitRepo) return false;
|
|
@@ -36923,7 +37261,7 @@ import { watch } from "fs";
|
|
|
36923
37261
|
import { relative as relative2 } from "path";
|
|
36924
37262
|
import { glob } from "glob";
|
|
36925
37263
|
import { minimatch } from "minimatch";
|
|
36926
|
-
import
|
|
37264
|
+
import pc4 from "picocolors";
|
|
36927
37265
|
var WatchMode = class {
|
|
36928
37266
|
watchers = [];
|
|
36929
37267
|
debounceTimers = /* @__PURE__ */ new Map();
|
|
@@ -36931,16 +37269,16 @@ var WatchMode = class {
|
|
|
36931
37269
|
const files = await glob(options.patterns, {
|
|
36932
37270
|
ignore: options.exclude
|
|
36933
37271
|
});
|
|
36934
|
-
console.log(
|
|
37272
|
+
console.log(pc4.cyan(`
|
|
36935
37273
|
Watching ${files.length} files for changes...
|
|
36936
37274
|
`));
|
|
36937
37275
|
if (options.dependents && options.dependents.size > 0) {
|
|
36938
37276
|
console.log(
|
|
36939
|
-
|
|
37277
|
+
pc4.dim(`Dependency-aware mode: ${options.dependents.size} files with dependents
|
|
36940
37278
|
`)
|
|
36941
37279
|
);
|
|
36942
37280
|
}
|
|
36943
|
-
console.log(
|
|
37281
|
+
console.log(pc4.dim("Press Ctrl+C to stop\n"));
|
|
36944
37282
|
const directories = new Set(files.map((f) => f.split("/").slice(0, -1).join("/")));
|
|
36945
37283
|
for (const dir of directories) {
|
|
36946
37284
|
const watcher = watch(dir || ".", { recursive: true }, async (_event, filename) => {
|
|
@@ -36956,23 +37294,23 @@ Watching ${files.length} files for changes...
|
|
|
36956
37294
|
this.debounceTimers.set(
|
|
36957
37295
|
fullPath,
|
|
36958
37296
|
setTimeout(async () => {
|
|
36959
|
-
console.log(
|
|
37297
|
+
console.log(pc4.yellow(`
|
|
36960
37298
|
Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
36961
37299
|
try {
|
|
36962
37300
|
await options.onTransform(fullPath);
|
|
36963
|
-
console.log(
|
|
37301
|
+
console.log(pc4.green(`Transformed successfully`));
|
|
36964
37302
|
const dependentFiles = options.dependents?.get(fullPath);
|
|
36965
37303
|
if (dependentFiles && dependentFiles.length > 0) {
|
|
36966
37304
|
console.log(
|
|
36967
|
-
|
|
37305
|
+
pc4.cyan(` Re-transforming ${dependentFiles.length} dependent file(s)...`)
|
|
36968
37306
|
);
|
|
36969
37307
|
for (const depFile of dependentFiles) {
|
|
36970
37308
|
try {
|
|
36971
37309
|
await options.onTransform(depFile);
|
|
36972
|
-
console.log(
|
|
37310
|
+
console.log(pc4.green(` \u2713 ${relative2(process.cwd(), depFile)}`));
|
|
36973
37311
|
} catch (error) {
|
|
36974
37312
|
console.error(
|
|
36975
|
-
|
|
37313
|
+
pc4.red(
|
|
36976
37314
|
` \u2717 ${relative2(process.cwd(), depFile)}: ${error instanceof Error ? error.message : String(error)}`
|
|
36977
37315
|
)
|
|
36978
37316
|
);
|
|
@@ -36982,7 +37320,7 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
|
36982
37320
|
console.log("");
|
|
36983
37321
|
} catch (error) {
|
|
36984
37322
|
console.error(
|
|
36985
|
-
|
|
37323
|
+
pc4.red(
|
|
36986
37324
|
`Transform failed: ${error instanceof Error ? error.message : String(error)}
|
|
36987
37325
|
`
|
|
36988
37326
|
)
|
|
@@ -37029,7 +37367,7 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
|
37029
37367
|
|
|
37030
37368
|
// src/cli.ts
|
|
37031
37369
|
var __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
37032
|
-
var pkg = JSON.parse(
|
|
37370
|
+
var pkg = JSON.parse(readFileSync5(join4(__dirname2, "..", "package.json"), "utf-8"));
|
|
37033
37371
|
var program = new Command();
|
|
37034
37372
|
var licenseManager = new LicenseManager();
|
|
37035
37373
|
var engine = new TransformEngine();
|
|
@@ -37049,8 +37387,8 @@ var POLAR_URL = "https://schemashift.qwady.app";
|
|
|
37049
37387
|
program.name("schemashift").version(pkg.version).description("TypeScript schema migration CLI");
|
|
37050
37388
|
program.command("init").description("Create .schemashiftrc.json config file").option("-f, --force", "Overwrite existing config").action((options) => {
|
|
37051
37389
|
const configPath = ".schemashiftrc.json";
|
|
37052
|
-
if (
|
|
37053
|
-
console.error(
|
|
37390
|
+
if (existsSync5(configPath) && !options.force) {
|
|
37391
|
+
console.error(pc5.red("Config file already exists. Use --force to overwrite."));
|
|
37054
37392
|
process.exit(1);
|
|
37055
37393
|
}
|
|
37056
37394
|
const defaultConfig = {
|
|
@@ -37071,8 +37409,8 @@ program.command("init").description("Create .schemashiftrc.json config file").op
|
|
|
37071
37409
|
ci: false
|
|
37072
37410
|
};
|
|
37073
37411
|
writeFileSync4(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
37074
|
-
console.log(
|
|
37075
|
-
console.log(
|
|
37412
|
+
console.log(pc5.green("Created .schemashiftrc.json"));
|
|
37413
|
+
console.log(pc5.dim("Edit the file to customize your migration settings."));
|
|
37076
37414
|
});
|
|
37077
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(
|
|
37078
37416
|
"--performance <migration>",
|
|
@@ -37080,10 +37418,10 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37080
37418
|
).option("--dedup", "Detect duplicate type definitions (Individual+)").action(async (targetPath, options) => {
|
|
37081
37419
|
const analyzer = new SchemaAnalyzer();
|
|
37082
37420
|
let files;
|
|
37083
|
-
if (
|
|
37421
|
+
if (existsSync5(targetPath) && statSync(targetPath).isFile()) {
|
|
37084
37422
|
files = [resolve2(targetPath)];
|
|
37085
37423
|
} else {
|
|
37086
|
-
files = await glob2(
|
|
37424
|
+
files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
37087
37425
|
ignore: ["**/node_modules/**", "**/dist/**"]
|
|
37088
37426
|
});
|
|
37089
37427
|
}
|
|
@@ -37096,8 +37434,8 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37096
37434
|
const validation = await licenseManager.validate();
|
|
37097
37435
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37098
37436
|
if (!features.advancedAnalysis) {
|
|
37099
|
-
console.error(
|
|
37100
|
-
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)}`);
|
|
37101
37439
|
process.exit(1);
|
|
37102
37440
|
}
|
|
37103
37441
|
}
|
|
@@ -37105,12 +37443,12 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37105
37443
|
console.log(JSON.stringify(result, null, 2));
|
|
37106
37444
|
return;
|
|
37107
37445
|
}
|
|
37108
|
-
console.log(
|
|
37109
|
-
console.log(`Total files: ${
|
|
37110
|
-
console.log(`Files with schemas: ${
|
|
37111
|
-
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())}`);
|
|
37112
37450
|
if (result.schemas.length > 0) {
|
|
37113
|
-
console.log(
|
|
37451
|
+
console.log(pc5.bold("\nSchemas by library:\n"));
|
|
37114
37452
|
const byLibrary = /* @__PURE__ */ new Map();
|
|
37115
37453
|
for (const schema of result.schemas) {
|
|
37116
37454
|
byLibrary.set(schema.library, (byLibrary.get(schema.library) || 0) + 1);
|
|
@@ -37119,13 +37457,13 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37119
37457
|
console.log(` ${lib}: ${count}`);
|
|
37120
37458
|
}
|
|
37121
37459
|
if (options.verbose) {
|
|
37122
|
-
console.log(
|
|
37460
|
+
console.log(pc5.bold("\nDetailed schemas:\n"));
|
|
37123
37461
|
for (const schema of result.schemas.slice(0, 20)) {
|
|
37124
|
-
console.log(` ${
|
|
37125
|
-
console.log(` ${
|
|
37462
|
+
console.log(` ${pc5.cyan(schema.name)} (${schema.library})`);
|
|
37463
|
+
console.log(` ${pc5.dim(schema.filePath)}:${schema.lineNumber}`);
|
|
37126
37464
|
}
|
|
37127
37465
|
if (result.schemas.length > 20) {
|
|
37128
|
-
console.log(
|
|
37466
|
+
console.log(pc5.dim(` ... and ${result.schemas.length - 20} more`));
|
|
37129
37467
|
}
|
|
37130
37468
|
}
|
|
37131
37469
|
}
|
|
@@ -37136,43 +37474,43 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37136
37474
|
const projectPath = resolve2(targetPath);
|
|
37137
37475
|
const detailedResult = detailedAnalyzer.generateDetailedResult(
|
|
37138
37476
|
complexities,
|
|
37139
|
-
|
|
37477
|
+
existsSync5(join4(projectPath, "package.json")) ? projectPath : process.cwd()
|
|
37140
37478
|
);
|
|
37141
37479
|
if (options.json) {
|
|
37142
37480
|
console.log(JSON.stringify(detailedResult, null, 2));
|
|
37143
37481
|
return;
|
|
37144
37482
|
}
|
|
37145
|
-
console.log(
|
|
37146
|
-
console.log(`Average complexity: ${
|
|
37147
|
-
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())}`);
|
|
37148
37486
|
if (detailedResult.libraryVersions.length > 0) {
|
|
37149
|
-
console.log(
|
|
37487
|
+
console.log(pc5.bold("\nDetected library versions:\n"));
|
|
37150
37488
|
for (const v of detailedResult.libraryVersions) {
|
|
37151
|
-
console.log(` ${v.library}: ${
|
|
37489
|
+
console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
|
|
37152
37490
|
}
|
|
37153
37491
|
}
|
|
37154
37492
|
if (options.complexity || options.detailed) {
|
|
37155
37493
|
const sorted = [...complexities].sort((a, b) => b.score - a.score);
|
|
37156
|
-
console.log(
|
|
37494
|
+
console.log(pc5.bold("\nPer-schema complexity:\n"));
|
|
37157
37495
|
for (const c of sorted.slice(0, 20)) {
|
|
37158
|
-
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;
|
|
37159
37497
|
console.log(
|
|
37160
|
-
` ${
|
|
37498
|
+
` ${pc5.cyan(c.schemaName)} ${levelColor(`[${c.level}]`)} score=${c.score}`
|
|
37161
37499
|
);
|
|
37162
37500
|
console.log(
|
|
37163
37501
|
` chain=${c.chainLength} nested=${c.nestedDepth} validations=${c.validationCount}`
|
|
37164
37502
|
);
|
|
37165
|
-
console.log(` ${
|
|
37503
|
+
console.log(` ${pc5.dim(c.filePath)}:${c.lineNumber}`);
|
|
37166
37504
|
}
|
|
37167
37505
|
if (sorted.length > 20) {
|
|
37168
|
-
console.log(
|
|
37506
|
+
console.log(pc5.dim(` ... and ${sorted.length - 20} more`));
|
|
37169
37507
|
}
|
|
37170
37508
|
}
|
|
37171
37509
|
}
|
|
37172
37510
|
if (options.readiness) {
|
|
37173
37511
|
const parts = options.readiness.split("->");
|
|
37174
37512
|
if (parts.length !== 2) {
|
|
37175
|
-
console.error(
|
|
37513
|
+
console.error(pc5.red("Invalid readiness format. Use: --readiness yup->zod"));
|
|
37176
37514
|
process.exit(1);
|
|
37177
37515
|
}
|
|
37178
37516
|
const [from, to] = parts;
|
|
@@ -37239,21 +37577,21 @@ program.command("analyze <path>").description("Analyze schemas in your project")
|
|
|
37239
37577
|
console.log(JSON.stringify(readiness, null, 2));
|
|
37240
37578
|
return;
|
|
37241
37579
|
}
|
|
37242
|
-
const riskColor = readiness.riskLevel === "critical" ?
|
|
37243
|
-
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(`
|
|
37244
37582
|
Migration Readiness: ${from} -> ${to}
|
|
37245
37583
|
`));
|
|
37246
37584
|
console.log(`Readiness: ${riskColor(`${readiness.readinessPercent}%`)}`);
|
|
37247
37585
|
console.log(`Risk level: ${riskColor(readiness.riskLevel)}`);
|
|
37248
37586
|
console.log(`Total schemas: ${readiness.totalSchemas}`);
|
|
37249
|
-
console.log(`Supported: ${
|
|
37587
|
+
console.log(`Supported: ${pc5.green(readiness.supportedSchemas.toString())}`);
|
|
37250
37588
|
console.log(
|
|
37251
|
-
`Estimated manual fixes: ${
|
|
37589
|
+
`Estimated manual fixes: ${pc5.yellow(readiness.estimatedManualFixes.toString())}`
|
|
37252
37590
|
);
|
|
37253
37591
|
if (readiness.unsupportedPatterns.length > 0) {
|
|
37254
|
-
console.log(
|
|
37592
|
+
console.log(pc5.bold("\nUnsupported patterns:\n"));
|
|
37255
37593
|
for (const p of readiness.unsupportedPatterns) {
|
|
37256
|
-
console.log(` ${
|
|
37594
|
+
console.log(` ${pc5.red(p)}`);
|
|
37257
37595
|
}
|
|
37258
37596
|
}
|
|
37259
37597
|
}
|
|
@@ -37261,7 +37599,7 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37261
37599
|
if (options.behavioral) {
|
|
37262
37600
|
const migParts = options.behavioral.split("->");
|
|
37263
37601
|
if (migParts.length !== 2) {
|
|
37264
|
-
console.error(
|
|
37602
|
+
console.error(pc5.red("Invalid format. Use: --behavioral yup->zod"));
|
|
37265
37603
|
} else {
|
|
37266
37604
|
const [bFrom, bTo] = migParts;
|
|
37267
37605
|
const behavioralAnalyzer = new BehavioralWarningAnalyzer();
|
|
@@ -37272,7 +37610,7 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37272
37610
|
bTo
|
|
37273
37611
|
);
|
|
37274
37612
|
if (behavioralResult.warnings.length > 0) {
|
|
37275
|
-
console.log(
|
|
37613
|
+
console.log(pc5.bold("\nBehavioral Difference Warnings\n"));
|
|
37276
37614
|
const byCategory = /* @__PURE__ */ new Map();
|
|
37277
37615
|
for (const w of behavioralResult.warnings) {
|
|
37278
37616
|
const existing = byCategory.get(w.category) ?? [];
|
|
@@ -37280,17 +37618,17 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37280
37618
|
byCategory.set(w.category, existing);
|
|
37281
37619
|
}
|
|
37282
37620
|
for (const [category, catWarnings] of byCategory) {
|
|
37283
|
-
console.log(
|
|
37621
|
+
console.log(pc5.yellow(` [${category}] ${catWarnings.length} warning(s)`));
|
|
37284
37622
|
for (const w of catWarnings.slice(0, 5)) {
|
|
37285
37623
|
console.log(` ${w.message}`);
|
|
37286
|
-
console.log(` ${
|
|
37624
|
+
console.log(` ${pc5.dim(w.detail)}`);
|
|
37287
37625
|
}
|
|
37288
37626
|
if (catWarnings.length > 5) {
|
|
37289
|
-
console.log(
|
|
37627
|
+
console.log(pc5.dim(` ... and ${catWarnings.length - 5} more`));
|
|
37290
37628
|
}
|
|
37291
37629
|
}
|
|
37292
37630
|
} else {
|
|
37293
|
-
console.log(
|
|
37631
|
+
console.log(pc5.green("\nNo behavioral difference warnings found."));
|
|
37294
37632
|
}
|
|
37295
37633
|
}
|
|
37296
37634
|
}
|
|
@@ -37298,12 +37636,12 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37298
37636
|
const validation = await licenseManager.validate();
|
|
37299
37637
|
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37300
37638
|
if (!tier.bundleEstimator) {
|
|
37301
|
-
console.error(
|
|
37302
|
-
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)}`);
|
|
37303
37641
|
} else {
|
|
37304
37642
|
const migParts = options.bundle.split("->");
|
|
37305
37643
|
if (migParts.length !== 2) {
|
|
37306
|
-
console.error(
|
|
37644
|
+
console.error(pc5.red("Invalid format. Use: --bundle zod->valibot"));
|
|
37307
37645
|
} else {
|
|
37308
37646
|
const [bFrom, bTo] = migParts;
|
|
37309
37647
|
const bundleEstimator = new BundleEstimator();
|
|
@@ -37313,16 +37651,16 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37313
37651
|
bFrom,
|
|
37314
37652
|
bTo
|
|
37315
37653
|
);
|
|
37316
|
-
console.log(
|
|
37654
|
+
console.log(pc5.bold("\nBundle Size Estimation\n"));
|
|
37317
37655
|
console.log(
|
|
37318
|
-
` ${estimate.from.library}: ~${
|
|
37656
|
+
` ${estimate.from.library}: ~${pc5.cyan(`${estimate.from.minifiedGzipKb}kB`)}`
|
|
37319
37657
|
);
|
|
37320
|
-
console.log(` ${estimate.to.library}: ~${
|
|
37658
|
+
console.log(` ${estimate.to.library}: ~${pc5.cyan(`${estimate.to.minifiedGzipKb}kB`)}`);
|
|
37321
37659
|
console.log(
|
|
37322
|
-
` Delta: ${
|
|
37660
|
+
` Delta: ${pc5.yellow(`${estimate.estimatedDelta > 0 ? "+" : ""}${estimate.estimatedDelta.toFixed(1)}kB (${estimate.deltaPercent.toFixed(0)}%)`)}`
|
|
37323
37661
|
);
|
|
37324
37662
|
console.log(`
|
|
37325
|
-
${
|
|
37663
|
+
${pc5.dim(estimate.summary)}`);
|
|
37326
37664
|
}
|
|
37327
37665
|
}
|
|
37328
37666
|
}
|
|
@@ -37330,12 +37668,12 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37330
37668
|
const validation = await licenseManager.validate();
|
|
37331
37669
|
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37332
37670
|
if (!tier.performanceAnalyzer) {
|
|
37333
|
-
console.error(
|
|
37334
|
-
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)}`);
|
|
37335
37673
|
} else {
|
|
37336
37674
|
const migParts = options.performance.split("->");
|
|
37337
37675
|
if (migParts.length !== 2) {
|
|
37338
|
-
console.error(
|
|
37676
|
+
console.error(pc5.red("Invalid format. Use: --performance zod-v3->v4"));
|
|
37339
37677
|
} else {
|
|
37340
37678
|
const [pFrom, pTo] = migParts;
|
|
37341
37679
|
const perfAnalyzer = new PerformanceAnalyzer();
|
|
@@ -37346,16 +37684,16 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37346
37684
|
pTo
|
|
37347
37685
|
);
|
|
37348
37686
|
if (perfResult.warnings.length > 0) {
|
|
37349
|
-
console.log(
|
|
37687
|
+
console.log(pc5.bold("\nPerformance Impact Analysis\n"));
|
|
37350
37688
|
for (const w of perfResult.warnings) {
|
|
37351
|
-
const color = w.severity === "error" ?
|
|
37689
|
+
const color = w.severity === "error" ? pc5.red : w.severity === "warning" ? pc5.yellow : pc5.dim;
|
|
37352
37690
|
console.log(` ${color(`[${w.severity}]`)} ${w.message}`);
|
|
37353
|
-
console.log(` ${
|
|
37691
|
+
console.log(` ${pc5.dim(w.detail)}`);
|
|
37354
37692
|
}
|
|
37355
37693
|
console.log(`
|
|
37356
|
-
${
|
|
37694
|
+
${pc5.dim(perfResult.recommendation)}`);
|
|
37357
37695
|
} else {
|
|
37358
|
-
console.log(
|
|
37696
|
+
console.log(pc5.green("\nNo performance concerns detected."));
|
|
37359
37697
|
}
|
|
37360
37698
|
}
|
|
37361
37699
|
}
|
|
@@ -37364,25 +37702,25 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37364
37702
|
const validation = await licenseManager.validate();
|
|
37365
37703
|
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37366
37704
|
if (!tier.typeDeduplication) {
|
|
37367
|
-
console.error(
|
|
37368
|
-
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)}`);
|
|
37369
37707
|
} else {
|
|
37370
37708
|
const dedupDetector = new TypeDedupDetector();
|
|
37371
37709
|
const sourceFiles = analyzer.getProject().getSourceFiles();
|
|
37372
37710
|
const dedupResult = dedupDetector.detect(sourceFiles);
|
|
37373
37711
|
if (dedupResult.candidates.length > 0) {
|
|
37374
|
-
console.log(
|
|
37712
|
+
console.log(pc5.bold("\nDuplicate Type Candidates\n"));
|
|
37375
37713
|
for (const c of dedupResult.candidates.slice(0, 10)) {
|
|
37376
|
-
console.log(` ${
|
|
37377
|
-
console.log(` Type: ${
|
|
37378
|
-
console.log(` Schema: ${
|
|
37379
|
-
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)}`);
|
|
37380
37718
|
}
|
|
37381
37719
|
if (dedupResult.candidates.length > 10) {
|
|
37382
|
-
console.log(
|
|
37720
|
+
console.log(pc5.dim(` ... and ${dedupResult.candidates.length - 10} more`));
|
|
37383
37721
|
}
|
|
37384
37722
|
} else {
|
|
37385
|
-
console.log(
|
|
37723
|
+
console.log(pc5.green("\nNo duplicate type candidates found."));
|
|
37386
37724
|
}
|
|
37387
37725
|
}
|
|
37388
37726
|
}
|
|
@@ -37391,24 +37729,66 @@ program.command("migrate <path>").description("Migrate schemas from one library
|
|
|
37391
37729
|
"--max-risk-score <score>",
|
|
37392
37730
|
"Exit 1 if any file exceeds risk score (Team+)",
|
|
37393
37731
|
Number.parseInt
|
|
37394
|
-
).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+)").
|
|
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) => {
|
|
37395
37737
|
const startTime = Date.now();
|
|
37738
|
+
if (options.preset) {
|
|
37739
|
+
const validation2 = await licenseManager.validate();
|
|
37740
|
+
const features2 = validation2.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37741
|
+
if (!features2.advancedAnalysis) {
|
|
37742
|
+
console.error(pc5.red("Migration presets require Individual tier or higher."));
|
|
37743
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37744
|
+
process.exit(1);
|
|
37745
|
+
}
|
|
37746
|
+
const preset = getMigrationTemplate(options.preset);
|
|
37747
|
+
if (!preset) {
|
|
37748
|
+
console.error(pc5.red(`Unknown preset: ${options.preset}`));
|
|
37749
|
+
const names = getAllMigrationTemplates().map((t) => t.name);
|
|
37750
|
+
console.log(`Available presets: ${names.join(", ")}`);
|
|
37751
|
+
process.exit(1);
|
|
37752
|
+
}
|
|
37753
|
+
const step = preset.migrationSteps[0];
|
|
37754
|
+
if (step && !options.from) options.from = step.from;
|
|
37755
|
+
if (step && !options.to) options.to = step.to;
|
|
37756
|
+
console.log(pc5.bold(`
|
|
37757
|
+
Using preset: ${preset.name}`));
|
|
37758
|
+
console.log(pc5.dim(preset.description));
|
|
37759
|
+
if (preset.preChecks.length > 0) {
|
|
37760
|
+
console.log(pc5.bold("\nPre-migration checklist:"));
|
|
37761
|
+
for (const check of preset.preChecks) {
|
|
37762
|
+
console.log(` ${pc5.yellow("\u2022")} ${check.description}`);
|
|
37763
|
+
}
|
|
37764
|
+
}
|
|
37765
|
+
if (preset.packageChanges.length > 0) {
|
|
37766
|
+
console.log(pc5.bold("\nRecommended package changes:"));
|
|
37767
|
+
for (const change of preset.packageChanges) {
|
|
37768
|
+
const verb = change.action === "install" ? "+" : change.action === "remove" ? "-" : "\u2191";
|
|
37769
|
+
console.log(
|
|
37770
|
+
` ${pc5.cyan(verb)} ${change.package}${change.version ? `@${change.version}` : ""}`
|
|
37771
|
+
);
|
|
37772
|
+
}
|
|
37773
|
+
}
|
|
37774
|
+
console.log("");
|
|
37775
|
+
}
|
|
37396
37776
|
if (!options.chain && (!options.from || !options.to)) {
|
|
37397
|
-
console.error(
|
|
37398
|
-
console.error(
|
|
37399
|
-
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"));
|
|
37400
37780
|
process.exit(1);
|
|
37401
37781
|
}
|
|
37402
37782
|
if (options.maxRiskScore !== void 0 && Number.isNaN(options.maxRiskScore)) {
|
|
37403
|
-
console.error(
|
|
37783
|
+
console.error(pc5.red("--max-risk-score must be a valid number."));
|
|
37404
37784
|
process.exit(1);
|
|
37405
37785
|
}
|
|
37406
37786
|
let config2;
|
|
37407
37787
|
try {
|
|
37408
|
-
config2 = await
|
|
37788
|
+
config2 = await loadConfig2(options.config);
|
|
37409
37789
|
} catch (error) {
|
|
37410
37790
|
const msg = error instanceof Error ? error.message : String(error);
|
|
37411
|
-
console.warn(
|
|
37791
|
+
console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
|
|
37412
37792
|
config2 = {
|
|
37413
37793
|
include: ["**/*.ts", "**/*.tsx"],
|
|
37414
37794
|
exclude: ["**/node_modules/**", "**/dist/**"],
|
|
@@ -37418,95 +37798,95 @@ program.command("migrate <path>").description("Migrate schemas from one library
|
|
|
37418
37798
|
}
|
|
37419
37799
|
const validation = await licenseManager.validate();
|
|
37420
37800
|
if (!validation.valid) {
|
|
37421
|
-
console.error(
|
|
37801
|
+
console.error(pc5.red(`License error: ${validation.error}`));
|
|
37422
37802
|
process.exit(1);
|
|
37423
37803
|
}
|
|
37424
37804
|
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
37425
37805
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37426
37806
|
const migrationPath = `${options.from}->${options.to}`;
|
|
37427
37807
|
if (!canUseMigration(tier, options.from, options.to)) {
|
|
37428
|
-
console.error(
|
|
37429
|
-
console.log(`Your tier: ${
|
|
37430
|
-
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)}`);
|
|
37431
37811
|
process.exit(1);
|
|
37432
37812
|
}
|
|
37433
37813
|
if (options.ci && !features.ciSupport) {
|
|
37434
|
-
console.error(
|
|
37435
|
-
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)}`);
|
|
37436
37816
|
process.exit(1);
|
|
37437
37817
|
}
|
|
37438
37818
|
const customRulesCount = config2.customRules?.length ?? 0;
|
|
37439
37819
|
if (customRulesCount > features.customRulesLimit) {
|
|
37440
37820
|
console.error(
|
|
37441
|
-
|
|
37821
|
+
pc5.red(
|
|
37442
37822
|
`Custom rules limit exceeded. You have ${customRulesCount} rules, max ${features.customRulesLimit} for ${tier} tier.`
|
|
37443
37823
|
)
|
|
37444
37824
|
);
|
|
37445
|
-
console.log(`Upgrade at: ${
|
|
37825
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37446
37826
|
process.exit(1);
|
|
37447
37827
|
}
|
|
37448
37828
|
if ((options.gitBranch || options.gitCommit) && !features.gitIntegration) {
|
|
37449
|
-
console.error(
|
|
37450
|
-
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)}`);
|
|
37451
37831
|
process.exit(1);
|
|
37452
37832
|
}
|
|
37453
37833
|
if (options.crossFile && !features.crossFileResolution) {
|
|
37454
|
-
console.error(
|
|
37455
|
-
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)}`);
|
|
37456
37836
|
process.exit(1);
|
|
37457
37837
|
}
|
|
37458
37838
|
if (options.chain && !features.chainMigrations) {
|
|
37459
|
-
console.error(
|
|
37460
|
-
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)}`);
|
|
37461
37841
|
process.exit(1);
|
|
37462
37842
|
}
|
|
37463
37843
|
if (options.compatCheck && !features.compatCheck) {
|
|
37464
|
-
console.error(
|
|
37465
|
-
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)}`);
|
|
37466
37846
|
process.exit(1);
|
|
37467
37847
|
}
|
|
37468
37848
|
if (options.failOnWarnings && !features.failOnWarnings) {
|
|
37469
|
-
console.error(
|
|
37470
|
-
console.log(`Upgrade at: ${
|
|
37849
|
+
console.error(pc5.red("--fail-on-warnings requires Team tier."));
|
|
37850
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37471
37851
|
process.exit(1);
|
|
37472
37852
|
}
|
|
37473
37853
|
if (options.maxRiskScore !== void 0 && !features.maxRiskScoreThreshold) {
|
|
37474
|
-
console.error(
|
|
37475
|
-
console.log(`Upgrade at: ${
|
|
37854
|
+
console.error(pc5.red("--max-risk-score requires Team tier."));
|
|
37855
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37476
37856
|
process.exit(1);
|
|
37477
37857
|
}
|
|
37478
37858
|
if (options.compatCheck) {
|
|
37479
37859
|
const compatAnalyzer = new CompatibilityAnalyzer();
|
|
37480
37860
|
const compatResult = compatAnalyzer.checkCompatibility(resolve2(targetPath));
|
|
37481
|
-
console.log(
|
|
37861
|
+
console.log(pc5.bold("\nCompatibility Check\n"));
|
|
37482
37862
|
if (compatResult.detectedVersions.length > 0) {
|
|
37483
37863
|
for (const v of compatResult.detectedVersions) {
|
|
37484
|
-
console.log(` ${v.library}: ${
|
|
37864
|
+
console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
|
|
37485
37865
|
}
|
|
37486
37866
|
}
|
|
37487
37867
|
console.log(`
|
|
37488
|
-
Compatibility score: ${
|
|
37868
|
+
Compatibility score: ${pc5.cyan(compatResult.overallScore.toString())}%`);
|
|
37489
37869
|
if (compatResult.issues.length > 0) {
|
|
37490
|
-
console.log(
|
|
37870
|
+
console.log(pc5.bold("\nIssues:\n"));
|
|
37491
37871
|
for (const issue2 of compatResult.issues) {
|
|
37492
|
-
const color = issue2.severity === "error" ?
|
|
37872
|
+
const color = issue2.severity === "error" ? pc5.red : issue2.severity === "warning" ? pc5.yellow : pc5.dim;
|
|
37493
37873
|
console.log(` ${color(`[${issue2.severity}]`)} ${issue2.issue}`);
|
|
37494
|
-
console.log(` ${
|
|
37874
|
+
console.log(` ${pc5.dim(issue2.suggestion)}`);
|
|
37495
37875
|
}
|
|
37496
37876
|
}
|
|
37497
37877
|
console.log("");
|
|
37498
37878
|
}
|
|
37499
37879
|
if (config2.plugins && config2.plugins.length > 0) {
|
|
37500
37880
|
if (!features.customPlugins) {
|
|
37501
|
-
console.error(
|
|
37502
|
-
console.log(`Upgrade at: ${
|
|
37881
|
+
console.error(pc5.red("Custom plugins require Team tier."));
|
|
37882
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37503
37883
|
process.exit(1);
|
|
37504
37884
|
}
|
|
37505
37885
|
const pluginLoader = new PluginLoader();
|
|
37506
37886
|
const pluginResult = await pluginLoader.loadPlugins(config2.plugins);
|
|
37507
37887
|
if (pluginResult.errors.length > 0) {
|
|
37508
37888
|
for (const err of pluginResult.errors) {
|
|
37509
|
-
console.warn(
|
|
37889
|
+
console.warn(pc5.yellow(`Plugin error: ${err}`));
|
|
37510
37890
|
}
|
|
37511
37891
|
}
|
|
37512
37892
|
for (const plugin of pluginResult.loaded) {
|
|
@@ -37515,7 +37895,7 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37515
37895
|
engine.registerHandler(h.from, h.to, h.handler);
|
|
37516
37896
|
}
|
|
37517
37897
|
}
|
|
37518
|
-
console.log(
|
|
37898
|
+
console.log(pc5.dim(`Loaded plugin: ${plugin.name} v${plugin.version}`));
|
|
37519
37899
|
}
|
|
37520
37900
|
}
|
|
37521
37901
|
let chainSteps;
|
|
@@ -37528,15 +37908,15 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37528
37908
|
}
|
|
37529
37909
|
const chainValidation = migrationChain.validateChain(chainSteps, engine);
|
|
37530
37910
|
if (!chainValidation.valid) {
|
|
37531
|
-
console.error(
|
|
37911
|
+
console.error(pc5.red("Chain migration validation failed:"));
|
|
37532
37912
|
for (const err of chainValidation.errors) {
|
|
37533
|
-
console.error(` ${
|
|
37913
|
+
console.error(` ${pc5.red(err)}`);
|
|
37534
37914
|
}
|
|
37535
37915
|
process.exit(1);
|
|
37536
37916
|
}
|
|
37537
37917
|
} else {
|
|
37538
37918
|
if (!engine.hasHandler(options.from, options.to)) {
|
|
37539
|
-
console.error(
|
|
37919
|
+
console.error(pc5.red(`No handler for ${migrationPath}`));
|
|
37540
37920
|
console.log(
|
|
37541
37921
|
"Supported migrations:",
|
|
37542
37922
|
engine.getSupportedPaths().map((p) => `${p.from}->${p.to}`).join(", ")
|
|
@@ -37545,57 +37925,57 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37545
37925
|
}
|
|
37546
37926
|
}
|
|
37547
37927
|
let files;
|
|
37548
|
-
if (
|
|
37928
|
+
if (existsSync5(targetPath) && statSync(targetPath).isFile()) {
|
|
37549
37929
|
files = [resolve2(targetPath)];
|
|
37550
37930
|
} else {
|
|
37551
|
-
files = await glob2(
|
|
37931
|
+
files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
37552
37932
|
ignore: config2.exclude
|
|
37553
37933
|
});
|
|
37554
37934
|
}
|
|
37555
37935
|
if (files.length === 0) {
|
|
37556
|
-
console.log(
|
|
37936
|
+
console.log(pc5.yellow("No files found matching patterns."));
|
|
37557
37937
|
process.exit(0);
|
|
37558
37938
|
}
|
|
37559
37939
|
const incrementalTracker = new IncrementalTracker(resolve2(targetPath));
|
|
37560
37940
|
if (options.incrementalStatus) {
|
|
37561
37941
|
const progress = incrementalTracker.getProgress();
|
|
37562
37942
|
if (!progress) {
|
|
37563
|
-
console.log(
|
|
37943
|
+
console.log(pc5.yellow("No incremental migration in progress."));
|
|
37564
37944
|
process.exit(0);
|
|
37565
37945
|
}
|
|
37566
|
-
console.log(
|
|
37567
|
-
console.log(`Completed: ${
|
|
37568
|
-
console.log(`Remaining: ${
|
|
37569
|
-
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())}`);
|
|
37570
37950
|
console.log(`Total: ${progress.total}`);
|
|
37571
|
-
console.log(`Progress: ${
|
|
37951
|
+
console.log(`Progress: ${pc5.cyan(`${progress.percent}%`)}`);
|
|
37572
37952
|
process.exit(0);
|
|
37573
37953
|
}
|
|
37574
37954
|
if (options.resume) {
|
|
37575
37955
|
const state = incrementalTracker.resume();
|
|
37576
37956
|
if (!state) {
|
|
37577
|
-
console.error(
|
|
37957
|
+
console.error(pc5.red("No incremental migration to resume."));
|
|
37578
37958
|
process.exit(1);
|
|
37579
37959
|
}
|
|
37580
37960
|
files = state.remainingFiles;
|
|
37581
|
-
console.log(
|
|
37961
|
+
console.log(pc5.dim(`Resuming incremental migration: ${state.migrationId}`));
|
|
37582
37962
|
const progress = incrementalTracker.getProgress();
|
|
37583
37963
|
if (progress) {
|
|
37584
37964
|
console.log(
|
|
37585
|
-
|
|
37965
|
+
pc5.dim(
|
|
37586
37966
|
`Progress: ${progress.completed}/${progress.total} completed (${progress.percent}%)`
|
|
37587
37967
|
)
|
|
37588
37968
|
);
|
|
37589
37969
|
}
|
|
37590
37970
|
} else if (options.incremental) {
|
|
37591
37971
|
incrementalTracker.start(files, options.from, options.to);
|
|
37592
|
-
console.log(
|
|
37972
|
+
console.log(pc5.dim("Started incremental migration."));
|
|
37593
37973
|
}
|
|
37594
37974
|
if (files.length > features.maxFiles) {
|
|
37595
37975
|
console.error(
|
|
37596
|
-
|
|
37976
|
+
pc5.red(`File limit exceeded. Found ${files.length}, max ${features.maxFiles}.`)
|
|
37597
37977
|
);
|
|
37598
|
-
console.log(`Upgrade at: ${
|
|
37978
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37599
37979
|
process.exit(1);
|
|
37600
37980
|
}
|
|
37601
37981
|
if (options.crossFile) {
|
|
@@ -37607,34 +37987,34 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37607
37987
|
const resolvedFiles = files.map((f) => resolve2(f));
|
|
37608
37988
|
const depResult = depResolver.resolve(analyzer.getProject(), resolvedFiles);
|
|
37609
37989
|
files = depResult.sortedFiles;
|
|
37610
|
-
console.log(
|
|
37611
|
-
console.log(`Files: ${
|
|
37612
|
-
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())}`);
|
|
37613
37993
|
if (depResult.circularWarnings.length > 0) {
|
|
37614
|
-
console.log(
|
|
37994
|
+
console.log(pc5.yellow(`Circular deps: ${depResult.circularWarnings.length}`));
|
|
37615
37995
|
for (const w of depResult.circularWarnings) {
|
|
37616
|
-
console.log(` ${
|
|
37996
|
+
console.log(` ${pc5.yellow(w)}`);
|
|
37617
37997
|
}
|
|
37618
37998
|
}
|
|
37619
37999
|
console.log("");
|
|
37620
38000
|
}
|
|
37621
38001
|
const displayPath = options.chain || migrationPath;
|
|
37622
|
-
console.log(
|
|
38002
|
+
console.log(pc5.bold(`
|
|
37623
38003
|
Migrating ${displayPath}
|
|
37624
38004
|
`));
|
|
37625
|
-
console.log(`Files: ${
|
|
37626
|
-
console.log(`Tier: ${
|
|
37627
|
-
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)}`);
|
|
37628
38008
|
if (chainSteps) {
|
|
37629
|
-
console.log(`Steps: ${
|
|
38009
|
+
console.log(`Steps: ${pc5.cyan(chainSteps.length.toString())}`);
|
|
37630
38010
|
}
|
|
37631
38011
|
if (options.dryRun) {
|
|
37632
|
-
console.log(
|
|
38012
|
+
console.log(pc5.yellow("\nDRY RUN \u2014 no files will be modified\n"));
|
|
37633
38013
|
}
|
|
37634
38014
|
const git = new GitIntegration();
|
|
37635
38015
|
if (options.gitBranch && git.isAvailable()) {
|
|
37636
38016
|
if (git.hasUncommittedChanges()) {
|
|
37637
|
-
console.error(
|
|
38017
|
+
console.error(pc5.red("Uncommitted changes detected. Commit or stash first."));
|
|
37638
38018
|
process.exit(1);
|
|
37639
38019
|
}
|
|
37640
38020
|
const branchName = git.generateBranchName(
|
|
@@ -37643,13 +38023,13 @@ Migrating ${displayPath}
|
|
|
37643
38023
|
options.to
|
|
37644
38024
|
);
|
|
37645
38025
|
git.createBranch(branchName);
|
|
37646
|
-
console.log(
|
|
38026
|
+
console.log(pc5.dim(`Created branch: ${branchName}`));
|
|
37647
38027
|
}
|
|
37648
38028
|
const backup = new BackupManager(config2.backup?.dir);
|
|
37649
38029
|
let backupManifest;
|
|
37650
38030
|
if (options.backup !== false && config2.backup?.enabled !== false && !options.dryRun) {
|
|
37651
38031
|
backupManifest = backup.createBackup(files, options.from, options.to);
|
|
37652
|
-
console.log(
|
|
38032
|
+
console.log(pc5.dim(`Backup created: ${backupManifest.id}`));
|
|
37653
38033
|
}
|
|
37654
38034
|
const results = [];
|
|
37655
38035
|
const useConcurrent = !options.crossFile;
|
|
@@ -37659,7 +38039,7 @@ Migrating ${displayPath}
|
|
|
37659
38039
|
task: async () => {
|
|
37660
38040
|
if (chainSteps) {
|
|
37661
38041
|
const migrationChain = new MigrationChain();
|
|
37662
|
-
const sourceCode =
|
|
38042
|
+
const sourceCode = readFileSync5(resolve2(file), "utf-8");
|
|
37663
38043
|
const chainResult = migrationChain.executeChain(
|
|
37664
38044
|
sourceCode,
|
|
37665
38045
|
resolve2(file),
|
|
@@ -37736,16 +38116,16 @@ Migrating ${displayPath}
|
|
|
37736
38116
|
const progress = incrementalTracker.getProgress();
|
|
37737
38117
|
if (progress) {
|
|
37738
38118
|
console.log(
|
|
37739
|
-
|
|
38119
|
+
pc5.bold(`
|
|
37740
38120
|
Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
37741
38121
|
);
|
|
37742
38122
|
if (progress.remaining > 0) {
|
|
37743
38123
|
console.log(
|
|
37744
|
-
|
|
38124
|
+
pc5.dim(`Run with --resume to continue. ${progress.remaining} files remaining.`)
|
|
37745
38125
|
);
|
|
37746
38126
|
}
|
|
37747
38127
|
if (progress.failed > 0) {
|
|
37748
|
-
console.log(
|
|
38128
|
+
console.log(pc5.yellow(`${progress.failed} file(s) failed. Run with --resume to retry.`));
|
|
37749
38129
|
}
|
|
37750
38130
|
}
|
|
37751
38131
|
}
|
|
@@ -37754,17 +38134,17 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37754
38134
|
if (successFiles.length > 0) {
|
|
37755
38135
|
git.stageFiles(successFiles);
|
|
37756
38136
|
git.commit(config2.git?.commitMessage || `chore: migrate ${options.from} to ${options.to}`);
|
|
37757
|
-
console.log(
|
|
38137
|
+
console.log(pc5.dim("Changes committed"));
|
|
37758
38138
|
}
|
|
37759
38139
|
}
|
|
37760
38140
|
if (options.report) {
|
|
37761
38141
|
if (options.report === "html" && !features.htmlReports) {
|
|
37762
|
-
console.error(
|
|
37763
|
-
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)}`);
|
|
37764
38144
|
process.exit(1);
|
|
37765
38145
|
} else if (options.report === "csv" && !features.csvExport) {
|
|
37766
|
-
console.error(
|
|
37767
|
-
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)}`);
|
|
37768
38148
|
process.exit(1);
|
|
37769
38149
|
} else {
|
|
37770
38150
|
const reporter = new ReportGenerator();
|
|
@@ -37785,13 +38165,13 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37785
38165
|
} else {
|
|
37786
38166
|
reporter.writeJson(report, outputPath);
|
|
37787
38167
|
}
|
|
37788
|
-
console.log(
|
|
38168
|
+
console.log(pc5.green(`Report saved: ${outputPath}`));
|
|
37789
38169
|
}
|
|
37790
38170
|
}
|
|
37791
38171
|
if (options.scaffoldTests) {
|
|
37792
38172
|
if (!features.testScaffolding) {
|
|
37793
|
-
console.error(
|
|
37794
|
-
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)}`);
|
|
37795
38175
|
} else {
|
|
37796
38176
|
const scaffolder = new TestScaffolder();
|
|
37797
38177
|
const successfulFiles = results.filter((r) => r.success).map((r) => r.filePath);
|
|
@@ -37802,20 +38182,20 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37802
38182
|
}
|
|
37803
38183
|
const sourceFiles = scaffoldAnalyzer.getProject().getSourceFiles();
|
|
37804
38184
|
const scaffoldResult = scaffolder.scaffold(sourceFiles, options.from, options.to);
|
|
37805
|
-
console.log(
|
|
38185
|
+
console.log(pc5.bold("\nTest Scaffolding\n"));
|
|
37806
38186
|
console.log(
|
|
37807
|
-
` Generated ${
|
|
38187
|
+
` Generated ${pc5.cyan(scaffoldResult.tests.length.toString())} test file(s) for ${scaffoldResult.totalSchemas} schema(s)`
|
|
37808
38188
|
);
|
|
37809
38189
|
for (const test of scaffoldResult.tests) {
|
|
37810
|
-
console.log(` ${
|
|
38190
|
+
console.log(` ${pc5.dim(test.filePath)} (${test.schemaCount} schemas)`);
|
|
37811
38191
|
}
|
|
37812
38192
|
}
|
|
37813
38193
|
}
|
|
37814
38194
|
}
|
|
37815
38195
|
if (options.audit) {
|
|
37816
38196
|
if (!features.auditLogging) {
|
|
37817
|
-
console.error(
|
|
37818
|
-
console.log(`Upgrade at: ${
|
|
38197
|
+
console.error(pc5.red("\nAudit logging requires Team tier."));
|
|
38198
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
37819
38199
|
} else {
|
|
37820
38200
|
const projectPath = resolve2(targetPath);
|
|
37821
38201
|
const auditLog = new MigrationAuditLog(projectPath);
|
|
@@ -37835,21 +38215,36 @@ Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
|
37835
38215
|
auditLog.append(entry);
|
|
37836
38216
|
}
|
|
37837
38217
|
console.log(
|
|
37838
|
-
|
|
38218
|
+
pc5.dim(`
|
|
37839
38219
|
Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
|
|
37840
38220
|
);
|
|
37841
38221
|
}
|
|
37842
38222
|
}
|
|
37843
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
|
+
}
|
|
37844
38229
|
const diffOutput = generateDiffPreview(results);
|
|
37845
38230
|
if (diffOutput) {
|
|
37846
|
-
console.log(
|
|
38231
|
+
console.log(pc5.bold("\nDiff Preview\n"));
|
|
37847
38232
|
console.log(diffOutput);
|
|
37848
38233
|
}
|
|
37849
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
|
+
}
|
|
37850
38245
|
const failed = results.filter((r) => !r.success).length;
|
|
37851
38246
|
const warnings = results.reduce((acc, r) => acc + r.warnings.length, 0);
|
|
37852
|
-
console.log(
|
|
38247
|
+
console.log(pc5.bold("\nSummary\n"));
|
|
37853
38248
|
console.log(generateDiffSummary(results));
|
|
37854
38249
|
console.log(`Duration: ${Date.now() - startTime}ms`);
|
|
37855
38250
|
if (failed > 0) {
|
|
@@ -37857,7 +38252,7 @@ Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
|
|
|
37857
38252
|
console.log(formatErrorSummary(errorFiles));
|
|
37858
38253
|
}
|
|
37859
38254
|
if (options.verbose && warnings > 0) {
|
|
37860
|
-
console.log(
|
|
38255
|
+
console.log(pc5.yellow("\nWarnings:\n"));
|
|
37861
38256
|
for (const result of results) {
|
|
37862
38257
|
for (const warning of result.warnings.slice(0, 5)) {
|
|
37863
38258
|
console.log(` ${warning}`);
|
|
@@ -37869,7 +38264,7 @@ Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
|
|
|
37869
38264
|
}
|
|
37870
38265
|
if (options.failOnWarnings && warnings > 0) {
|
|
37871
38266
|
console.error(
|
|
37872
|
-
|
|
38267
|
+
pc5.red(`
|
|
37873
38268
|
Failing due to ${warnings} warning(s) (--fail-on-warnings enabled).`)
|
|
37874
38269
|
);
|
|
37875
38270
|
process.exit(1);
|
|
@@ -37885,13 +38280,13 @@ Failing due to ${warnings} warning(s) (--fail-on-warnings enabled).`)
|
|
|
37885
38280
|
}
|
|
37886
38281
|
if (highRiskFiles.length > 0) {
|
|
37887
38282
|
console.error(
|
|
37888
|
-
|
|
38283
|
+
pc5.red(
|
|
37889
38284
|
`
|
|
37890
38285
|
Failing due to ${highRiskFiles.length} file(s) exceeding max risk score of ${options.maxRiskScore}:`
|
|
37891
38286
|
)
|
|
37892
38287
|
);
|
|
37893
38288
|
for (const f of highRiskFiles) {
|
|
37894
|
-
console.error(
|
|
38289
|
+
console.error(pc5.red(` ${f}`));
|
|
37895
38290
|
}
|
|
37896
38291
|
process.exit(1);
|
|
37897
38292
|
}
|
|
@@ -37900,22 +38295,22 @@ Failing due to ${highRiskFiles.length} file(s) exceeding max risk score of ${opt
|
|
|
37900
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) => {
|
|
37901
38296
|
const validation = await licenseManager.validate();
|
|
37902
38297
|
if (!validation.license?.features.watchMode) {
|
|
37903
|
-
console.error(
|
|
37904
|
-
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)}`);
|
|
37905
38300
|
process.exit(1);
|
|
37906
38301
|
}
|
|
37907
38302
|
const tier = validation.license?.tier || LicenseTier.FREE;
|
|
37908
38303
|
if (!canUseMigration(tier, options.from, options.to)) {
|
|
37909
|
-
console.error(
|
|
37910
|
-
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)}`);
|
|
37911
38306
|
process.exit(1);
|
|
37912
38307
|
}
|
|
37913
38308
|
let config2;
|
|
37914
38309
|
try {
|
|
37915
|
-
config2 = await
|
|
38310
|
+
config2 = await loadConfig2(options.config);
|
|
37916
38311
|
} catch (error) {
|
|
37917
38312
|
const msg = error instanceof Error ? error.message : String(error);
|
|
37918
|
-
console.warn(
|
|
38313
|
+
console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
|
|
37919
38314
|
config2 = {
|
|
37920
38315
|
include: ["**/*.ts", "**/*.tsx"],
|
|
37921
38316
|
exclude: ["**/node_modules/**", "**/dist/**"]
|
|
@@ -37923,7 +38318,7 @@ program.command("watch <path>").description("Watch files and migrate on change")
|
|
|
37923
38318
|
}
|
|
37924
38319
|
const watchMode = new WatchMode();
|
|
37925
38320
|
await watchMode.start({
|
|
37926
|
-
patterns: config2.include.map((p) =>
|
|
38321
|
+
patterns: config2.include.map((p) => join4(targetPath, p)),
|
|
37927
38322
|
exclude: config2.exclude,
|
|
37928
38323
|
from: options.from,
|
|
37929
38324
|
to: options.to,
|
|
@@ -37946,7 +38341,7 @@ program.command("watch <path>").description("Watch files and migrate on change")
|
|
|
37946
38341
|
});
|
|
37947
38342
|
process.on("SIGINT", () => {
|
|
37948
38343
|
watchMode.stop();
|
|
37949
|
-
console.log(
|
|
38344
|
+
console.log(pc5.dim("\nStopped watching."));
|
|
37950
38345
|
process.exit(0);
|
|
37951
38346
|
});
|
|
37952
38347
|
});
|
|
@@ -37955,56 +38350,56 @@ program.command("rollback [backupId]").description("Restore files from a backup"
|
|
|
37955
38350
|
if (options.list) {
|
|
37956
38351
|
const backups = backup.listBackups();
|
|
37957
38352
|
if (backups.length === 0) {
|
|
37958
|
-
console.log(
|
|
38353
|
+
console.log(pc5.yellow("No backups found."));
|
|
37959
38354
|
return;
|
|
37960
38355
|
}
|
|
37961
|
-
console.log(
|
|
38356
|
+
console.log(pc5.bold("\nAvailable backups:\n"));
|
|
37962
38357
|
for (const b of backups) {
|
|
37963
|
-
console.log(` ${
|
|
38358
|
+
console.log(` ${pc5.cyan(b.id)}`);
|
|
37964
38359
|
console.log(` ${b.from} -> ${b.to} | ${b.files.length} files`);
|
|
37965
|
-
console.log(` ${
|
|
38360
|
+
console.log(` ${pc5.dim(new Date(b.timestamp).toLocaleString())}
|
|
37966
38361
|
`);
|
|
37967
38362
|
}
|
|
37968
38363
|
return;
|
|
37969
38364
|
}
|
|
37970
38365
|
if (options.clean) {
|
|
37971
38366
|
backup.cleanOldBackups(5);
|
|
37972
|
-
console.log(
|
|
38367
|
+
console.log(pc5.green("Old backups cleaned."));
|
|
37973
38368
|
return;
|
|
37974
38369
|
}
|
|
37975
38370
|
if (!backupId) {
|
|
37976
38371
|
const backups = backup.listBackups();
|
|
37977
38372
|
if (backups.length === 0) {
|
|
37978
|
-
console.error(
|
|
38373
|
+
console.error(pc5.red("No backups available."));
|
|
37979
38374
|
process.exit(1);
|
|
37980
38375
|
}
|
|
37981
38376
|
backupId = backups[0]?.id;
|
|
37982
38377
|
}
|
|
37983
38378
|
if (!backupId) {
|
|
37984
|
-
console.error(
|
|
38379
|
+
console.error(pc5.red("No backup ID provided."));
|
|
37985
38380
|
process.exit(1);
|
|
37986
38381
|
}
|
|
37987
38382
|
try {
|
|
37988
38383
|
backup.restore(backupId);
|
|
37989
|
-
console.log(
|
|
38384
|
+
console.log(pc5.green(`Restored from ${backupId}`));
|
|
37990
38385
|
} catch (error) {
|
|
37991
|
-
console.error(
|
|
38386
|
+
console.error(pc5.red(error instanceof Error ? error.message : "Restore failed"));
|
|
37992
38387
|
process.exit(1);
|
|
37993
38388
|
}
|
|
37994
38389
|
});
|
|
37995
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) => {
|
|
37996
38391
|
if (options.activate) {
|
|
37997
|
-
console.log(
|
|
38392
|
+
console.log(pc5.dim("Activating license..."));
|
|
37998
38393
|
const result = await licenseManager.activate(options.activate);
|
|
37999
38394
|
if (result.valid && result.license) {
|
|
38000
|
-
console.log(
|
|
38001
|
-
console.log(`Tier: ${
|
|
38395
|
+
console.log(pc5.green("\nLicense activated successfully!\n"));
|
|
38396
|
+
console.log(`Tier: ${pc5.cyan(result.license.tier)}`);
|
|
38002
38397
|
console.log(`Migrations: ${result.license.features.migrations.join(", ")}`);
|
|
38003
38398
|
console.log(
|
|
38004
38399
|
`Max files: ${result.license.features.maxFiles === Infinity ? "Unlimited" : result.license.features.maxFiles}`
|
|
38005
38400
|
);
|
|
38006
38401
|
} else {
|
|
38007
|
-
console.error(
|
|
38402
|
+
console.error(pc5.red(`
|
|
38008
38403
|
Activation failed: ${result.error}`));
|
|
38009
38404
|
process.exit(1);
|
|
38010
38405
|
}
|
|
@@ -38013,26 +38408,26 @@ Activation failed: ${result.error}`));
|
|
|
38013
38408
|
if (options.deactivate) {
|
|
38014
38409
|
const result = await licenseManager.deactivate();
|
|
38015
38410
|
if (result.success) {
|
|
38016
|
-
console.log(
|
|
38411
|
+
console.log(pc5.green("License deactivated. You can now activate on another device."));
|
|
38017
38412
|
} else {
|
|
38018
|
-
console.error(
|
|
38413
|
+
console.error(pc5.red(`Deactivation failed: ${result.error}`));
|
|
38019
38414
|
process.exit(1);
|
|
38020
38415
|
}
|
|
38021
38416
|
return;
|
|
38022
38417
|
}
|
|
38023
38418
|
const status = await licenseManager.getStatus();
|
|
38024
|
-
console.log(
|
|
38419
|
+
console.log(pc5.bold("\nLicense Status\n"));
|
|
38025
38420
|
if (!status.activated) {
|
|
38026
|
-
console.log(`Tier: ${
|
|
38421
|
+
console.log(`Tier: ${pc5.yellow("FREE")}`);
|
|
38027
38422
|
console.log("Max files: 5");
|
|
38028
38423
|
console.log("Migrations: yup->zod only");
|
|
38029
38424
|
console.log("\nActivate a license: schemashift license --activate <key>");
|
|
38030
|
-
console.log(`Purchase: ${
|
|
38425
|
+
console.log(`Purchase: ${pc5.cyan(POLAR_URL)}`);
|
|
38031
38426
|
return;
|
|
38032
38427
|
}
|
|
38033
38428
|
const features = TIER_FEATURES[status.tier];
|
|
38034
|
-
console.log(`Tier: ${
|
|
38035
|
-
console.log(`Activated: ${
|
|
38429
|
+
console.log(`Tier: ${pc5.cyan(status.tier.toUpperCase())}`);
|
|
38430
|
+
console.log(`Activated: ${pc5.green("Yes")}`);
|
|
38036
38431
|
if (status.email) console.log(`Email: ${status.email}`);
|
|
38037
38432
|
if (status.expiresAt) console.log(`Expires: ${status.expiresAt.toLocaleDateString()}`);
|
|
38038
38433
|
if (status.lastValidated)
|
|
@@ -38040,29 +38435,29 @@ Activation failed: ${result.error}`));
|
|
|
38040
38435
|
console.log("\nFeatures:");
|
|
38041
38436
|
console.log(` Max files: ${features.maxFiles === Infinity ? "Unlimited" : features.maxFiles}`);
|
|
38042
38437
|
console.log(` Migrations: ${features.migrations.join(", ")}`);
|
|
38043
|
-
console.log(` CI/CD: ${features.ciSupport ?
|
|
38044
|
-
console.log(` HTML reports: ${features.htmlReports ?
|
|
38045
|
-
console.log(` Watch mode: ${features.watchMode ?
|
|
38046
|
-
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")}`);
|
|
38047
38442
|
console.log(
|
|
38048
|
-
` Advanced analysis: ${features.advancedAnalysis ?
|
|
38443
|
+
` Advanced analysis: ${features.advancedAnalysis ? pc5.green("Yes") : pc5.red("No")}`
|
|
38049
38444
|
);
|
|
38050
|
-
console.log(` Risk scoring: ${features.riskScoring ?
|
|
38051
|
-
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")}`);
|
|
38052
38447
|
console.log(
|
|
38053
|
-
` Cross-file resolution: ${features.crossFileResolution ?
|
|
38448
|
+
` Cross-file resolution: ${features.crossFileResolution ? pc5.green("Yes") : pc5.red("No")}`
|
|
38054
38449
|
);
|
|
38055
|
-
console.log(` Chain migrations: ${features.chainMigrations ?
|
|
38056
|
-
console.log(` Compat check: ${features.compatCheck ?
|
|
38057
|
-
console.log(` Custom plugins: ${features.customPlugins ?
|
|
38058
|
-
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")}`);
|
|
38059
38454
|
});
|
|
38060
38455
|
program.command("compat <path>").description("Check schema library compatibility (Pro+)").option("--json", "Output as JSON").action(async (targetPath, options) => {
|
|
38061
38456
|
const validation = await licenseManager.validate();
|
|
38062
38457
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38063
38458
|
if (!features.compatCheck) {
|
|
38064
|
-
console.error(
|
|
38065
|
-
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)}`);
|
|
38066
38461
|
process.exit(1);
|
|
38067
38462
|
}
|
|
38068
38463
|
const compatAnalyzer = new CompatibilityAnalyzer();
|
|
@@ -38071,43 +38466,43 @@ program.command("compat <path>").description("Check schema library compatibility
|
|
|
38071
38466
|
console.log(JSON.stringify(result, null, 2));
|
|
38072
38467
|
return;
|
|
38073
38468
|
}
|
|
38074
|
-
console.log(
|
|
38469
|
+
console.log(pc5.bold("\nSchema Compatibility Report\n"));
|
|
38075
38470
|
if (result.detectedVersions.length === 0) {
|
|
38076
|
-
console.log(
|
|
38471
|
+
console.log(pc5.yellow("No schema libraries detected in package.json."));
|
|
38077
38472
|
return;
|
|
38078
38473
|
}
|
|
38079
|
-
console.log(
|
|
38474
|
+
console.log(pc5.bold("Detected libraries:\n"));
|
|
38080
38475
|
for (const v of result.detectedVersions) {
|
|
38081
|
-
console.log(` ${v.library}: ${
|
|
38476
|
+
console.log(` ${v.library}: ${pc5.cyan(v.version)}`);
|
|
38082
38477
|
}
|
|
38083
38478
|
console.log(`
|
|
38084
|
-
Compatibility score: ${
|
|
38479
|
+
Compatibility score: ${pc5.cyan(result.overallScore.toString())}%`);
|
|
38085
38480
|
if (result.issues.length > 0) {
|
|
38086
|
-
console.log(
|
|
38481
|
+
console.log(pc5.bold("\nIssues:\n"));
|
|
38087
38482
|
for (const issue2 of result.issues) {
|
|
38088
|
-
const color = issue2.severity === "error" ?
|
|
38483
|
+
const color = issue2.severity === "error" ? pc5.red : issue2.severity === "warning" ? pc5.yellow : pc5.dim;
|
|
38089
38484
|
console.log(` ${color(`[${issue2.severity}]`)} ${issue2.library} ${issue2.detectedVersion}`);
|
|
38090
38485
|
console.log(` ${issue2.issue}`);
|
|
38091
|
-
console.log(` ${
|
|
38486
|
+
console.log(` ${pc5.dim(issue2.suggestion)}`);
|
|
38092
38487
|
}
|
|
38093
38488
|
} else {
|
|
38094
|
-
console.log(
|
|
38489
|
+
console.log(pc5.green("\nNo compatibility issues detected."));
|
|
38095
38490
|
}
|
|
38096
38491
|
});
|
|
38097
|
-
program.command("governance <path>").description("Run schema governance checks (Team+)").option("--json", "Output as JSON").option("-c, --config <path>", "Path to config file").action(async (targetPath, options) => {
|
|
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) => {
|
|
38098
38493
|
const validation = await licenseManager.validate();
|
|
38099
38494
|
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38100
38495
|
if (!features.governance) {
|
|
38101
|
-
console.error(
|
|
38102
|
-
console.log(`Upgrade at: ${
|
|
38496
|
+
console.error(pc5.red("Schema governance requires Team tier."));
|
|
38497
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38103
38498
|
process.exit(1);
|
|
38104
38499
|
}
|
|
38105
38500
|
let config2;
|
|
38106
38501
|
try {
|
|
38107
|
-
config2 = await
|
|
38502
|
+
config2 = await loadConfig2(options.config);
|
|
38108
38503
|
} catch (error) {
|
|
38109
38504
|
const msg = error instanceof Error ? error.message : String(error);
|
|
38110
|
-
console.warn(
|
|
38505
|
+
console.warn(pc5.yellow(`Could not load config: ${msg}. Using defaults.`));
|
|
38111
38506
|
config2 = {
|
|
38112
38507
|
include: ["**/*.ts", "**/*.tsx"],
|
|
38113
38508
|
exclude: ["**/node_modules/**", "**/dist/**"],
|
|
@@ -38116,17 +38511,17 @@ program.command("governance <path>").description("Run schema governance checks (
|
|
|
38116
38511
|
};
|
|
38117
38512
|
}
|
|
38118
38513
|
if (!config2.governance?.rules) {
|
|
38119
|
-
console.error(
|
|
38120
|
-
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.'));
|
|
38121
38516
|
process.exit(1);
|
|
38122
38517
|
}
|
|
38123
38518
|
const { Project } = await import("./ts-morph-5TNWNYI7.js");
|
|
38124
38519
|
const project = new Project();
|
|
38125
|
-
const files = await glob2(
|
|
38520
|
+
const files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
38126
38521
|
ignore: config2.exclude
|
|
38127
38522
|
});
|
|
38128
38523
|
if (files.length === 0) {
|
|
38129
|
-
console.log(
|
|
38524
|
+
console.log(pc5.yellow("No files found."));
|
|
38130
38525
|
process.exit(0);
|
|
38131
38526
|
}
|
|
38132
38527
|
for (const file of files) {
|
|
@@ -38139,46 +38534,477 @@ program.command("governance <path>").description("Run schema governance checks (
|
|
|
38139
38534
|
console.log(JSON.stringify(result, null, 2));
|
|
38140
38535
|
return;
|
|
38141
38536
|
}
|
|
38142
|
-
console.log(
|
|
38143
|
-
console.log(`Files scanned: ${
|
|
38144
|
-
console.log(`Schemas checked: ${
|
|
38145
|
-
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())}`);
|
|
38146
38541
|
if (result.violations.length > 0) {
|
|
38147
38542
|
console.log("");
|
|
38148
38543
|
for (const v of result.violations) {
|
|
38149
|
-
const color = v.severity === "error" ?
|
|
38544
|
+
const color = v.severity === "error" ? pc5.red : pc5.yellow;
|
|
38150
38545
|
console.log(` ${color(`[${v.severity}]`)} ${v.message}`);
|
|
38151
|
-
console.log(` ${
|
|
38546
|
+
console.log(` ${pc5.dim(`${v.filePath}:${v.lineNumber}`)}`);
|
|
38547
|
+
}
|
|
38548
|
+
}
|
|
38549
|
+
if ((options.fix || options.fixDryRun) && result.violations.length > 0) {
|
|
38550
|
+
const governanceFixer = new GovernanceFixer();
|
|
38551
|
+
const fixableCount = result.violations.filter((v) => governanceFixer.canFix(v)).length;
|
|
38552
|
+
if (fixableCount === 0) {
|
|
38553
|
+
console.log(pc5.yellow("\nNo auto-fixable violations found."));
|
|
38554
|
+
} else {
|
|
38555
|
+
console.log(
|
|
38556
|
+
pc5.bold(
|
|
38557
|
+
`
|
|
38558
|
+
${options.fixDryRun ? "Fix Preview" : "Auto-fixing"} (${fixableCount} fixable)
|
|
38559
|
+
`
|
|
38560
|
+
)
|
|
38561
|
+
);
|
|
38562
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
38563
|
+
for (const v of result.violations) {
|
|
38564
|
+
const existing = byFile.get(v.filePath) ?? [];
|
|
38565
|
+
existing.push(v);
|
|
38566
|
+
byFile.set(v.filePath, existing);
|
|
38567
|
+
}
|
|
38568
|
+
let totalFixed = 0;
|
|
38569
|
+
for (const [filePath, fileViolations] of byFile) {
|
|
38570
|
+
const sourceCode = readFileSync5(filePath, "utf-8");
|
|
38571
|
+
const summary = governanceFixer.fixAll(fileViolations, sourceCode);
|
|
38572
|
+
if (summary.fixed > 0) {
|
|
38573
|
+
const shortPath = filePath.replace(`${process.cwd()}/`, "");
|
|
38574
|
+
console.log(` ${pc5.cyan(shortPath)}: ${summary.fixed} fix(es)`);
|
|
38575
|
+
for (const r of summary.results) {
|
|
38576
|
+
if (r.success) {
|
|
38577
|
+
console.log(` ${pc5.green("\u2713")} ${r.explanation}`);
|
|
38578
|
+
}
|
|
38579
|
+
}
|
|
38580
|
+
if (!options.fixDryRun && summary.results.length > 0) {
|
|
38581
|
+
const lastSuccess = [...summary.results].reverse().find((r) => r.success);
|
|
38582
|
+
if (lastSuccess?.fixedCode) {
|
|
38583
|
+
writeFileSync4(filePath, lastSuccess.fixedCode);
|
|
38584
|
+
}
|
|
38585
|
+
}
|
|
38586
|
+
totalFixed += summary.fixed;
|
|
38587
|
+
}
|
|
38588
|
+
}
|
|
38589
|
+
if (options.fixDryRun) {
|
|
38590
|
+
console.log(pc5.yellow(`
|
|
38591
|
+
Dry run: ${totalFixed} fix(es) would be applied.`));
|
|
38592
|
+
} else {
|
|
38593
|
+
console.log(pc5.green(`
|
|
38594
|
+
${totalFixed} fix(es) applied.`));
|
|
38595
|
+
}
|
|
38152
38596
|
}
|
|
38153
38597
|
}
|
|
38154
38598
|
console.log("");
|
|
38155
38599
|
if (result.passed) {
|
|
38156
|
-
console.log(
|
|
38600
|
+
console.log(pc5.green("Governance check passed."));
|
|
38157
38601
|
} else {
|
|
38158
|
-
console.log(
|
|
38602
|
+
console.log(pc5.red("Governance check failed."));
|
|
38159
38603
|
if (config2.governance.failOnViolation) {
|
|
38160
38604
|
process.exit(1);
|
|
38161
38605
|
}
|
|
38162
38606
|
}
|
|
38163
38607
|
});
|
|
38608
|
+
program.command("presets").description("List available migration presets").option("--json", "Output as JSON").option("--category <category>", "Filter by category").action((options) => {
|
|
38609
|
+
let templates = getAllMigrationTemplates();
|
|
38610
|
+
if (options.category) {
|
|
38611
|
+
templates = templates.filter((t) => t.category === options.category);
|
|
38612
|
+
}
|
|
38613
|
+
if (options.json) {
|
|
38614
|
+
console.log(JSON.stringify(templates, null, 2));
|
|
38615
|
+
return;
|
|
38616
|
+
}
|
|
38617
|
+
console.log(pc5.bold("\nAvailable Migration Presets\n"));
|
|
38618
|
+
for (const t of templates) {
|
|
38619
|
+
console.log(` ${pc5.cyan(t.name)}`);
|
|
38620
|
+
console.log(` ${t.description}`);
|
|
38621
|
+
console.log(` Category: ${t.category} | Effort: ${t.estimatedEffort}`);
|
|
38622
|
+
console.log(` Steps: ${t.migrationSteps.map((s) => `${s.from}\u2192${s.to}`).join(", ")}`);
|
|
38623
|
+
console.log("");
|
|
38624
|
+
}
|
|
38625
|
+
console.log(pc5.dim("Use with: schemashift migrate <path> --preset <name>"));
|
|
38626
|
+
});
|
|
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) => {
|
|
38628
|
+
const validation = await licenseManager.validate();
|
|
38629
|
+
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38630
|
+
if (!features.crossFileResolution) {
|
|
38631
|
+
console.error(pc5.red("Dependency graph visualization requires Pro tier or higher."));
|
|
38632
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38633
|
+
process.exit(1);
|
|
38634
|
+
}
|
|
38635
|
+
const files = await glob2(join4(targetPath, "**/*.{ts,tsx}"), {
|
|
38636
|
+
ignore: ["**/node_modules/**", "**/dist/**"]
|
|
38637
|
+
});
|
|
38638
|
+
if (files.length === 0) {
|
|
38639
|
+
console.log(pc5.yellow("No files found."));
|
|
38640
|
+
process.exit(0);
|
|
38641
|
+
}
|
|
38642
|
+
const analyzer = new SchemaAnalyzer();
|
|
38643
|
+
for (const file of files) {
|
|
38644
|
+
analyzer.addSourceFiles([file]);
|
|
38645
|
+
}
|
|
38646
|
+
const resolvedFiles = files.map((f) => resolve2(f));
|
|
38647
|
+
const depResolver = new SchemaDependencyResolver();
|
|
38648
|
+
const depResult = depResolver.resolve(analyzer.getProject(), resolvedFiles);
|
|
38649
|
+
const analysisResult = analyzer.analyze();
|
|
38650
|
+
const nodeMetadata = /* @__PURE__ */ new Map();
|
|
38651
|
+
for (const file of resolvedFiles) {
|
|
38652
|
+
const schemas = analysisResult.schemas.filter((s) => s.filePath === file);
|
|
38653
|
+
const library = schemas[0]?.library;
|
|
38654
|
+
nodeMetadata.set(file, { library, schemaCount: schemas.length });
|
|
38655
|
+
}
|
|
38656
|
+
const graphExporter = new GraphExporter();
|
|
38657
|
+
const exportOptions = {
|
|
38658
|
+
colorByLibrary: options.color || false,
|
|
38659
|
+
highlightCircular: options.highlightCircular || false,
|
|
38660
|
+
filterLibrary: options.filter,
|
|
38661
|
+
nodeMetadata
|
|
38662
|
+
};
|
|
38663
|
+
let output;
|
|
38664
|
+
if (options.format === "dot") {
|
|
38665
|
+
output = graphExporter.exportDot(depResult, exportOptions);
|
|
38666
|
+
} else {
|
|
38667
|
+
output = graphExporter.exportMermaid(depResult, exportOptions);
|
|
38668
|
+
}
|
|
38669
|
+
if (options.output) {
|
|
38670
|
+
writeFileSync4(options.output, output);
|
|
38671
|
+
console.log(pc5.green(`Graph written to ${options.output}`));
|
|
38672
|
+
} else {
|
|
38673
|
+
console.log(output);
|
|
38674
|
+
}
|
|
38675
|
+
console.log(
|
|
38676
|
+
pc5.dim(`
|
|
38677
|
+
${depResult.sortedFiles.length} files, ${depResult.crossFileRefs} cross-file refs`)
|
|
38678
|
+
);
|
|
38679
|
+
if (depResult.circularWarnings.length > 0) {
|
|
38680
|
+
console.log(pc5.yellow(`${depResult.circularWarnings.length} circular dependency warning(s)`));
|
|
38681
|
+
}
|
|
38682
|
+
});
|
|
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(
|
|
38684
|
+
async (inputPath, opts) => {
|
|
38685
|
+
const validation = await licenseManager.validate();
|
|
38686
|
+
const features = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
38687
|
+
if (!features.governance) {
|
|
38688
|
+
console.log(pc5.red("Approval workflows require a TEAM license."));
|
|
38689
|
+
console.log(`Upgrade at: ${pc5.cyan(POLAR_URL)}`);
|
|
38690
|
+
return;
|
|
38691
|
+
}
|
|
38692
|
+
const projectPath = resolve2(inputPath);
|
|
38693
|
+
const manager = new ApprovalManager(projectPath);
|
|
38694
|
+
if (opts.create) {
|
|
38695
|
+
if (!opts.from || !opts.to) {
|
|
38696
|
+
console.log(pc5.red("--from and --to are required with --create"));
|
|
38697
|
+
return;
|
|
38698
|
+
}
|
|
38699
|
+
const files = await glob2("**/*.{ts,tsx,js,jsx}", {
|
|
38700
|
+
cwd: projectPath,
|
|
38701
|
+
ignore: ["node_modules/**", "dist/**"],
|
|
38702
|
+
absolute: true
|
|
38703
|
+
});
|
|
38704
|
+
const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
|
|
38705
|
+
const request = manager.createRequest(opts.from, opts.to, files, reviewer);
|
|
38706
|
+
if (opts.json) {
|
|
38707
|
+
console.log(JSON.stringify(request, null, 2));
|
|
38708
|
+
} else {
|
|
38709
|
+
console.log(pc5.green(`Migration request created: ${pc5.bold(request.id)}`));
|
|
38710
|
+
console.log(` From: ${request.from} \u2192 To: ${request.to}`);
|
|
38711
|
+
console.log(` Files: ${request.files.length}`);
|
|
38712
|
+
console.log(` Status: ${pc5.yellow("pending")}`);
|
|
38713
|
+
}
|
|
38714
|
+
} else if (opts.approve) {
|
|
38715
|
+
const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
|
|
38716
|
+
const request = manager.review({
|
|
38717
|
+
requestId: opts.approve,
|
|
38718
|
+
status: "approved",
|
|
38719
|
+
reviewedBy: reviewer,
|
|
38720
|
+
reason: opts.reason
|
|
38721
|
+
});
|
|
38722
|
+
console.log(pc5.green(`Migration request ${pc5.bold(request.id)} approved by ${reviewer}`));
|
|
38723
|
+
} else if (opts.reject) {
|
|
38724
|
+
const reviewer = opts.reviewer ?? process.env.USER ?? "unknown";
|
|
38725
|
+
const request = manager.review({
|
|
38726
|
+
requestId: opts.reject,
|
|
38727
|
+
status: "rejected",
|
|
38728
|
+
reviewedBy: reviewer,
|
|
38729
|
+
reason: opts.reason
|
|
38730
|
+
});
|
|
38731
|
+
console.log(pc5.red(`Migration request ${pc5.bold(request.id)} rejected by ${reviewer}`));
|
|
38732
|
+
if (opts.reason) {
|
|
38733
|
+
console.log(` Reason: ${opts.reason}`);
|
|
38734
|
+
}
|
|
38735
|
+
} else {
|
|
38736
|
+
const status = opts.status;
|
|
38737
|
+
const requests = manager.listRequests(status);
|
|
38738
|
+
if (opts.json) {
|
|
38739
|
+
console.log(JSON.stringify(requests, null, 2));
|
|
38740
|
+
} else {
|
|
38741
|
+
const summary = manager.getSummary();
|
|
38742
|
+
console.log(pc5.bold(`
|
|
38743
|
+
Migration Approval Requests (${summary.total} total)
|
|
38744
|
+
`));
|
|
38745
|
+
console.log(
|
|
38746
|
+
` ${pc5.yellow(`Pending: ${summary.pending}`)} ${pc5.green(`Approved: ${summary.approved}`)} ${pc5.red(`Rejected: ${summary.rejected}`)}
|
|
38747
|
+
`
|
|
38748
|
+
);
|
|
38749
|
+
for (const req of requests) {
|
|
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)}]`);
|
|
38752
|
+
console.log(
|
|
38753
|
+
` ${req.from} \u2192 ${req.to} | ${req.files.length} files | by ${req.requestedBy}`
|
|
38754
|
+
);
|
|
38755
|
+
if (req.reviewedBy) {
|
|
38756
|
+
console.log(` Reviewed by ${req.reviewedBy} at ${req.reviewedAt}`);
|
|
38757
|
+
}
|
|
38758
|
+
}
|
|
38759
|
+
if (requests.length === 0) {
|
|
38760
|
+
console.log(pc5.dim(" No migration requests found."));
|
|
38761
|
+
}
|
|
38762
|
+
}
|
|
38763
|
+
}
|
|
38764
|
+
}
|
|
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
|
+
});
|
|
38164
38990
|
program.command("pricing").description("Show pricing information").action(() => {
|
|
38165
|
-
console.log(
|
|
38166
|
-
console.log(`${
|
|
38167
|
-
console.log("
|
|
38168
|
-
console.log(" Up to 5 custom rules\n");
|
|
38169
|
-
console.log(`${
|
|
38170
|
-
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");
|
|
38171
38997
|
console.log(" HTML reports, git integration, advanced analysis");
|
|
38172
|
-
console.log(" Risk scoring, CSV export,
|
|
38173
|
-
console.log(`${
|
|
38174
|
-
console.log(" All migrations including io-ts
|
|
38175
|
-
console.log(" CI/CD support, watch mode,
|
|
38176
|
-
console.log(" Cross-file resolution, chain migrations");
|
|
38177
|
-
console.log("
|
|
38178
|
-
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`);
|
|
38179
39004
|
console.log(" Everything in Pro, unlimited devices");
|
|
38180
|
-
console.log(" Custom plugins, schema governance");
|
|
38181
|
-
console.log("
|
|
38182
|
-
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)}`);
|
|
38183
39009
|
});
|
|
38184
39010
|
program.parse(process.argv);
|