schemashift-cli 0.7.0 → 0.9.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/README.md +18 -0
- package/dist/cli.js +358 -18
- package/dist/index.cjs +93 -10
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +93 -10
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -33,6 +33,10 @@ schemashift analyze <path> [options]
|
|
|
33
33
|
| `--detailed` | Full stats with complexity scoring | Individual+ |
|
|
34
34
|
| `--readiness <path>` | Migration readiness report, e.g. `yup->zod` | Individual+ |
|
|
35
35
|
| `--complexity` | Per-schema complexity scores | Individual+ |
|
|
36
|
+
| `--behavioral <migration>` | Behavioral difference warnings, e.g. `yup->zod` | Free |
|
|
37
|
+
| `--bundle <migration>` | Bundle size estimation, e.g. `zod->valibot` | Individual+ |
|
|
38
|
+
| `--performance <migration>` | Performance impact analysis, e.g. `zod-v3->v4` | Individual+ |
|
|
39
|
+
| `--dedup` | Detect duplicate type definitions | Individual+ |
|
|
36
40
|
|
|
37
41
|
### `migrate`
|
|
38
42
|
|
|
@@ -73,6 +77,8 @@ schemashift migrate <path> [options]
|
|
|
73
77
|
| `--compat-check` | Run compatibility check before migration | Pro+ |
|
|
74
78
|
| `--fail-on-warnings` | Exit 1 if any warnings | Team |
|
|
75
79
|
| `--max-risk-score <n>` | Exit 1 if any file exceeds risk score | Team |
|
|
80
|
+
| `--scaffold-tests` | Generate validation tests after migration | Pro+ |
|
|
81
|
+
| `--audit` | Enable migration audit logging | Team |
|
|
76
82
|
|
|
77
83
|
### `watch`
|
|
78
84
|
|
|
@@ -193,6 +199,18 @@ schemashift migrate ./src --chain yup->zod->valibot
|
|
|
193
199
|
# CI mode
|
|
194
200
|
schemashift migrate ./src --from yup --to zod --ci --report json
|
|
195
201
|
|
|
202
|
+
# Backward migration: Zod→Yup
|
|
203
|
+
schemashift migrate ./src --from zod --to yup
|
|
204
|
+
|
|
205
|
+
# Backward migration: Valibot→Zod
|
|
206
|
+
schemashift migrate ./src --from valibot --to zod
|
|
207
|
+
|
|
208
|
+
# Analyze with behavioral warnings and bundle estimation
|
|
209
|
+
schemashift analyze ./src --behavioral yup->zod --bundle yup->zod
|
|
210
|
+
|
|
211
|
+
# Migrate with test scaffolding
|
|
212
|
+
schemashift migrate ./src --from yup --to zod --scaffold-tests
|
|
213
|
+
|
|
196
214
|
# Rollback last migration
|
|
197
215
|
schemashift rollback
|
|
198
216
|
```
|
package/dist/cli.js
CHANGED
|
@@ -10,16 +10,23 @@ import { existsSync as existsSync4, readFileSync as readFileSync4, statSync, wri
|
|
|
10
10
|
import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
import {
|
|
13
|
+
BehavioralWarningAnalyzer,
|
|
14
|
+
BundleEstimator,
|
|
13
15
|
CompatibilityAnalyzer,
|
|
14
16
|
DetailedAnalyzer,
|
|
15
17
|
detectFormLibraries,
|
|
16
18
|
GovernanceEngine,
|
|
19
|
+
IncrementalTracker,
|
|
17
20
|
loadConfig,
|
|
21
|
+
MigrationAuditLog,
|
|
18
22
|
MigrationChain,
|
|
23
|
+
PerformanceAnalyzer,
|
|
19
24
|
PluginLoader,
|
|
20
25
|
SchemaAnalyzer,
|
|
21
26
|
SchemaDependencyResolver,
|
|
22
|
-
|
|
27
|
+
TestScaffolder,
|
|
28
|
+
TransformEngine,
|
|
29
|
+
TypeDedupDetector
|
|
23
30
|
} from "@schemashift/core";
|
|
24
31
|
import { createIoTsToZodHandler } from "@schemashift/io-ts-zod";
|
|
25
32
|
import { createJoiToZodHandler } from "@schemashift/joi-zod";
|
|
@@ -35824,7 +35831,16 @@ var TIER_FEATURES = {
|
|
|
35824
35831
|
customPlugins: false,
|
|
35825
35832
|
governance: false,
|
|
35826
35833
|
failOnWarnings: false,
|
|
35827
|
-
maxRiskScoreThreshold: false
|
|
35834
|
+
maxRiskScoreThreshold: false,
|
|
35835
|
+
formResolverMigration: false,
|
|
35836
|
+
complexityEstimator: false,
|
|
35837
|
+
monorepoAware: false,
|
|
35838
|
+
behavioralWarnings: true,
|
|
35839
|
+
bundleEstimator: false,
|
|
35840
|
+
performanceAnalyzer: false,
|
|
35841
|
+
typeDeduplication: false,
|
|
35842
|
+
testScaffolding: false,
|
|
35843
|
+
auditLogging: false
|
|
35828
35844
|
},
|
|
35829
35845
|
[
|
|
35830
35846
|
"individual"
|
|
@@ -35848,7 +35864,16 @@ var TIER_FEATURES = {
|
|
|
35848
35864
|
customPlugins: false,
|
|
35849
35865
|
governance: false,
|
|
35850
35866
|
failOnWarnings: false,
|
|
35851
|
-
maxRiskScoreThreshold: false
|
|
35867
|
+
maxRiskScoreThreshold: false,
|
|
35868
|
+
formResolverMigration: true,
|
|
35869
|
+
complexityEstimator: true,
|
|
35870
|
+
monorepoAware: false,
|
|
35871
|
+
behavioralWarnings: true,
|
|
35872
|
+
bundleEstimator: true,
|
|
35873
|
+
performanceAnalyzer: true,
|
|
35874
|
+
typeDeduplication: true,
|
|
35875
|
+
testScaffolding: false,
|
|
35876
|
+
auditLogging: false
|
|
35852
35877
|
},
|
|
35853
35878
|
[
|
|
35854
35879
|
"pro"
|
|
@@ -35861,7 +35886,9 @@ var TIER_FEATURES = {
|
|
|
35861
35886
|
"zod-v3->v4",
|
|
35862
35887
|
"io-ts->zod",
|
|
35863
35888
|
"zod->valibot",
|
|
35864
|
-
"any->valibot"
|
|
35889
|
+
"any->valibot",
|
|
35890
|
+
"zod->yup",
|
|
35891
|
+
"valibot->zod"
|
|
35865
35892
|
],
|
|
35866
35893
|
devices: 4,
|
|
35867
35894
|
ciSupport: true,
|
|
@@ -35879,7 +35906,16 @@ var TIER_FEATURES = {
|
|
|
35879
35906
|
customPlugins: false,
|
|
35880
35907
|
governance: false,
|
|
35881
35908
|
failOnWarnings: false,
|
|
35882
|
-
maxRiskScoreThreshold: false
|
|
35909
|
+
maxRiskScoreThreshold: false,
|
|
35910
|
+
formResolverMigration: true,
|
|
35911
|
+
complexityEstimator: true,
|
|
35912
|
+
monorepoAware: true,
|
|
35913
|
+
behavioralWarnings: true,
|
|
35914
|
+
bundleEstimator: true,
|
|
35915
|
+
performanceAnalyzer: true,
|
|
35916
|
+
typeDeduplication: true,
|
|
35917
|
+
testScaffolding: true,
|
|
35918
|
+
auditLogging: false
|
|
35883
35919
|
},
|
|
35884
35920
|
[
|
|
35885
35921
|
"team"
|
|
@@ -35892,7 +35928,9 @@ var TIER_FEATURES = {
|
|
|
35892
35928
|
"zod-v3->v4",
|
|
35893
35929
|
"io-ts->zod",
|
|
35894
35930
|
"any->valibot",
|
|
35895
|
-
"zod->valibot"
|
|
35931
|
+
"zod->valibot",
|
|
35932
|
+
"zod->yup",
|
|
35933
|
+
"valibot->zod"
|
|
35896
35934
|
],
|
|
35897
35935
|
devices: Infinity,
|
|
35898
35936
|
ciSupport: true,
|
|
@@ -35910,7 +35948,16 @@ var TIER_FEATURES = {
|
|
|
35910
35948
|
customPlugins: true,
|
|
35911
35949
|
governance: true,
|
|
35912
35950
|
failOnWarnings: true,
|
|
35913
|
-
maxRiskScoreThreshold: true
|
|
35951
|
+
maxRiskScoreThreshold: true,
|
|
35952
|
+
formResolverMigration: true,
|
|
35953
|
+
complexityEstimator: true,
|
|
35954
|
+
monorepoAware: true,
|
|
35955
|
+
behavioralWarnings: true,
|
|
35956
|
+
bundleEstimator: true,
|
|
35957
|
+
performanceAnalyzer: true,
|
|
35958
|
+
typeDeduplication: true,
|
|
35959
|
+
testScaffolding: true,
|
|
35960
|
+
auditLogging: true
|
|
35914
35961
|
}
|
|
35915
35962
|
};
|
|
35916
35963
|
function canUseMigration(tier, from, to) {
|
|
@@ -36187,9 +36234,9 @@ var LicenseManager = class {
|
|
|
36187
36234
|
};
|
|
36188
36235
|
|
|
36189
36236
|
// src/cli.ts
|
|
36190
|
-
import { createYupToZodHandler } from "@schemashift/yup-zod";
|
|
36237
|
+
import { createYupToZodHandler, createZodToYupHandler } from "@schemashift/yup-zod";
|
|
36191
36238
|
import { createZodV3ToV4Handler } from "@schemashift/zod-v3-v4";
|
|
36192
|
-
import { createZodToValibotHandler } from "@schemashift/zod-valibot";
|
|
36239
|
+
import { createValibotToZodHandler, createZodToValibotHandler } from "@schemashift/zod-valibot";
|
|
36193
36240
|
import { Command } from "commander";
|
|
36194
36241
|
import { glob as glob2 } from "glob";
|
|
36195
36242
|
import { Listr } from "listr2";
|
|
@@ -36871,6 +36918,12 @@ var WatchMode = class {
|
|
|
36871
36918
|
console.log(pc3.cyan(`
|
|
36872
36919
|
Watching ${files.length} files for changes...
|
|
36873
36920
|
`));
|
|
36921
|
+
if (options.dependents && options.dependents.size > 0) {
|
|
36922
|
+
console.log(
|
|
36923
|
+
pc3.dim(`Dependency-aware mode: ${options.dependents.size} files with dependents
|
|
36924
|
+
`)
|
|
36925
|
+
);
|
|
36926
|
+
}
|
|
36874
36927
|
console.log(pc3.dim("Press Ctrl+C to stop\n"));
|
|
36875
36928
|
const directories = new Set(files.map((f) => f.split("/").slice(0, -1).join("/")));
|
|
36876
36929
|
for (const dir of directories) {
|
|
@@ -36891,11 +36944,33 @@ Watching ${files.length} files for changes...
|
|
|
36891
36944
|
Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
36892
36945
|
try {
|
|
36893
36946
|
await options.onTransform(fullPath);
|
|
36894
|
-
console.log(pc3.green(`Transformed successfully
|
|
36895
|
-
|
|
36947
|
+
console.log(pc3.green(`Transformed successfully`));
|
|
36948
|
+
const dependentFiles = options.dependents?.get(fullPath);
|
|
36949
|
+
if (dependentFiles && dependentFiles.length > 0) {
|
|
36950
|
+
console.log(
|
|
36951
|
+
pc3.cyan(` Re-transforming ${dependentFiles.length} dependent file(s)...`)
|
|
36952
|
+
);
|
|
36953
|
+
for (const depFile of dependentFiles) {
|
|
36954
|
+
try {
|
|
36955
|
+
await options.onTransform(depFile);
|
|
36956
|
+
console.log(pc3.green(` \u2713 ${relative2(process.cwd(), depFile)}`));
|
|
36957
|
+
} catch (error) {
|
|
36958
|
+
console.error(
|
|
36959
|
+
pc3.red(
|
|
36960
|
+
` \u2717 ${relative2(process.cwd(), depFile)}: ${error instanceof Error ? error.message : String(error)}`
|
|
36961
|
+
)
|
|
36962
|
+
);
|
|
36963
|
+
}
|
|
36964
|
+
}
|
|
36965
|
+
}
|
|
36966
|
+
console.log("");
|
|
36896
36967
|
} catch (error) {
|
|
36897
|
-
console.error(
|
|
36898
|
-
|
|
36968
|
+
console.error(
|
|
36969
|
+
pc3.red(
|
|
36970
|
+
`Transform failed: ${error instanceof Error ? error.message : String(error)}
|
|
36971
|
+
`
|
|
36972
|
+
)
|
|
36973
|
+
);
|
|
36899
36974
|
}
|
|
36900
36975
|
this.debounceTimers.delete(fullPath);
|
|
36901
36976
|
}, 100)
|
|
@@ -36914,6 +36989,21 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
|
36914
36989
|
}
|
|
36915
36990
|
this.debounceTimers.clear();
|
|
36916
36991
|
}
|
|
36992
|
+
/**
|
|
36993
|
+
* Build a reverse dependency map (file -> files that depend on it).
|
|
36994
|
+
* Takes the forward dependency map from SchemaDependencyResolver and inverts it.
|
|
36995
|
+
*/
|
|
36996
|
+
static buildDependentsMap(dependencies) {
|
|
36997
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
36998
|
+
for (const [file, deps] of dependencies) {
|
|
36999
|
+
for (const dep of deps) {
|
|
37000
|
+
const existing = dependents.get(dep) ?? [];
|
|
37001
|
+
existing.push(file);
|
|
37002
|
+
dependents.set(dep, existing);
|
|
37003
|
+
}
|
|
37004
|
+
}
|
|
37005
|
+
return dependents;
|
|
37006
|
+
}
|
|
36917
37007
|
matchesPatterns(file, include, exclude) {
|
|
36918
37008
|
const isIncluded = include.some((p) => minimatch(file, p));
|
|
36919
37009
|
const isExcluded = exclude.some((p) => minimatch(file, p));
|
|
@@ -36932,6 +37022,8 @@ engine.registerHandler("joi", "zod", createJoiToZodHandler());
|
|
|
36932
37022
|
engine.registerHandler("io-ts", "zod", createIoTsToZodHandler());
|
|
36933
37023
|
engine.registerHandler("zod-v3", "v4", createZodV3ToV4Handler());
|
|
36934
37024
|
engine.registerHandler("zod", "valibot", createZodToValibotHandler());
|
|
37025
|
+
engine.registerHandler("zod", "yup", createZodToYupHandler());
|
|
37026
|
+
engine.registerHandler("valibot", "zod", createValibotToZodHandler());
|
|
36935
37027
|
var POLAR_URL = "https://schemashift.qwady.app";
|
|
36936
37028
|
program.name("schemashift").version(pkg.version).description("TypeScript schema migration CLI");
|
|
36937
37029
|
program.command("init").description("Create .schemashiftrc.json config file").option("-f, --force", "Overwrite existing config").action((options) => {
|
|
@@ -36961,7 +37053,10 @@ program.command("init").description("Create .schemashiftrc.json config file").op
|
|
|
36961
37053
|
console.log(pc4.green("Created .schemashiftrc.json"));
|
|
36962
37054
|
console.log(pc4.dim("Edit the file to customize your migration settings."));
|
|
36963
37055
|
});
|
|
36964
|
-
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+)").
|
|
37056
|
+
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(
|
|
37057
|
+
"--performance <migration>",
|
|
37058
|
+
"Show performance impact analysis, e.g. zod-v3->v4 (Individual+)"
|
|
37059
|
+
).option("--dedup", "Detect duplicate type definitions (Individual+)").action(async (targetPath, options) => {
|
|
36965
37060
|
const analyzer = new SchemaAnalyzer();
|
|
36966
37061
|
let files;
|
|
36967
37062
|
if (existsSync4(targetPath) && statSync(targetPath).isFile()) {
|
|
@@ -37142,13 +37237,145 @@ Migration Readiness: ${from} -> ${to}
|
|
|
37142
37237
|
}
|
|
37143
37238
|
}
|
|
37144
37239
|
}
|
|
37240
|
+
if (options.behavioral) {
|
|
37241
|
+
const migParts = options.behavioral.split("->");
|
|
37242
|
+
if (migParts.length !== 2) {
|
|
37243
|
+
console.error(pc4.red("Invalid format. Use: --behavioral yup->zod"));
|
|
37244
|
+
} else {
|
|
37245
|
+
const [bFrom, bTo] = migParts;
|
|
37246
|
+
const behavioralAnalyzer = new BehavioralWarningAnalyzer();
|
|
37247
|
+
const sourceFiles = analyzer.getProject().getSourceFiles();
|
|
37248
|
+
const behavioralResult = behavioralAnalyzer.analyze(
|
|
37249
|
+
sourceFiles,
|
|
37250
|
+
bFrom,
|
|
37251
|
+
bTo
|
|
37252
|
+
);
|
|
37253
|
+
if (behavioralResult.warnings.length > 0) {
|
|
37254
|
+
console.log(pc4.bold("\nBehavioral Difference Warnings\n"));
|
|
37255
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
37256
|
+
for (const w of behavioralResult.warnings) {
|
|
37257
|
+
const existing = byCategory.get(w.category) ?? [];
|
|
37258
|
+
existing.push(w);
|
|
37259
|
+
byCategory.set(w.category, existing);
|
|
37260
|
+
}
|
|
37261
|
+
for (const [category, catWarnings] of byCategory) {
|
|
37262
|
+
console.log(pc4.yellow(` [${category}] ${catWarnings.length} warning(s)`));
|
|
37263
|
+
for (const w of catWarnings.slice(0, 5)) {
|
|
37264
|
+
console.log(` ${w.message}`);
|
|
37265
|
+
console.log(` ${pc4.dim(w.detail)}`);
|
|
37266
|
+
}
|
|
37267
|
+
if (catWarnings.length > 5) {
|
|
37268
|
+
console.log(pc4.dim(` ... and ${catWarnings.length - 5} more`));
|
|
37269
|
+
}
|
|
37270
|
+
}
|
|
37271
|
+
} else {
|
|
37272
|
+
console.log(pc4.green("\nNo behavioral difference warnings found."));
|
|
37273
|
+
}
|
|
37274
|
+
}
|
|
37275
|
+
}
|
|
37276
|
+
if (options.bundle) {
|
|
37277
|
+
const validation = await licenseManager.validate();
|
|
37278
|
+
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37279
|
+
if (!tier.bundleEstimator) {
|
|
37280
|
+
console.error(pc4.red("\nBundle estimation requires Individual tier or higher."));
|
|
37281
|
+
console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
|
|
37282
|
+
} else {
|
|
37283
|
+
const migParts = options.bundle.split("->");
|
|
37284
|
+
if (migParts.length !== 2) {
|
|
37285
|
+
console.error(pc4.red("Invalid format. Use: --bundle zod->valibot"));
|
|
37286
|
+
} else {
|
|
37287
|
+
const [bFrom, bTo] = migParts;
|
|
37288
|
+
const bundleEstimator = new BundleEstimator();
|
|
37289
|
+
const sourceFiles = analyzer.getProject().getSourceFiles();
|
|
37290
|
+
const estimate = bundleEstimator.estimate(
|
|
37291
|
+
sourceFiles,
|
|
37292
|
+
bFrom,
|
|
37293
|
+
bTo
|
|
37294
|
+
);
|
|
37295
|
+
console.log(pc4.bold("\nBundle Size Estimation\n"));
|
|
37296
|
+
console.log(
|
|
37297
|
+
` ${estimate.from.library}: ~${pc4.cyan(`${estimate.from.minifiedGzipKb}kB`)}`
|
|
37298
|
+
);
|
|
37299
|
+
console.log(` ${estimate.to.library}: ~${pc4.cyan(`${estimate.to.minifiedGzipKb}kB`)}`);
|
|
37300
|
+
console.log(
|
|
37301
|
+
` Delta: ${pc4.yellow(`${estimate.estimatedDelta > 0 ? "+" : ""}${estimate.estimatedDelta.toFixed(1)}kB (${estimate.deltaPercent.toFixed(0)}%)`)}`
|
|
37302
|
+
);
|
|
37303
|
+
console.log(`
|
|
37304
|
+
${pc4.dim(estimate.summary)}`);
|
|
37305
|
+
}
|
|
37306
|
+
}
|
|
37307
|
+
}
|
|
37308
|
+
if (options.performance) {
|
|
37309
|
+
const validation = await licenseManager.validate();
|
|
37310
|
+
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37311
|
+
if (!tier.performanceAnalyzer) {
|
|
37312
|
+
console.error(pc4.red("\nPerformance analysis requires Individual tier or higher."));
|
|
37313
|
+
console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
|
|
37314
|
+
} else {
|
|
37315
|
+
const migParts = options.performance.split("->");
|
|
37316
|
+
if (migParts.length !== 2) {
|
|
37317
|
+
console.error(pc4.red("Invalid format. Use: --performance zod-v3->v4"));
|
|
37318
|
+
} else {
|
|
37319
|
+
const [pFrom, pTo] = migParts;
|
|
37320
|
+
const perfAnalyzer = new PerformanceAnalyzer();
|
|
37321
|
+
const sourceFiles = analyzer.getProject().getSourceFiles();
|
|
37322
|
+
const perfResult = perfAnalyzer.analyze(
|
|
37323
|
+
sourceFiles,
|
|
37324
|
+
pFrom,
|
|
37325
|
+
pTo
|
|
37326
|
+
);
|
|
37327
|
+
if (perfResult.warnings.length > 0) {
|
|
37328
|
+
console.log(pc4.bold("\nPerformance Impact Analysis\n"));
|
|
37329
|
+
for (const w of perfResult.warnings) {
|
|
37330
|
+
const color = w.severity === "error" ? pc4.red : w.severity === "warning" ? pc4.yellow : pc4.dim;
|
|
37331
|
+
console.log(` ${color(`[${w.severity}]`)} ${w.message}`);
|
|
37332
|
+
console.log(` ${pc4.dim(w.detail)}`);
|
|
37333
|
+
}
|
|
37334
|
+
console.log(`
|
|
37335
|
+
${pc4.dim(perfResult.recommendation)}`);
|
|
37336
|
+
} else {
|
|
37337
|
+
console.log(pc4.green("\nNo performance concerns detected."));
|
|
37338
|
+
}
|
|
37339
|
+
}
|
|
37340
|
+
}
|
|
37341
|
+
}
|
|
37342
|
+
if (options.dedup) {
|
|
37343
|
+
const validation = await licenseManager.validate();
|
|
37344
|
+
const tier = validation.license?.features || TIER_FEATURES[LicenseTier.FREE];
|
|
37345
|
+
if (!tier.typeDeduplication) {
|
|
37346
|
+
console.error(pc4.red("\nType deduplication detection requires Individual tier or higher."));
|
|
37347
|
+
console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
|
|
37348
|
+
} else {
|
|
37349
|
+
const dedupDetector = new TypeDedupDetector();
|
|
37350
|
+
const sourceFiles = analyzer.getProject().getSourceFiles();
|
|
37351
|
+
const dedupResult = dedupDetector.detect(sourceFiles);
|
|
37352
|
+
if (dedupResult.candidates.length > 0) {
|
|
37353
|
+
console.log(pc4.bold("\nDuplicate Type Candidates\n"));
|
|
37354
|
+
for (const c of dedupResult.candidates.slice(0, 10)) {
|
|
37355
|
+
console.log(` ${pc4.cyan(c.typeName)} <-> ${pc4.cyan(c.schemaName)}`);
|
|
37356
|
+
console.log(` Type: ${pc4.dim(c.typeFilePath)}:${c.typeLineNumber}`);
|
|
37357
|
+
console.log(` Schema: ${pc4.dim(c.schemaFilePath)}:${c.schemaLineNumber}`);
|
|
37358
|
+
console.log(` Confidence: ${pc4.dim(c.confidence)} | ${pc4.dim(c.suggestion)}`);
|
|
37359
|
+
}
|
|
37360
|
+
if (dedupResult.candidates.length > 10) {
|
|
37361
|
+
console.log(pc4.dim(` ... and ${dedupResult.candidates.length - 10} more`));
|
|
37362
|
+
}
|
|
37363
|
+
} else {
|
|
37364
|
+
console.log(pc4.green("\nNo duplicate type candidates found."));
|
|
37365
|
+
}
|
|
37366
|
+
}
|
|
37367
|
+
}
|
|
37145
37368
|
});
|
|
37146
37369
|
program.command("migrate <path>").description("Migrate schemas from one library to another").requiredOption("-f, --from <library>", "Source library (yup, joi, io-ts, zod-v3, zod)").requiredOption("-t, --to <library>", "Target library (zod, v4, valibot)").option("-d, --dry-run", "Preview changes without writing files").option("-v, --verbose", "Show detailed transformation info").option("-c, --config <path>", "Path to config file").option("--report <format>", "Generate report (json, html, csv)").option("--report-output <path>", "Report output path").option("--git-branch", "Create git branch for changes").option("--git-commit", "Auto-commit changes").option("--no-backup", "Skip backup creation").option("--yes", "Skip confirmation prompt").option("--ci", "CI mode (non-interactive, exit code on failure)").option("--cross-file", "Resolve cross-file schema dependencies (Pro+)").option("--chain <path>", "Chain migrations, e.g. yup->zod->valibot (Pro+)").option("--compat-check", "Run compatibility check before migration (Pro+)").option("--fail-on-warnings", "Exit 1 if any warnings (Team+)").option(
|
|
37147
37370
|
"--max-risk-score <score>",
|
|
37148
37371
|
"Exit 1 if any file exceeds risk score (Team+)",
|
|
37149
37372
|
Number.parseInt
|
|
37150
|
-
).action(async (targetPath, options) => {
|
|
37373
|
+
).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+)").action(async (targetPath, options) => {
|
|
37151
37374
|
const startTime = Date.now();
|
|
37375
|
+
if (options.maxRiskScore !== void 0 && Number.isNaN(options.maxRiskScore)) {
|
|
37376
|
+
console.error(pc4.red("--max-risk-score must be a valid number."));
|
|
37377
|
+
process.exit(1);
|
|
37378
|
+
}
|
|
37152
37379
|
let config2;
|
|
37153
37380
|
try {
|
|
37154
37381
|
config2 = await loadConfig(options.config);
|
|
@@ -37298,6 +37525,41 @@ Compatibility score: ${pc4.cyan(compatResult.overallScore.toString())}%`);
|
|
|
37298
37525
|
console.log(pc4.yellow("No files found matching patterns."));
|
|
37299
37526
|
process.exit(0);
|
|
37300
37527
|
}
|
|
37528
|
+
const incrementalTracker = new IncrementalTracker(resolve2(targetPath));
|
|
37529
|
+
if (options.incrementalStatus) {
|
|
37530
|
+
const progress = incrementalTracker.getProgress();
|
|
37531
|
+
if (!progress) {
|
|
37532
|
+
console.log(pc4.yellow("No incremental migration in progress."));
|
|
37533
|
+
process.exit(0);
|
|
37534
|
+
}
|
|
37535
|
+
console.log(pc4.bold("\nIncremental Migration Status\n"));
|
|
37536
|
+
console.log(`Completed: ${pc4.green(progress.completed.toString())}`);
|
|
37537
|
+
console.log(`Remaining: ${pc4.cyan(progress.remaining.toString())}`);
|
|
37538
|
+
console.log(`Failed: ${pc4.red(progress.failed.toString())}`);
|
|
37539
|
+
console.log(`Total: ${progress.total}`);
|
|
37540
|
+
console.log(`Progress: ${pc4.cyan(`${progress.percent}%`)}`);
|
|
37541
|
+
process.exit(0);
|
|
37542
|
+
}
|
|
37543
|
+
if (options.resume) {
|
|
37544
|
+
const state = incrementalTracker.resume();
|
|
37545
|
+
if (!state) {
|
|
37546
|
+
console.error(pc4.red("No incremental migration to resume."));
|
|
37547
|
+
process.exit(1);
|
|
37548
|
+
}
|
|
37549
|
+
files = state.remainingFiles;
|
|
37550
|
+
console.log(pc4.dim(`Resuming incremental migration: ${state.migrationId}`));
|
|
37551
|
+
const progress = incrementalTracker.getProgress();
|
|
37552
|
+
if (progress) {
|
|
37553
|
+
console.log(
|
|
37554
|
+
pc4.dim(
|
|
37555
|
+
`Progress: ${progress.completed}/${progress.total} completed (${progress.percent}%)`
|
|
37556
|
+
)
|
|
37557
|
+
);
|
|
37558
|
+
}
|
|
37559
|
+
} else if (options.incremental) {
|
|
37560
|
+
incrementalTracker.start(files, options.from, options.to);
|
|
37561
|
+
console.log(pc4.dim("Started incremental migration."));
|
|
37562
|
+
}
|
|
37301
37563
|
if (files.length > features.maxFiles) {
|
|
37302
37564
|
console.error(
|
|
37303
37565
|
pc4.red(`File limit exceeded. Found ${files.length}, max ${features.maxFiles}.`)
|
|
@@ -37432,6 +37694,30 @@ Migrating ${displayPath}
|
|
|
37432
37694
|
}
|
|
37433
37695
|
}
|
|
37434
37696
|
}
|
|
37697
|
+
if (options.incremental || options.resume) {
|
|
37698
|
+
for (const result of results) {
|
|
37699
|
+
if (result.success) {
|
|
37700
|
+
incrementalTracker.markComplete(result.filePath);
|
|
37701
|
+
} else {
|
|
37702
|
+
incrementalTracker.markFailed(result.filePath);
|
|
37703
|
+
}
|
|
37704
|
+
}
|
|
37705
|
+
const progress = incrementalTracker.getProgress();
|
|
37706
|
+
if (progress) {
|
|
37707
|
+
console.log(
|
|
37708
|
+
pc4.bold(`
|
|
37709
|
+
Incremental: ${progress.completed}/${progress.total} (${progress.percent}%)`)
|
|
37710
|
+
);
|
|
37711
|
+
if (progress.remaining > 0) {
|
|
37712
|
+
console.log(
|
|
37713
|
+
pc4.dim(`Run with --resume to continue. ${progress.remaining} files remaining.`)
|
|
37714
|
+
);
|
|
37715
|
+
}
|
|
37716
|
+
if (progress.failed > 0) {
|
|
37717
|
+
console.log(pc4.yellow(`${progress.failed} file(s) failed. Run with --resume to retry.`));
|
|
37718
|
+
}
|
|
37719
|
+
}
|
|
37720
|
+
}
|
|
37435
37721
|
if (options.gitCommit && git.isAvailable() && !options.dryRun) {
|
|
37436
37722
|
const successFiles = results.filter((r) => r.success).map((r) => r.filePath);
|
|
37437
37723
|
if (successFiles.length > 0) {
|
|
@@ -37444,9 +37730,11 @@ Migrating ${displayPath}
|
|
|
37444
37730
|
if (options.report === "html" && !features.htmlReports) {
|
|
37445
37731
|
console.error(pc4.red("HTML reports require Individual tier or higher."));
|
|
37446
37732
|
console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
|
|
37733
|
+
process.exit(1);
|
|
37447
37734
|
} else if (options.report === "csv" && !features.csvExport) {
|
|
37448
37735
|
console.error(pc4.red("CSV export requires Individual tier or higher."));
|
|
37449
37736
|
console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
|
|
37737
|
+
process.exit(1);
|
|
37450
37738
|
} else {
|
|
37451
37739
|
const reporter = new ReportGenerator();
|
|
37452
37740
|
const includeRisk = features.riskScoring;
|
|
@@ -37469,6 +37757,58 @@ Migrating ${displayPath}
|
|
|
37469
37757
|
console.log(pc4.green(`Report saved: ${outputPath}`));
|
|
37470
37758
|
}
|
|
37471
37759
|
}
|
|
37760
|
+
if (options.scaffoldTests) {
|
|
37761
|
+
if (!features.testScaffolding) {
|
|
37762
|
+
console.error(pc4.red("\nTest scaffolding requires Pro tier or higher."));
|
|
37763
|
+
console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
|
|
37764
|
+
} else {
|
|
37765
|
+
const scaffolder = new TestScaffolder();
|
|
37766
|
+
const successfulFiles = results.filter((r) => r.success).map((r) => r.filePath);
|
|
37767
|
+
if (successfulFiles.length > 0) {
|
|
37768
|
+
const scaffoldAnalyzer = new SchemaAnalyzer();
|
|
37769
|
+
for (const f of successfulFiles) {
|
|
37770
|
+
scaffoldAnalyzer.addSourceFiles([f]);
|
|
37771
|
+
}
|
|
37772
|
+
const sourceFiles = scaffoldAnalyzer.getProject().getSourceFiles();
|
|
37773
|
+
const scaffoldResult = scaffolder.scaffold(sourceFiles, options.from, options.to);
|
|
37774
|
+
console.log(pc4.bold("\nTest Scaffolding\n"));
|
|
37775
|
+
console.log(
|
|
37776
|
+
` Generated ${pc4.cyan(scaffoldResult.tests.length.toString())} test file(s) for ${scaffoldResult.totalSchemas} schema(s)`
|
|
37777
|
+
);
|
|
37778
|
+
for (const test of scaffoldResult.tests) {
|
|
37779
|
+
console.log(` ${pc4.dim(test.filePath)} (${test.schemaCount} schemas)`);
|
|
37780
|
+
}
|
|
37781
|
+
}
|
|
37782
|
+
}
|
|
37783
|
+
}
|
|
37784
|
+
if (options.audit) {
|
|
37785
|
+
if (!features.auditLogging) {
|
|
37786
|
+
console.error(pc4.red("\nAudit logging requires Team tier."));
|
|
37787
|
+
console.log(`Upgrade at: ${pc4.cyan(POLAR_URL)}`);
|
|
37788
|
+
} else {
|
|
37789
|
+
const projectPath = resolve2(targetPath);
|
|
37790
|
+
const auditLog = new MigrationAuditLog(projectPath);
|
|
37791
|
+
const migrationId = `${options.from}->${options.to}-${Date.now()}`;
|
|
37792
|
+
for (const result of results) {
|
|
37793
|
+
const entry = auditLog.createEntry({
|
|
37794
|
+
migrationId,
|
|
37795
|
+
filePath: result.filePath,
|
|
37796
|
+
from: options.from,
|
|
37797
|
+
to: options.to,
|
|
37798
|
+
originalCode: result.originalCode ?? "",
|
|
37799
|
+
transformedCode: result.transformedCode,
|
|
37800
|
+
success: result.success,
|
|
37801
|
+
warningCount: result.warnings.length,
|
|
37802
|
+
errorCount: result.errors.length
|
|
37803
|
+
});
|
|
37804
|
+
auditLog.append(entry);
|
|
37805
|
+
}
|
|
37806
|
+
console.log(
|
|
37807
|
+
pc4.dim(`
|
|
37808
|
+
Audit log saved to .schemashift/audit-log.json (${results.length} entries)`)
|
|
37809
|
+
);
|
|
37810
|
+
}
|
|
37811
|
+
}
|
|
37472
37812
|
if (options.dryRun) {
|
|
37473
37813
|
const diffOutput = generateDiffPreview(results);
|
|
37474
37814
|
if (diffOutput) {
|
|
@@ -37551,15 +37891,15 @@ program.command("watch <path>").description("Watch files and migrate on change")
|
|
|
37551
37891
|
};
|
|
37552
37892
|
}
|
|
37553
37893
|
const watchMode = new WatchMode();
|
|
37554
|
-
const analyzer = new SchemaAnalyzer();
|
|
37555
37894
|
await watchMode.start({
|
|
37556
37895
|
patterns: config2.include.map((p) => join3(targetPath, p)),
|
|
37557
37896
|
exclude: config2.exclude,
|
|
37558
37897
|
from: options.from,
|
|
37559
37898
|
to: options.to,
|
|
37560
37899
|
onTransform: async (file) => {
|
|
37561
|
-
|
|
37562
|
-
|
|
37900
|
+
const fileAnalyzer = new SchemaAnalyzer();
|
|
37901
|
+
fileAnalyzer.addSourceFiles([file]);
|
|
37902
|
+
const sourceFile = fileAnalyzer.getProject().getSourceFileOrThrow(resolve2(file));
|
|
37563
37903
|
const result = engine.transform(sourceFile, options.from, options.to, {
|
|
37564
37904
|
from: options.from,
|
|
37565
37905
|
to: options.to,
|
package/dist/index.cjs
CHANGED
|
@@ -33788,7 +33788,16 @@ var TIER_FEATURES = {
|
|
|
33788
33788
|
customPlugins: false,
|
|
33789
33789
|
governance: false,
|
|
33790
33790
|
failOnWarnings: false,
|
|
33791
|
-
maxRiskScoreThreshold: false
|
|
33791
|
+
maxRiskScoreThreshold: false,
|
|
33792
|
+
formResolverMigration: false,
|
|
33793
|
+
complexityEstimator: false,
|
|
33794
|
+
monorepoAware: false,
|
|
33795
|
+
behavioralWarnings: true,
|
|
33796
|
+
bundleEstimator: false,
|
|
33797
|
+
performanceAnalyzer: false,
|
|
33798
|
+
typeDeduplication: false,
|
|
33799
|
+
testScaffolding: false,
|
|
33800
|
+
auditLogging: false
|
|
33792
33801
|
},
|
|
33793
33802
|
[
|
|
33794
33803
|
"individual"
|
|
@@ -33812,7 +33821,16 @@ var TIER_FEATURES = {
|
|
|
33812
33821
|
customPlugins: false,
|
|
33813
33822
|
governance: false,
|
|
33814
33823
|
failOnWarnings: false,
|
|
33815
|
-
maxRiskScoreThreshold: false
|
|
33824
|
+
maxRiskScoreThreshold: false,
|
|
33825
|
+
formResolverMigration: true,
|
|
33826
|
+
complexityEstimator: true,
|
|
33827
|
+
monorepoAware: false,
|
|
33828
|
+
behavioralWarnings: true,
|
|
33829
|
+
bundleEstimator: true,
|
|
33830
|
+
performanceAnalyzer: true,
|
|
33831
|
+
typeDeduplication: true,
|
|
33832
|
+
testScaffolding: false,
|
|
33833
|
+
auditLogging: false
|
|
33816
33834
|
},
|
|
33817
33835
|
[
|
|
33818
33836
|
"pro"
|
|
@@ -33825,7 +33843,9 @@ var TIER_FEATURES = {
|
|
|
33825
33843
|
"zod-v3->v4",
|
|
33826
33844
|
"io-ts->zod",
|
|
33827
33845
|
"zod->valibot",
|
|
33828
|
-
"any->valibot"
|
|
33846
|
+
"any->valibot",
|
|
33847
|
+
"zod->yup",
|
|
33848
|
+
"valibot->zod"
|
|
33829
33849
|
],
|
|
33830
33850
|
devices: 4,
|
|
33831
33851
|
ciSupport: true,
|
|
@@ -33843,7 +33863,16 @@ var TIER_FEATURES = {
|
|
|
33843
33863
|
customPlugins: false,
|
|
33844
33864
|
governance: false,
|
|
33845
33865
|
failOnWarnings: false,
|
|
33846
|
-
maxRiskScoreThreshold: false
|
|
33866
|
+
maxRiskScoreThreshold: false,
|
|
33867
|
+
formResolverMigration: true,
|
|
33868
|
+
complexityEstimator: true,
|
|
33869
|
+
monorepoAware: true,
|
|
33870
|
+
behavioralWarnings: true,
|
|
33871
|
+
bundleEstimator: true,
|
|
33872
|
+
performanceAnalyzer: true,
|
|
33873
|
+
typeDeduplication: true,
|
|
33874
|
+
testScaffolding: true,
|
|
33875
|
+
auditLogging: false
|
|
33847
33876
|
},
|
|
33848
33877
|
[
|
|
33849
33878
|
"team"
|
|
@@ -33856,7 +33885,9 @@ var TIER_FEATURES = {
|
|
|
33856
33885
|
"zod-v3->v4",
|
|
33857
33886
|
"io-ts->zod",
|
|
33858
33887
|
"any->valibot",
|
|
33859
|
-
"zod->valibot"
|
|
33888
|
+
"zod->valibot",
|
|
33889
|
+
"zod->yup",
|
|
33890
|
+
"valibot->zod"
|
|
33860
33891
|
],
|
|
33861
33892
|
devices: Infinity,
|
|
33862
33893
|
ciSupport: true,
|
|
@@ -33874,7 +33905,16 @@ var TIER_FEATURES = {
|
|
|
33874
33905
|
customPlugins: true,
|
|
33875
33906
|
governance: true,
|
|
33876
33907
|
failOnWarnings: true,
|
|
33877
|
-
maxRiskScoreThreshold: true
|
|
33908
|
+
maxRiskScoreThreshold: true,
|
|
33909
|
+
formResolverMigration: true,
|
|
33910
|
+
complexityEstimator: true,
|
|
33911
|
+
monorepoAware: true,
|
|
33912
|
+
behavioralWarnings: true,
|
|
33913
|
+
bundleEstimator: true,
|
|
33914
|
+
performanceAnalyzer: true,
|
|
33915
|
+
typeDeduplication: true,
|
|
33916
|
+
testScaffolding: true,
|
|
33917
|
+
auditLogging: true
|
|
33878
33918
|
}
|
|
33879
33919
|
};
|
|
33880
33920
|
var POLAR_ORG_ID = process.env.POLAR_ORG_ID || "79bbe935-1836-4b9e-9ca8-4c7a94217f5e";
|
|
@@ -34629,6 +34669,12 @@ var WatchMode = class {
|
|
|
34629
34669
|
console.log(import_picocolors.default.cyan(`
|
|
34630
34670
|
Watching ${files.length} files for changes...
|
|
34631
34671
|
`));
|
|
34672
|
+
if (options.dependents && options.dependents.size > 0) {
|
|
34673
|
+
console.log(
|
|
34674
|
+
import_picocolors.default.dim(`Dependency-aware mode: ${options.dependents.size} files with dependents
|
|
34675
|
+
`)
|
|
34676
|
+
);
|
|
34677
|
+
}
|
|
34632
34678
|
console.log(import_picocolors.default.dim("Press Ctrl+C to stop\n"));
|
|
34633
34679
|
const directories = new Set(files.map((f) => f.split("/").slice(0, -1).join("/")));
|
|
34634
34680
|
for (const dir of directories) {
|
|
@@ -34649,11 +34695,33 @@ Watching ${files.length} files for changes...
|
|
|
34649
34695
|
Changed: ${(0, import_node_path2.relative)(process.cwd(), fullPath)}`));
|
|
34650
34696
|
try {
|
|
34651
34697
|
await options.onTransform(fullPath);
|
|
34652
|
-
console.log(import_picocolors.default.green(`Transformed successfully
|
|
34653
|
-
|
|
34698
|
+
console.log(import_picocolors.default.green(`Transformed successfully`));
|
|
34699
|
+
const dependentFiles = options.dependents?.get(fullPath);
|
|
34700
|
+
if (dependentFiles && dependentFiles.length > 0) {
|
|
34701
|
+
console.log(
|
|
34702
|
+
import_picocolors.default.cyan(` Re-transforming ${dependentFiles.length} dependent file(s)...`)
|
|
34703
|
+
);
|
|
34704
|
+
for (const depFile of dependentFiles) {
|
|
34705
|
+
try {
|
|
34706
|
+
await options.onTransform(depFile);
|
|
34707
|
+
console.log(import_picocolors.default.green(` \u2713 ${(0, import_node_path2.relative)(process.cwd(), depFile)}`));
|
|
34708
|
+
} catch (error) {
|
|
34709
|
+
console.error(
|
|
34710
|
+
import_picocolors.default.red(
|
|
34711
|
+
` \u2717 ${(0, import_node_path2.relative)(process.cwd(), depFile)}: ${error instanceof Error ? error.message : String(error)}`
|
|
34712
|
+
)
|
|
34713
|
+
);
|
|
34714
|
+
}
|
|
34715
|
+
}
|
|
34716
|
+
}
|
|
34717
|
+
console.log("");
|
|
34654
34718
|
} catch (error) {
|
|
34655
|
-
console.error(
|
|
34656
|
-
|
|
34719
|
+
console.error(
|
|
34720
|
+
import_picocolors.default.red(
|
|
34721
|
+
`Transform failed: ${error instanceof Error ? error.message : String(error)}
|
|
34722
|
+
`
|
|
34723
|
+
)
|
|
34724
|
+
);
|
|
34657
34725
|
}
|
|
34658
34726
|
this.debounceTimers.delete(fullPath);
|
|
34659
34727
|
}, 100)
|
|
@@ -34672,6 +34740,21 @@ Changed: ${(0, import_node_path2.relative)(process.cwd(), fullPath)}`));
|
|
|
34672
34740
|
}
|
|
34673
34741
|
this.debounceTimers.clear();
|
|
34674
34742
|
}
|
|
34743
|
+
/**
|
|
34744
|
+
* Build a reverse dependency map (file -> files that depend on it).
|
|
34745
|
+
* Takes the forward dependency map from SchemaDependencyResolver and inverts it.
|
|
34746
|
+
*/
|
|
34747
|
+
static buildDependentsMap(dependencies) {
|
|
34748
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
34749
|
+
for (const [file, deps] of dependencies) {
|
|
34750
|
+
for (const dep of deps) {
|
|
34751
|
+
const existing = dependents.get(dep) ?? [];
|
|
34752
|
+
existing.push(file);
|
|
34753
|
+
dependents.set(dep, existing);
|
|
34754
|
+
}
|
|
34755
|
+
}
|
|
34756
|
+
return dependents;
|
|
34757
|
+
}
|
|
34675
34758
|
matchesPatterns(file, include, exclude) {
|
|
34676
34759
|
const isIncluded = include.some((p) => (0, import_minimatch.minimatch)(file, p));
|
|
34677
34760
|
const isExcluded = exclude.some((p) => (0, import_minimatch.minimatch)(file, p));
|
package/dist/index.d.cts
CHANGED
|
@@ -91,12 +91,19 @@ interface WatchOptions {
|
|
|
91
91
|
from: string;
|
|
92
92
|
to: string;
|
|
93
93
|
onTransform: (file: string) => Promise<void>;
|
|
94
|
+
/** Optional: dependency map from SchemaDependencyResolver. Keys are file paths, values are lists of files that depend on the key. */
|
|
95
|
+
dependents?: Map<string, string[]>;
|
|
94
96
|
}
|
|
95
97
|
declare class WatchMode {
|
|
96
98
|
private watchers;
|
|
97
99
|
private debounceTimers;
|
|
98
100
|
start(options: WatchOptions): Promise<void>;
|
|
99
101
|
stop(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Build a reverse dependency map (file -> files that depend on it).
|
|
104
|
+
* Takes the forward dependency map from SchemaDependencyResolver and inverts it.
|
|
105
|
+
*/
|
|
106
|
+
static buildDependentsMap(dependencies: Map<string, string[]>): Map<string, string[]>;
|
|
100
107
|
private matchesPatterns;
|
|
101
108
|
}
|
|
102
109
|
|
package/dist/index.d.ts
CHANGED
|
@@ -91,12 +91,19 @@ interface WatchOptions {
|
|
|
91
91
|
from: string;
|
|
92
92
|
to: string;
|
|
93
93
|
onTransform: (file: string) => Promise<void>;
|
|
94
|
+
/** Optional: dependency map from SchemaDependencyResolver. Keys are file paths, values are lists of files that depend on the key. */
|
|
95
|
+
dependents?: Map<string, string[]>;
|
|
94
96
|
}
|
|
95
97
|
declare class WatchMode {
|
|
96
98
|
private watchers;
|
|
97
99
|
private debounceTimers;
|
|
98
100
|
start(options: WatchOptions): Promise<void>;
|
|
99
101
|
stop(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Build a reverse dependency map (file -> files that depend on it).
|
|
104
|
+
* Takes the forward dependency map from SchemaDependencyResolver and inverts it.
|
|
105
|
+
*/
|
|
106
|
+
static buildDependentsMap(dependencies: Map<string, string[]>): Map<string, string[]>;
|
|
100
107
|
private matchesPatterns;
|
|
101
108
|
}
|
|
102
109
|
|
package/dist/index.js
CHANGED
|
@@ -33747,7 +33747,16 @@ var TIER_FEATURES = {
|
|
|
33747
33747
|
customPlugins: false,
|
|
33748
33748
|
governance: false,
|
|
33749
33749
|
failOnWarnings: false,
|
|
33750
|
-
maxRiskScoreThreshold: false
|
|
33750
|
+
maxRiskScoreThreshold: false,
|
|
33751
|
+
formResolverMigration: false,
|
|
33752
|
+
complexityEstimator: false,
|
|
33753
|
+
monorepoAware: false,
|
|
33754
|
+
behavioralWarnings: true,
|
|
33755
|
+
bundleEstimator: false,
|
|
33756
|
+
performanceAnalyzer: false,
|
|
33757
|
+
typeDeduplication: false,
|
|
33758
|
+
testScaffolding: false,
|
|
33759
|
+
auditLogging: false
|
|
33751
33760
|
},
|
|
33752
33761
|
[
|
|
33753
33762
|
"individual"
|
|
@@ -33771,7 +33780,16 @@ var TIER_FEATURES = {
|
|
|
33771
33780
|
customPlugins: false,
|
|
33772
33781
|
governance: false,
|
|
33773
33782
|
failOnWarnings: false,
|
|
33774
|
-
maxRiskScoreThreshold: false
|
|
33783
|
+
maxRiskScoreThreshold: false,
|
|
33784
|
+
formResolverMigration: true,
|
|
33785
|
+
complexityEstimator: true,
|
|
33786
|
+
monorepoAware: false,
|
|
33787
|
+
behavioralWarnings: true,
|
|
33788
|
+
bundleEstimator: true,
|
|
33789
|
+
performanceAnalyzer: true,
|
|
33790
|
+
typeDeduplication: true,
|
|
33791
|
+
testScaffolding: false,
|
|
33792
|
+
auditLogging: false
|
|
33775
33793
|
},
|
|
33776
33794
|
[
|
|
33777
33795
|
"pro"
|
|
@@ -33784,7 +33802,9 @@ var TIER_FEATURES = {
|
|
|
33784
33802
|
"zod-v3->v4",
|
|
33785
33803
|
"io-ts->zod",
|
|
33786
33804
|
"zod->valibot",
|
|
33787
|
-
"any->valibot"
|
|
33805
|
+
"any->valibot",
|
|
33806
|
+
"zod->yup",
|
|
33807
|
+
"valibot->zod"
|
|
33788
33808
|
],
|
|
33789
33809
|
devices: 4,
|
|
33790
33810
|
ciSupport: true,
|
|
@@ -33802,7 +33822,16 @@ var TIER_FEATURES = {
|
|
|
33802
33822
|
customPlugins: false,
|
|
33803
33823
|
governance: false,
|
|
33804
33824
|
failOnWarnings: false,
|
|
33805
|
-
maxRiskScoreThreshold: false
|
|
33825
|
+
maxRiskScoreThreshold: false,
|
|
33826
|
+
formResolverMigration: true,
|
|
33827
|
+
complexityEstimator: true,
|
|
33828
|
+
monorepoAware: true,
|
|
33829
|
+
behavioralWarnings: true,
|
|
33830
|
+
bundleEstimator: true,
|
|
33831
|
+
performanceAnalyzer: true,
|
|
33832
|
+
typeDeduplication: true,
|
|
33833
|
+
testScaffolding: true,
|
|
33834
|
+
auditLogging: false
|
|
33806
33835
|
},
|
|
33807
33836
|
[
|
|
33808
33837
|
"team"
|
|
@@ -33815,7 +33844,9 @@ var TIER_FEATURES = {
|
|
|
33815
33844
|
"zod-v3->v4",
|
|
33816
33845
|
"io-ts->zod",
|
|
33817
33846
|
"any->valibot",
|
|
33818
|
-
"zod->valibot"
|
|
33847
|
+
"zod->valibot",
|
|
33848
|
+
"zod->yup",
|
|
33849
|
+
"valibot->zod"
|
|
33819
33850
|
],
|
|
33820
33851
|
devices: Infinity,
|
|
33821
33852
|
ciSupport: true,
|
|
@@ -33833,7 +33864,16 @@ var TIER_FEATURES = {
|
|
|
33833
33864
|
customPlugins: true,
|
|
33834
33865
|
governance: true,
|
|
33835
33866
|
failOnWarnings: true,
|
|
33836
|
-
maxRiskScoreThreshold: true
|
|
33867
|
+
maxRiskScoreThreshold: true,
|
|
33868
|
+
formResolverMigration: true,
|
|
33869
|
+
complexityEstimator: true,
|
|
33870
|
+
monorepoAware: true,
|
|
33871
|
+
behavioralWarnings: true,
|
|
33872
|
+
bundleEstimator: true,
|
|
33873
|
+
performanceAnalyzer: true,
|
|
33874
|
+
typeDeduplication: true,
|
|
33875
|
+
testScaffolding: true,
|
|
33876
|
+
auditLogging: true
|
|
33837
33877
|
}
|
|
33838
33878
|
};
|
|
33839
33879
|
var POLAR_ORG_ID = process.env.POLAR_ORG_ID || "79bbe935-1836-4b9e-9ca8-4c7a94217f5e";
|
|
@@ -34596,6 +34636,12 @@ var WatchMode = class {
|
|
|
34596
34636
|
console.log(pc.cyan(`
|
|
34597
34637
|
Watching ${files.length} files for changes...
|
|
34598
34638
|
`));
|
|
34639
|
+
if (options.dependents && options.dependents.size > 0) {
|
|
34640
|
+
console.log(
|
|
34641
|
+
pc.dim(`Dependency-aware mode: ${options.dependents.size} files with dependents
|
|
34642
|
+
`)
|
|
34643
|
+
);
|
|
34644
|
+
}
|
|
34599
34645
|
console.log(pc.dim("Press Ctrl+C to stop\n"));
|
|
34600
34646
|
const directories = new Set(files.map((f) => f.split("/").slice(0, -1).join("/")));
|
|
34601
34647
|
for (const dir of directories) {
|
|
@@ -34616,11 +34662,33 @@ Watching ${files.length} files for changes...
|
|
|
34616
34662
|
Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
34617
34663
|
try {
|
|
34618
34664
|
await options.onTransform(fullPath);
|
|
34619
|
-
console.log(pc.green(`Transformed successfully
|
|
34620
|
-
|
|
34665
|
+
console.log(pc.green(`Transformed successfully`));
|
|
34666
|
+
const dependentFiles = options.dependents?.get(fullPath);
|
|
34667
|
+
if (dependentFiles && dependentFiles.length > 0) {
|
|
34668
|
+
console.log(
|
|
34669
|
+
pc.cyan(` Re-transforming ${dependentFiles.length} dependent file(s)...`)
|
|
34670
|
+
);
|
|
34671
|
+
for (const depFile of dependentFiles) {
|
|
34672
|
+
try {
|
|
34673
|
+
await options.onTransform(depFile);
|
|
34674
|
+
console.log(pc.green(` \u2713 ${relative2(process.cwd(), depFile)}`));
|
|
34675
|
+
} catch (error) {
|
|
34676
|
+
console.error(
|
|
34677
|
+
pc.red(
|
|
34678
|
+
` \u2717 ${relative2(process.cwd(), depFile)}: ${error instanceof Error ? error.message : String(error)}`
|
|
34679
|
+
)
|
|
34680
|
+
);
|
|
34681
|
+
}
|
|
34682
|
+
}
|
|
34683
|
+
}
|
|
34684
|
+
console.log("");
|
|
34621
34685
|
} catch (error) {
|
|
34622
|
-
console.error(
|
|
34623
|
-
|
|
34686
|
+
console.error(
|
|
34687
|
+
pc.red(
|
|
34688
|
+
`Transform failed: ${error instanceof Error ? error.message : String(error)}
|
|
34689
|
+
`
|
|
34690
|
+
)
|
|
34691
|
+
);
|
|
34624
34692
|
}
|
|
34625
34693
|
this.debounceTimers.delete(fullPath);
|
|
34626
34694
|
}, 100)
|
|
@@ -34639,6 +34707,21 @@ Changed: ${relative2(process.cwd(), fullPath)}`));
|
|
|
34639
34707
|
}
|
|
34640
34708
|
this.debounceTimers.clear();
|
|
34641
34709
|
}
|
|
34710
|
+
/**
|
|
34711
|
+
* Build a reverse dependency map (file -> files that depend on it).
|
|
34712
|
+
* Takes the forward dependency map from SchemaDependencyResolver and inverts it.
|
|
34713
|
+
*/
|
|
34714
|
+
static buildDependentsMap(dependencies) {
|
|
34715
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
34716
|
+
for (const [file, deps] of dependencies) {
|
|
34717
|
+
for (const dep of deps) {
|
|
34718
|
+
const existing = dependents.get(dep) ?? [];
|
|
34719
|
+
existing.push(file);
|
|
34720
|
+
dependents.set(dep, existing);
|
|
34721
|
+
}
|
|
34722
|
+
}
|
|
34723
|
+
return dependents;
|
|
34724
|
+
}
|
|
34642
34725
|
matchesPatterns(file, include, exclude) {
|
|
34643
34726
|
const isIncluded = include.some((p) => minimatch(file, p));
|
|
34644
34727
|
const isExcluded = exclude.some((p) => minimatch(file, p));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "schemashift-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "TypeScript schema migration CLI - migrate between Zod, Yup, Joi, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"author": "Joseph May",
|
|
50
50
|
"license": "MIT",
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@schemashift/core": "0.
|
|
53
|
-
"@schemashift/io-ts-zod": "0.
|
|
54
|
-
"@schemashift/joi-zod": "0.
|
|
55
|
-
"@schemashift/yup-zod": "0.
|
|
56
|
-
"@schemashift/zod-valibot": "0.
|
|
57
|
-
"@schemashift/zod-v3-v4": "0.
|
|
52
|
+
"@schemashift/core": "0.9.0",
|
|
53
|
+
"@schemashift/io-ts-zod": "0.9.0",
|
|
54
|
+
"@schemashift/joi-zod": "0.9.0",
|
|
55
|
+
"@schemashift/yup-zod": "0.9.0",
|
|
56
|
+
"@schemashift/zod-valibot": "0.9.0",
|
|
57
|
+
"@schemashift/zod-v3-v4": "0.9.0",
|
|
58
58
|
"commander": "14.0.2",
|
|
59
59
|
"cosmiconfig": "9.0.0",
|
|
60
60
|
"glob": "13.0.0",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"picocolors": "1.1.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@schemashift/license": "0.
|
|
66
|
+
"@schemashift/license": "0.9.0"
|
|
67
67
|
},
|
|
68
68
|
"publishConfig": {
|
|
69
69
|
"access": "public"
|