zodvex 0.7.1-beta.1 → 0.7.1-beta.3

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.
@@ -0,0 +1,4 @@
1
+ export declare function runToMiniCodemod(targetDir: string, options?: {
2
+ dryRun?: boolean;
3
+ }): Promise<void>;
4
+ //# sourceMappingURL=codemod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codemod.d.ts","sourceRoot":"","sources":["../../src/cli/codemod.ts"],"names":[],"mappings":"AAyBA,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,OAAO,CAAC,IAAI,CAAC,CA6Ef"}
package/dist/cli/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
- import fs2 from 'fs';
3
- import path3 from 'path';
2
+ import fs2, { readFileSync, writeFileSync, existsSync } from 'fs';
3
+ import path3, { resolve, relative, join } from 'path';
4
+ import { Project, SyntaxKind } from 'ts-morph';
4
5
  import { globSync } from 'tinyglobby';
5
6
  import { $ZodCodec, $ZodNumber, $ZodCustom, $ZodOptional, $ZodNullable, $ZodObject, $ZodUnion, $ZodArray, $ZodRecord, $ZodTuple, $ZodString, $ZodBoolean, $ZodNull, $ZodUndefined, $ZodAny, $ZodEnum, $ZodLiteral } from 'zod/v4/core';
6
7
 
@@ -217,6 +218,525 @@ var init_migrate = __esm({
217
218
  ];
218
219
  }
219
220
  });
221
+ function getCallObject(call) {
222
+ const expr = call.getExpression();
223
+ if (expr.getKind() !== SyntaxKind.PropertyAccessExpression) return null;
224
+ const propAccess = expr;
225
+ return propAccess.getExpression().getText();
226
+ }
227
+ function getMethodName(call) {
228
+ const expr = call.getExpression();
229
+ if (expr.getKind() !== SyntaxKind.PropertyAccessExpression) return null;
230
+ return expr.getName();
231
+ }
232
+ function transformWrappers(file) {
233
+ let count = 0;
234
+ const calls = file.getDescendantsOfKind(SyntaxKind.CallExpression).reverse();
235
+ for (const call of calls) {
236
+ if (call.wasForgotten()) continue;
237
+ const method = getMethodName(call);
238
+ if (!method || !WRAPPER_METHODS.includes(method)) continue;
239
+ if (call.getArguments().length > 0) continue;
240
+ const obj = getCallObject(call);
241
+ if (!obj) continue;
242
+ call.replaceWithText(`z.${method}(${obj})`);
243
+ count++;
244
+ }
245
+ return count;
246
+ }
247
+ function isNamespaceCall(obj) {
248
+ return NAMESPACE_IDENTIFIERS.has(obj.trim());
249
+ }
250
+ function isLikelySchemaExpr(obj) {
251
+ if (obj.match(/^z\.\w+\(/)) return true;
252
+ if (obj.match(/^zx\.\w+\(/)) return true;
253
+ if (obj.match(/^z\.(optional|nullable)\(/)) return true;
254
+ return false;
255
+ }
256
+ function isZodSchemaByType(call, typeChecker) {
257
+ const expr = call.getExpression();
258
+ if (expr.getKind() !== SyntaxKind.PropertyAccessExpression) return false;
259
+ const receiver = expr.getExpression();
260
+ try {
261
+ const type = typeChecker.getTypeAtLocation(receiver);
262
+ if (type.isAny()) return null;
263
+ return type.getProperties().some((p) => p.getName() === "_zod");
264
+ } catch {
265
+ return null;
266
+ }
267
+ }
268
+ function transformChecks(file) {
269
+ let count = 0;
270
+ const calls = file.getDescendantsOfKind(SyntaxKind.CallExpression).reverse();
271
+ for (const call of calls) {
272
+ if (call.wasForgotten()) continue;
273
+ const method = getMethodName(call);
274
+ if (!method) continue;
275
+ const obj = getCallObject(call);
276
+ if (!obj) continue;
277
+ if (isNamespaceCall(obj)) continue;
278
+ const args = call.getArguments().map((a) => a.getText());
279
+ const argsStr = args.length > 0 ? args.join(", ") : "";
280
+ if (ZOD_ONLY_CHECK_METHODS.includes(method)) {
281
+ call.replaceWithText(`${obj}.check(z.${method}(${argsStr}))`);
282
+ count++;
283
+ continue;
284
+ }
285
+ if (AMBIGUOUS_CHECK_METHODS.includes(method) && isLikelySchemaExpr(obj)) {
286
+ call.replaceWithText(`${obj}.check(z.${method}(${argsStr}))`);
287
+ count++;
288
+ continue;
289
+ }
290
+ if ((method === "min" || method === "max") && isLikelySchemaExpr(obj)) {
291
+ const isString = obj.includes("z.string");
292
+ const checkName = isString ? STRING_RENAME[method] : NUMBER_RENAME[method];
293
+ call.replaceWithText(`${obj}.check(z.${checkName}(${argsStr}))`);
294
+ count++;
295
+ continue;
296
+ }
297
+ }
298
+ return count;
299
+ }
300
+ function transformMethods(file, typeChecker) {
301
+ let count = 0;
302
+ const calls = file.getDescendantsOfKind(SyntaxKind.CallExpression).reverse();
303
+ for (const call of calls) {
304
+ if (call.wasForgotten()) continue;
305
+ const method = getMethodName(call);
306
+ if (!method) continue;
307
+ const obj = getCallObject(call);
308
+ if (!obj) continue;
309
+ if (isNamespaceCall(obj)) continue;
310
+ const args = call.getArguments().map((a) => a.getText());
311
+ if (UNCONDITIONAL_TOP_LEVEL.includes(method)) {
312
+ const argsStr = args.length > 0 ? `, ${args.join(", ")}` : "";
313
+ call.replaceWithText(`z.${method}(${obj}${argsStr})`);
314
+ count++;
315
+ continue;
316
+ }
317
+ if (AMBIGUOUS_TOP_LEVEL.includes(method)) {
318
+ let isSchema;
319
+ if (typeChecker) {
320
+ const typeResult = isZodSchemaByType(call, typeChecker);
321
+ isSchema = typeResult === true || typeResult === null && isLikelySchemaExpr(obj);
322
+ } else {
323
+ isSchema = isLikelySchemaExpr(obj);
324
+ }
325
+ if (!isSchema) continue;
326
+ const argsStr = args.length > 0 ? `, ${args.join(", ")}` : "";
327
+ call.replaceWithText(`z.${method}(${obj}${argsStr})`);
328
+ count++;
329
+ continue;
330
+ }
331
+ if (RENAMED_METHODS.has(method)) {
332
+ const newName = RENAMED_METHODS.get(method);
333
+ const argsStr = args.length > 0 ? `, ${args.join(", ")}` : "";
334
+ call.replaceWithText(`z.${newName}(${obj}${argsStr})`);
335
+ count++;
336
+ continue;
337
+ }
338
+ if (method === TRANSFORM_METHOD) {
339
+ const argsStr = args.join(", ");
340
+ call.replaceWithText(`z.pipe(${obj}, z.transform(${argsStr}))`);
341
+ count++;
342
+ continue;
343
+ }
344
+ if (CHECK_WRAP_METHODS.includes(method)) {
345
+ const argsStr = args.join(", ");
346
+ call.replaceWithText(`${obj}.check(z.${method}(${argsStr}))`);
347
+ count++;
348
+ continue;
349
+ }
350
+ }
351
+ return count;
352
+ }
353
+ function transformConstructorReplacements(file) {
354
+ let count = 0;
355
+ const calls = file.getDescendantsOfKind(SyntaxKind.CallExpression).reverse();
356
+ for (const call of calls) {
357
+ if (call.wasForgotten()) continue;
358
+ const method = getMethodName(call);
359
+ if (!method) continue;
360
+ const obj = getCallObject(call);
361
+ if (!obj) continue;
362
+ if (method in CONSTRUCTOR_REPLACEMENTS && call.getArguments().length === 0) {
363
+ const match = obj.match(/^z\s*\.object\(([\s\S]*)\)$/);
364
+ if (!match) continue;
365
+ const shape = match[1];
366
+ const replacement = CONSTRUCTOR_REPLACEMENTS[method];
367
+ call.replaceWithText(`z.${replacement}(${shape})`);
368
+ count++;
369
+ continue;
370
+ }
371
+ if (method === "datetime" && /^z\s*\.string\(\s*\)$/.test(obj)) {
372
+ const args = call.getArguments().map((a) => a.getText());
373
+ const argsStr = args.length > 0 ? args.join(", ") : "";
374
+ call.replaceWithText(`z.iso.datetime(${argsStr})`);
375
+ count++;
376
+ continue;
377
+ }
378
+ }
379
+ return count;
380
+ }
381
+ function findObjectOnlyMethods(file) {
382
+ const results = [];
383
+ const calls = file.getDescendantsOfKind(SyntaxKind.CallExpression);
384
+ for (const call of calls) {
385
+ const method = getMethodName(call);
386
+ if (!method || !WARN_METHODS.includes(method)) continue;
387
+ results.push({
388
+ line: call.getStartLineNumber(),
389
+ method,
390
+ text: call.getText().slice(0, 80)
391
+ });
392
+ }
393
+ return results;
394
+ }
395
+ function transformImports(file) {
396
+ let count = 0;
397
+ const imports = file.getImportDeclarations();
398
+ for (const imp of imports) {
399
+ const moduleSpecifier = imp.getModuleSpecifierValue();
400
+ if (moduleSpecifier === "zod") {
401
+ imp.setModuleSpecifier("zod/mini");
402
+ count++;
403
+ }
404
+ }
405
+ return count;
406
+ }
407
+ function transformClassRefs(file) {
408
+ let count = 0;
409
+ const neededImports = /* @__PURE__ */ new Set();
410
+ const propAccesses = file.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression);
411
+ for (const pa of propAccesses) {
412
+ if (pa.wasForgotten()) continue;
413
+ const text = pa.getText();
414
+ const replacement = CLASS_RENAMES[text];
415
+ if (!replacement) continue;
416
+ pa.replaceWithText(replacement);
417
+ neededImports.add(replacement);
418
+ count++;
419
+ }
420
+ const qualNames = file.getDescendantsOfKind(SyntaxKind.QualifiedName);
421
+ for (const qn of qualNames) {
422
+ if (qn.wasForgotten()) continue;
423
+ const text = qn.getText();
424
+ const replacement = CLASS_RENAMES[text];
425
+ if (!replacement) continue;
426
+ qn.replaceWithText(replacement);
427
+ neededImports.add(replacement);
428
+ count++;
429
+ }
430
+ if (neededImports.size > 0) {
431
+ const existingCoreImport = file.getImportDeclaration(
432
+ (d) => d.getModuleSpecifierValue() === "zod/v4/core"
433
+ );
434
+ if (existingCoreImport) {
435
+ for (const name of neededImports) {
436
+ if (!existingCoreImport.getNamedImports().some((n) => n.getName() === name)) {
437
+ existingCoreImport.addNamedImport(name);
438
+ }
439
+ }
440
+ } else {
441
+ const internalImport = file.getImportDeclaration(
442
+ (d) => d.getModuleSpecifierValue().endsWith("/zod-core")
443
+ );
444
+ if (internalImport) {
445
+ for (const name of neededImports) {
446
+ if (!internalImport.getNamedImports().some((n) => n.getName() === name)) {
447
+ internalImport.addNamedImport(name);
448
+ }
449
+ }
450
+ } else {
451
+ file.addImportDeclaration({
452
+ moduleSpecifier: "zod/v4/core",
453
+ namedImports: [...neededImports].sort()
454
+ });
455
+ }
456
+ }
457
+ }
458
+ return count;
459
+ }
460
+ function transformFile(file, typeChecker) {
461
+ const filePath = file.getFilePath();
462
+ let constructorReplacements = 0;
463
+ let wrappers = 0;
464
+ let checks = 0;
465
+ let methods = 0;
466
+ for (let i = 0; i < 10; i++) {
467
+ const cr = transformConstructorReplacements(file);
468
+ const w = transformWrappers(file);
469
+ const c = transformChecks(file);
470
+ const m = transformMethods(file, typeChecker);
471
+ constructorReplacements += cr;
472
+ wrappers += w;
473
+ checks += c;
474
+ methods += m;
475
+ if (cr + w + c + m === 0) break;
476
+ }
477
+ const classRefs = transformClassRefs(file);
478
+ const objectOnlyWarnings = findObjectOnlyMethods(file);
479
+ const imports = 0;
480
+ return {
481
+ filePath,
482
+ constructorReplacements,
483
+ wrappers,
484
+ checks,
485
+ methods,
486
+ imports,
487
+ classRefs,
488
+ objectOnlyWarnings,
489
+ totalChanges: constructorReplacements + wrappers + checks + methods + classRefs
490
+ };
491
+ }
492
+ function transformCode(code, options) {
493
+ try {
494
+ const project = options?.project ?? new Project({
495
+ useInMemoryFileSystem: true,
496
+ compilerOptions: { strict: false }
497
+ });
498
+ const filename = options?.filename ?? "transform.ts";
499
+ const file = project.createSourceFile(filename, code, { overwrite: true });
500
+ const typeChecker = options?.project ? project.getTypeChecker() : void 0;
501
+ const result = transformFile(file, typeChecker);
502
+ const transformed = file.getFullText();
503
+ if (options?.project) {
504
+ project.removeSourceFile(file);
505
+ }
506
+ return {
507
+ code: transformed,
508
+ changed: result.totalChanges > 0
509
+ };
510
+ } catch (err) {
511
+ const filename = options?.filename ?? "unknown";
512
+ const message = err instanceof Error ? err.message : String(err);
513
+ console.warn(`[zod-to-mini] Transform failed for ${filename}, returning original code: ${message}`);
514
+ return { code, changed: false };
515
+ }
516
+ }
517
+ var WRAPPER_METHODS, NAMESPACE_IDENTIFIERS, ZOD_ONLY_CHECK_METHODS, AMBIGUOUS_CHECK_METHODS, STRING_RENAME, NUMBER_RENAME, UNCONDITIONAL_TOP_LEVEL, AMBIGUOUS_TOP_LEVEL, RENAMED_METHODS, TRANSFORM_METHOD, CHECK_WRAP_METHODS, CONSTRUCTOR_REPLACEMENTS, WARN_METHODS, CLASS_RENAMES;
518
+ var init_transforms = __esm({
519
+ "../zod-to-mini/src/transforms.ts"() {
520
+ WRAPPER_METHODS = ["optional", "nullable"];
521
+ NAMESPACE_IDENTIFIERS = /* @__PURE__ */ new Set(["z", "zx", "zm", "zod"]);
522
+ ZOD_ONLY_CHECK_METHODS = [
523
+ "email",
524
+ "url",
525
+ "uuid",
526
+ "cuid",
527
+ "cuid2",
528
+ "ulid",
529
+ "nanoid",
530
+ "emoji",
531
+ "base64",
532
+ "base64url",
533
+ "jwt",
534
+ "int",
535
+ "positive",
536
+ "negative",
537
+ "nonnegative",
538
+ "nonpositive",
539
+ "multipleOf"
540
+ ];
541
+ AMBIGUOUS_CHECK_METHODS = [
542
+ "trim",
543
+ "toLowerCase",
544
+ "toUpperCase",
545
+ "startsWith",
546
+ "endsWith",
547
+ "includes",
548
+ "regex",
549
+ "length",
550
+ "gt",
551
+ "gte",
552
+ "lt",
553
+ "lte"
554
+ ];
555
+ STRING_RENAME = {
556
+ min: "minLength",
557
+ max: "maxLength"
558
+ };
559
+ NUMBER_RENAME = {
560
+ min: "gte",
561
+ max: "lte"
562
+ };
563
+ UNCONDITIONAL_TOP_LEVEL = ["pipe", "brand"];
564
+ AMBIGUOUS_TOP_LEVEL = ["partial", "extend", "catchall", "omit", "pick"];
565
+ RENAMED_METHODS = /* @__PURE__ */ new Map([
566
+ ["default", "_default"]
567
+ ]);
568
+ TRANSFORM_METHOD = "transform";
569
+ CHECK_WRAP_METHODS = ["refine", "superRefine", "describe"];
570
+ CONSTRUCTOR_REPLACEMENTS = {
571
+ passthrough: "looseObject",
572
+ strict: "strictObject"
573
+ };
574
+ WARN_METHODS = [
575
+ "merge"
576
+ // use z.extend() or spread
577
+ ];
578
+ CLASS_RENAMES = {
579
+ "z.ZodError": "$ZodError",
580
+ "z.ZodType": "$ZodType",
581
+ "z.ZodTypeAny": "$ZodType",
582
+ "z.ZodRawShape": "$ZodShape",
583
+ "z.ZodObject": "$ZodObject",
584
+ "z.ZodArray": "$ZodArray",
585
+ "z.ZodString": "$ZodString",
586
+ "z.ZodNumber": "$ZodNumber",
587
+ "z.ZodBoolean": "$ZodBoolean",
588
+ "z.ZodOptional": "$ZodOptional",
589
+ "z.ZodNullable": "$ZodNullable",
590
+ "z.ZodUnion": "$ZodUnion",
591
+ "z.ZodEnum": "$ZodEnum",
592
+ "z.ZodLiteral": "$ZodLiteral",
593
+ "z.ZodCodec": "$ZodCodec",
594
+ "z.ZodCustom": "$ZodCustom",
595
+ "z.ZodDefault": "$ZodDefault",
596
+ "z.ZodRecord": "$ZodRecord",
597
+ "z.ZodTuple": "$ZodTuple",
598
+ "z.ZodDiscriminatedUnion": "$ZodDiscriminatedUnion",
599
+ "z.ZodLazy": "$ZodLazy",
600
+ "z.ZodPipe": "$ZodPipe",
601
+ "z.ZodTransform": "$ZodTransform"
602
+ };
603
+ }
604
+ });
605
+ function zodToMiniPlugin(options) {
606
+ let project;
607
+ return {
608
+ name: "zod-to-mini",
609
+ enforce: "pre",
610
+ buildStart() {
611
+ if (options?.tsconfig) {
612
+ project = new Project({
613
+ tsConfigFilePath: options.tsconfig,
614
+ skipAddingFilesFromTsConfig: true
615
+ });
616
+ }
617
+ },
618
+ transform(code, id) {
619
+ if (!/\.[jt]sx?$/.test(id)) return;
620
+ if (options?.include && !options.include.test(id)) return;
621
+ if (options?.exclude && options.exclude.test(id)) return;
622
+ if (!code.includes("'zod'") && !code.includes('"zod"') && !code.includes("z.Zod")) return;
623
+ const result = transformCode(code, {
624
+ filename: id,
625
+ project
626
+ });
627
+ if (!result.changed) return;
628
+ return { code: result.code, map: null };
629
+ }
630
+ };
631
+ }
632
+ var init_vite_plugin = __esm({
633
+ "../zod-to-mini/src/vite-plugin.ts"() {
634
+ init_transforms();
635
+ }
636
+ });
637
+
638
+ // ../zod-to-mini/src/index.ts
639
+ var src_exports = {};
640
+ __export(src_exports, {
641
+ findObjectOnlyMethods: () => findObjectOnlyMethods,
642
+ transformChecks: () => transformChecks,
643
+ transformClassRefs: () => transformClassRefs,
644
+ transformCode: () => transformCode,
645
+ transformConstructorReplacements: () => transformConstructorReplacements,
646
+ transformFile: () => transformFile,
647
+ transformImports: () => transformImports,
648
+ transformMethods: () => transformMethods,
649
+ transformWrappers: () => transformWrappers,
650
+ zodToMiniPlugin: () => zodToMiniPlugin
651
+ });
652
+ var init_src = __esm({
653
+ "../zod-to-mini/src/index.ts"() {
654
+ init_transforms();
655
+ init_vite_plugin();
656
+ }
657
+ });
658
+
659
+ // src/cli/codemod.ts
660
+ var codemod_exports = {};
661
+ __export(codemod_exports, {
662
+ runToMiniCodemod: () => runToMiniCodemod
663
+ });
664
+ function findTsconfig(startDir) {
665
+ let dir = startDir;
666
+ while (true) {
667
+ const candidate = join(dir, "tsconfig.json");
668
+ if (existsSync(candidate)) return candidate;
669
+ const parent = resolve(dir, "..");
670
+ if (parent === dir) return void 0;
671
+ dir = parent;
672
+ }
673
+ }
674
+ async function runToMiniCodemod(targetDir, options = {}) {
675
+ const { transformCode: transformCode2, transformImports: transformImports2 } = await Promise.resolve().then(() => (init_src(), src_exports));
676
+ const { Project: Project3 } = await import('ts-morph');
677
+ const dir = resolve(process.cwd(), targetDir);
678
+ const files = globSync(["**/*.ts", "**/*.tsx"], {
679
+ cwd: dir,
680
+ ignore: ["_generated/**", "_zodvex/**", "**/*.d.ts", "node_modules/**"],
681
+ absolute: true
682
+ });
683
+ let typeProject;
684
+ const tsconfigPath = findTsconfig(dir);
685
+ if (tsconfigPath) {
686
+ typeProject = new Project3({
687
+ tsConfigFilePath: tsconfigPath,
688
+ skipAddingFilesFromTsConfig: true
689
+ });
690
+ console.log(`[zodvex codemod] Using type checker (${relative(process.cwd(), tsconfigPath)})`);
691
+ }
692
+ console.log(
693
+ `[zodvex codemod] ${options.dryRun ? "Dry run \u2014 " : ""}Processing ${files.length} files in ${targetDir}/`
694
+ );
695
+ console.log("");
696
+ let totalChanged = 0;
697
+ for (const filePath of files) {
698
+ const code = readFileSync(filePath, "utf-8");
699
+ if (!code.includes("'zod'") && !code.includes('"zod"')) continue;
700
+ const result = transformCode2(code, {
701
+ filename: filePath,
702
+ project: typeProject
703
+ });
704
+ let output = result.code;
705
+ const project = new Project3({ useInMemoryFileSystem: true });
706
+ const sf = project.createSourceFile("tmp.ts", output);
707
+ transformImports2(sf);
708
+ for (const imp of sf.getImportDeclarations()) {
709
+ const spec = imp.getModuleSpecifierValue();
710
+ if (spec === "zodvex/core") imp.setModuleSpecifier("zodvex/mini");
711
+ }
712
+ output = sf.getFullText();
713
+ if (output !== code) {
714
+ totalChanged++;
715
+ const rel = relative(process.cwd(), filePath);
716
+ if (options.dryRun) {
717
+ console.log(` would change: ${rel}`);
718
+ } else {
719
+ writeFileSync(filePath, output);
720
+ console.log(` changed: ${rel}`);
721
+ }
722
+ }
723
+ }
724
+ console.log("");
725
+ console.log(
726
+ `[zodvex codemod] ${totalChanged} file(s) ${options.dryRun ? "would be changed" : "changed"}.`
727
+ );
728
+ if (!options.dryRun && totalChanged > 0) {
729
+ console.log("");
730
+ console.log("Next steps:");
731
+ console.log(" 1. Run `zodvex generate --mini` to regenerate codegen output");
732
+ console.log(" 2. Run your type-checker and tests to verify");
733
+ console.log(" 3. To undo: git restore " + targetDir + "/");
734
+ }
735
+ }
736
+ var init_codemod = __esm({
737
+ "src/cli/codemod.ts"() {
738
+ }
739
+ });
220
740
 
221
741
  // src/meta.ts
222
742
  var META_KEY = "__zodvexMeta";
@@ -1074,6 +1594,18 @@ async function main() {
1074
1594
  }
1075
1595
  break;
1076
1596
  }
1597
+ case "codemod": {
1598
+ const toMini = process.argv.includes("--to-mini");
1599
+ if (!toMini) {
1600
+ console.error("[zodvex] No codemod specified. Available: --to-mini");
1601
+ process.exit(1);
1602
+ }
1603
+ const dryRun = process.argv.includes("--dry-run");
1604
+ const targetDir = process.argv.slice(3).find((a) => !a.startsWith("--")) ?? "convex";
1605
+ const { runToMiniCodemod: runToMiniCodemod2 } = await Promise.resolve().then(() => (init_codemod(), codemod_exports));
1606
+ await runToMiniCodemod2(targetDir, { dryRun });
1607
+ break;
1608
+ }
1077
1609
  case "help":
1078
1610
  case "--help":
1079
1611
  case "-h":
@@ -1096,6 +1628,8 @@ Usage:
1096
1628
  zodvex init Set up zodvex in an existing Convex project
1097
1629
  zodvex migrate [dir] Migrate pre-0.6 APIs (renames + import fixes)
1098
1630
  zodvex migrate [dir] --dry-run Preview changes without writing
1631
+ zodvex codemod --to-mini [dir] Convert full-zod code to zod/mini syntax
1632
+ zodvex codemod --to-mini [dir] --dry-run Preview changes without writing
1099
1633
  zodvex help Show this help message
1100
1634
 
1101
1635
  Flags: