zodvex 0.7.7-beta.0 → 0.7.7-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +55 -107
  2. package/dist/cli/index.d.ts +1 -1
  3. package/dist/cli/index.js +281 -68
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/client/index.js +5 -7
  6. package/dist/client/index.js.map +1 -1
  7. package/dist/codegen/index.js +240 -27
  8. package/dist/codegen/index.js.map +1 -1
  9. package/dist/core/index.js +58 -20
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/index.js +58 -20
  12. package/dist/index.js.map +1 -1
  13. package/dist/internal/boundaryHelpers.d.ts +2 -15
  14. package/dist/internal/boundaryHelpers.d.ts.map +1 -1
  15. package/dist/internal/custom.d.ts.map +1 -1
  16. package/dist/internal/registry.d.ts +7 -2
  17. package/dist/internal/registry.d.ts.map +1 -1
  18. package/dist/internal/zx.d.ts.map +1 -1
  19. package/dist/internal/zxDateBrand.d.ts +5 -0
  20. package/dist/internal/zxDateBrand.d.ts.map +1 -0
  21. package/dist/legacy/index.js +27 -12
  22. package/dist/legacy/index.js.map +1 -1
  23. package/dist/mini/client/index.js +5 -7
  24. package/dist/mini/client/index.js.map +1 -1
  25. package/dist/mini/index.js +58 -20
  26. package/dist/mini/index.js.map +1 -1
  27. package/dist/mini/react/index.js +5 -7
  28. package/dist/mini/react/index.js.map +1 -1
  29. package/dist/mini/server/index.js +32 -19
  30. package/dist/mini/server/index.js.map +1 -1
  31. package/dist/public/cli/index.d.ts +1 -1
  32. package/dist/public/codegen/discover.d.ts.map +1 -1
  33. package/dist/public/codegen/discovery-hooks.d.ts +2 -1
  34. package/dist/public/codegen/discovery-hooks.d.ts.map +1 -1
  35. package/dist/public/codegen/extractCodec.d.ts.map +1 -1
  36. package/dist/public/codegen/tsconfigPaths.d.ts +41 -0
  37. package/dist/public/codegen/tsconfigPaths.d.ts.map +1 -0
  38. package/dist/public/codegen/zodToSource.d.ts.map +1 -1
  39. package/dist/react/index.js +5 -7
  40. package/dist/react/index.js.map +1 -1
  41. package/dist/server/index.js +32 -19
  42. package/dist/server/index.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/cli/index.ts +1 -1
  45. package/src/internal/boundaryHelpers.ts +19 -9
  46. package/src/internal/custom.ts +11 -2
  47. package/src/internal/db.ts +1 -1
  48. package/src/internal/mapping/types.ts +2 -2
  49. package/src/internal/registry.ts +50 -3
  50. package/src/internal/types.ts +2 -2
  51. package/src/internal/zx.ts +12 -9
  52. package/src/internal/zxDateBrand.ts +34 -0
  53. package/src/public/cli/index.ts +1 -1
  54. package/src/public/codegen/discover.ts +31 -5
  55. package/src/public/codegen/discovery-hooks.ts +73 -8
  56. package/src/public/codegen/extractCodec.ts +5 -11
  57. package/src/public/codegen/tsconfigPaths.ts +224 -0
  58. package/src/public/codegen/zodToSource.ts +5 -6
package/dist/cli/index.js CHANGED
@@ -1,12 +1,12 @@
1
- #!/usr/bin/env bun
2
- import fs2, { readFileSync, writeFileSync, existsSync } from 'fs';
3
- import path3, { resolve, relative, join } from 'path';
1
+ #!/usr/bin/env node
2
+ import fs3, { readFileSync, writeFileSync, existsSync } from 'fs';
3
+ import path4, { resolve, relative, join } from 'path';
4
4
  import { Project, SyntaxKind } from 'ts-morph';
5
5
  import { globSync } from 'tinyglobby';
6
6
  import { spawn } from 'child_process';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { z } from 'zod';
9
- import { $ZodCodec, $ZodNumber, $ZodCustom, $ZodType, $ZodOptional, $ZodNullable, $ZodObject, $ZodUnion, $ZodArray, $ZodRecord, $ZodTuple, clone, $ZodString, $ZodBoolean, $ZodNull, $ZodUndefined, $ZodAny, $ZodEnum, $ZodLiteral, $ZodDiscriminatedUnion } from 'zod/v4/core';
9
+ import { $ZodCodec, $ZodType, $ZodOptional, $ZodNullable, $ZodObject, $ZodUnion, $ZodArray, $ZodRecord, $ZodTuple, clone, $ZodString, $ZodNumber, $ZodBoolean, $ZodNull, $ZodUndefined, $ZodAny, $ZodEnum, $ZodLiteral, $ZodDiscriminatedUnion } from 'zod/v4/core';
10
10
  import 'convex/values';
11
11
 
12
12
  var __defProp = Object.defineProperty;
@@ -62,12 +62,12 @@ function gitignoreEntry(content) {
62
62
  return lines.join("\n");
63
63
  }
64
64
  function generateStubs(convexDir2) {
65
- const zodvexDir = path3.join(convexDir2, "_zodvex");
66
- fs2.mkdirSync(zodvexDir, { recursive: true });
65
+ const zodvexDir = path4.join(convexDir2, "_zodvex");
66
+ fs3.mkdirSync(zodvexDir, { recursive: true });
67
67
  const apiStub = `// Auto-generated stub. Run \`zodvex generate\` to populate.
68
68
  export const zodvexRegistry = {} as const
69
69
  `;
70
- fs2.writeFileSync(path3.join(zodvexDir, "api.ts"), apiStub);
70
+ fs3.writeFileSync(path4.join(zodvexDir, "api.ts"), apiStub);
71
71
  const clientStub = `// Auto-generated stub. Run \`zodvex generate\` to populate.
72
72
  import { zodvexRegistry } from './api'
73
73
 
@@ -75,11 +75,11 @@ export const useZodQuery = undefined as any
75
75
  export const useZodMutation = undefined as any
76
76
  export const createClient = undefined as any
77
77
  `;
78
- fs2.writeFileSync(path3.join(zodvexDir, "client.ts"), clientStub);
78
+ fs3.writeFileSync(path4.join(zodvexDir, "client.ts"), clientStub);
79
79
  }
80
80
  async function init() {
81
- const convexDir2 = path3.resolve("convex");
82
- if (!fs2.existsSync(convexDir2)) {
81
+ const convexDir2 = path4.resolve("convex");
82
+ if (!fs3.existsSync(convexDir2)) {
83
83
  console.error("[zodvex] No convex/ directory found. Run this from your project root.");
84
84
  return;
85
85
  }
@@ -100,14 +100,14 @@ __export(migrate_exports, {
100
100
  function collectFiles(dir) {
101
101
  const results = [];
102
102
  function walk(current) {
103
- const entries = fs2.readdirSync(current, { withFileTypes: true });
103
+ const entries = fs3.readdirSync(current, { withFileTypes: true });
104
104
  for (const entry of entries) {
105
105
  if (entry.isDirectory()) {
106
106
  if (!SKIP_DIRS.has(entry.name)) {
107
- walk(path3.join(current, entry.name));
107
+ walk(path4.join(current, entry.name));
108
108
  }
109
- } else if (entry.isFile() && TS_EXTENSIONS.has(path3.extname(entry.name))) {
110
- results.push(path3.join(current, entry.name));
109
+ } else if (entry.isFile() && TS_EXTENSIONS.has(path4.extname(entry.name))) {
110
+ results.push(path4.join(current, entry.name));
111
111
  }
112
112
  }
113
113
  }
@@ -204,7 +204,7 @@ function migrate(dir, options) {
204
204
  let wouldChange = 0;
205
205
  const allDeprecations = [];
206
206
  for (const filePath of files) {
207
- const original = fs2.readFileSync(filePath, "utf-8");
207
+ const original = fs3.readFileSync(filePath, "utf-8");
208
208
  let content = original;
209
209
  content = applyIdentifierRenames(content);
210
210
  content = applyZidTransform(content);
@@ -214,7 +214,7 @@ function migrate(dir, options) {
214
214
  if (options.dryRun) {
215
215
  wouldChange++;
216
216
  } else {
217
- fs2.writeFileSync(filePath, content);
217
+ fs3.writeFileSync(filePath, content);
218
218
  filesChanged++;
219
219
  }
220
220
  }
@@ -1054,18 +1054,34 @@ function zodvexCodec(wire, runtime, transforms) {
1054
1054
  return z.codec(wire, runtime, transforms);
1055
1055
  }
1056
1056
 
1057
+ // src/internal/zxDateBrand.ts
1058
+ var ZX_DATE_BRAND = /* @__PURE__ */ Symbol.for("zodvex.zxDate");
1059
+ function brandZxDate(codec2) {
1060
+ codec2[ZX_DATE_BRAND] = true;
1061
+ const def = codec2._zod?.def;
1062
+ if (def) def[ZX_DATE_BRAND] = true;
1063
+ return codec2;
1064
+ }
1065
+ function isZxDateCodec(schema) {
1066
+ if (!schema || typeof schema !== "object") return false;
1067
+ const s = schema;
1068
+ return s[ZX_DATE_BRAND] === true || s._zod?.def?.[ZX_DATE_BRAND] === true;
1069
+ }
1070
+
1057
1071
  // src/internal/zx.ts
1058
1072
  function date() {
1059
- return zodvexCodec(
1060
- z.number(),
1061
- // Wire: timestamp
1062
- z.custom((val) => val instanceof Date, {
1063
- message: "Expected Date instance"
1064
- }),
1065
- {
1066
- decode: (timestamp) => new Date(timestamp),
1067
- encode: (date2) => date2.getTime()
1068
- }
1073
+ return brandZxDate(
1074
+ zodvexCodec(
1075
+ z.number(),
1076
+ // Wire: timestamp
1077
+ z.custom((val) => val instanceof Date, {
1078
+ message: "Expected Date instance"
1079
+ }),
1080
+ {
1081
+ decode: (timestamp) => new Date(timestamp),
1082
+ encode: (date2) => date2.getTime()
1083
+ }
1084
+ )
1069
1085
  );
1070
1086
  }
1071
1087
  function id(tableName) {
@@ -1304,12 +1320,66 @@ function createSchemaUpdateSchema(name, inputSchema) {
1304
1320
  }
1305
1321
  return inputSchema;
1306
1322
  }
1307
- var HOOKS_SOURCE = `
1308
- export function resolve(specifier, context, nextResolve) {
1323
+ function buildHooksSource(aliases) {
1324
+ return `
1325
+ import { pathToFileURL } from 'node:url';
1326
+
1327
+ const EXT_CANDIDATES = ['.ts', '.tsx', '.mts', '.js', '.mjs', '.jsx', '/index.ts', '/index.js'];
1328
+
1329
+ // tsconfig path aliases compiled by loadTsconfigAliases (#99). Bun resolves
1330
+ // these natively; Node's ESM loader needs them replayed here.
1331
+ const ALIASES = ${JSON.stringify(aliases)};
1332
+
1333
+ function aliasCandidates(specifier) {
1334
+ const out = [];
1335
+ for (const a of ALIASES) {
1336
+ if (a.star) {
1337
+ if (
1338
+ specifier.length >= a.prefix.length + a.suffix.length &&
1339
+ specifier.startsWith(a.prefix) &&
1340
+ specifier.endsWith(a.suffix)
1341
+ ) {
1342
+ const captured = specifier.slice(a.prefix.length, specifier.length - a.suffix.length);
1343
+ for (const t of a.targets) out.push(t.prefix + captured + t.suffix);
1344
+ }
1345
+ } else if (specifier === a.prefix) {
1346
+ for (const t of a.targets) out.push(t.prefix);
1347
+ }
1348
+ }
1349
+ return out;
1350
+ }
1351
+
1352
+ export async function resolve(specifier, context, nextResolve) {
1309
1353
  if (/_generated\\/api(\\.[mc]?[jt]sx?)?$/.test(specifier)) {
1310
1354
  return { shortCircuit: true, url: 'zodvex-stub://api' };
1311
1355
  }
1312
- return nextResolve(specifier, context);
1356
+ try {
1357
+ return await nextResolve(specifier, context);
1358
+ } catch (err) {
1359
+ // tsconfig path aliases (e.g. '@/convex/...'). Try each mapped absolute
1360
+ // path as-is, then with the usual extension/index candidates.
1361
+ for (const base of aliasCandidates(specifier)) {
1362
+ for (const ext of ['', ...EXT_CANDIDATES]) {
1363
+ try {
1364
+ return await nextResolve(pathToFileURL(base + ext).href, context);
1365
+ } catch {}
1366
+ }
1367
+ }
1368
+ // Convex code uses extensionless relative imports (bundler resolution).
1369
+ // Bun resolves those natively; Node's ESM loader does not \u2014 retry with
1370
+ // the usual extension candidates before giving up.
1371
+ if (
1372
+ (specifier.startsWith('./') || specifier.startsWith('../')) &&
1373
+ !/\\.[cm]?[jt]sx?$/.test(specifier)
1374
+ ) {
1375
+ for (const ext of EXT_CANDIDATES) {
1376
+ try {
1377
+ return await nextResolve(specifier + ext, context);
1378
+ } catch {}
1379
+ }
1380
+ }
1381
+ throw err;
1382
+ }
1313
1383
  }
1314
1384
 
1315
1385
  export function load(url, context, nextLoad) {
@@ -1339,13 +1409,18 @@ export function load(url, context, nextLoad) {
1339
1409
  return nextLoad(url, context);
1340
1410
  }
1341
1411
  `;
1412
+ }
1342
1413
  var hooksRegistered = false;
1343
- function registerDiscoveryHooks() {
1414
+ function registerDiscoveryHooks(aliases = []) {
1344
1415
  if (hooksRegistered) return true;
1345
1416
  try {
1346
- const { register } = __require("module");
1417
+ const nodeModule = typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("node:module") : (
1418
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
1419
+ __require("module")
1420
+ );
1421
+ const { register } = nodeModule;
1347
1422
  if (typeof register !== "function") return false;
1348
- register(`data:text/javascript,${encodeURIComponent(HOOKS_SOURCE)}`);
1423
+ register(`data:text/javascript,${encodeURIComponent(buildHooksSource(aliases))}`);
1349
1424
  hooksRegistered = true;
1350
1425
  return true;
1351
1426
  } catch {
@@ -1370,22 +1445,22 @@ export const components = p;
1370
1445
  export const httpRouter = p;
1371
1446
  `;
1372
1447
  function writeGeneratedStubs(convexDir2) {
1373
- const generatedDir = path3.join(convexDir2, "_generated");
1374
- const apiPath = path3.join(generatedDir, "api.ts");
1448
+ const generatedDir = path4.join(convexDir2, "_generated");
1449
+ const apiPath = path4.join(generatedDir, "api.ts");
1375
1450
  let original;
1376
1451
  try {
1377
- original = fs2.readFileSync(apiPath, "utf8");
1452
+ original = fs3.readFileSync(apiPath, "utf8");
1378
1453
  } catch {
1379
1454
  original = null;
1380
1455
  }
1381
- fs2.mkdirSync(generatedDir, { recursive: true });
1382
- fs2.writeFileSync(apiPath, PROXY_STUB_API);
1456
+ fs3.mkdirSync(generatedDir, { recursive: true });
1457
+ fs3.writeFileSync(apiPath, PROXY_STUB_API);
1383
1458
  return () => {
1384
1459
  if (original !== null) {
1385
- fs2.writeFileSync(apiPath, original);
1460
+ fs3.writeFileSync(apiPath, original);
1386
1461
  } else {
1387
1462
  try {
1388
- fs2.unlinkSync(apiPath);
1463
+ fs3.unlinkSync(apiPath);
1389
1464
  } catch {
1390
1465
  }
1391
1466
  }
@@ -1397,8 +1472,7 @@ function findCodec(schema) {
1397
1472
  let current = schema;
1398
1473
  for (let i = 0; i < 10; i++) {
1399
1474
  if (current instanceof $ZodCodec) {
1400
- const isZxDate = current._zod.def.in instanceof $ZodNumber && current._zod.def.out instanceof $ZodCustom;
1401
- if (isZxDate) return void 0;
1475
+ if (isZxDateCodec(current)) return void 0;
1402
1476
  return current;
1403
1477
  }
1404
1478
  if (current instanceof $ZodOptional || current instanceof $ZodNullable) {
@@ -1409,6 +1483,134 @@ function findCodec(schema) {
1409
1483
  }
1410
1484
  return void 0;
1411
1485
  }
1486
+ function stripJsonComments(input) {
1487
+ let out = "";
1488
+ let inString = false;
1489
+ let inLine = false;
1490
+ let inBlock = false;
1491
+ for (let i = 0; i < input.length; i++) {
1492
+ const ch = input[i];
1493
+ const next = input[i + 1];
1494
+ if (inLine) {
1495
+ if (ch === "\n") {
1496
+ inLine = false;
1497
+ out += ch;
1498
+ }
1499
+ continue;
1500
+ }
1501
+ if (inBlock) {
1502
+ if (ch === "*" && next === "/") {
1503
+ inBlock = false;
1504
+ i++;
1505
+ }
1506
+ continue;
1507
+ }
1508
+ if (inString) {
1509
+ out += ch;
1510
+ if (ch === "\\") {
1511
+ out += next ?? "";
1512
+ i++;
1513
+ } else if (ch === '"') {
1514
+ inString = false;
1515
+ }
1516
+ continue;
1517
+ }
1518
+ if (ch === '"') {
1519
+ inString = true;
1520
+ out += ch;
1521
+ continue;
1522
+ }
1523
+ if (ch === "/" && next === "/") {
1524
+ inLine = true;
1525
+ i++;
1526
+ continue;
1527
+ }
1528
+ if (ch === "/" && next === "*") {
1529
+ inBlock = true;
1530
+ i++;
1531
+ continue;
1532
+ }
1533
+ out += ch;
1534
+ }
1535
+ return out.replace(/,\s*([}\]])/g, "$1");
1536
+ }
1537
+ function readTsconfig(configPath) {
1538
+ try {
1539
+ const raw = fs3.readFileSync(configPath, "utf8");
1540
+ return JSON.parse(stripJsonComments(raw));
1541
+ } catch {
1542
+ return null;
1543
+ }
1544
+ }
1545
+ function resolvePathsFromConfig(configPath, seen) {
1546
+ const abs = path4.resolve(configPath);
1547
+ if (seen.has(abs)) return null;
1548
+ seen.add(abs);
1549
+ const config = readTsconfig(abs);
1550
+ if (!config) return null;
1551
+ const dir = path4.dirname(abs);
1552
+ const own = config.compilerOptions?.paths;
1553
+ if (own) {
1554
+ const baseUrl = config.compilerOptions?.baseUrl;
1555
+ return { paths: own, baseDir: baseUrl ? path4.resolve(dir, baseUrl) : dir };
1556
+ }
1557
+ const parents = config.extends == null ? [] : [].concat(config.extends);
1558
+ for (const ref of parents) {
1559
+ if (!ref.startsWith(".") && !path4.isAbsolute(ref)) continue;
1560
+ let target = path4.resolve(dir, ref);
1561
+ if (!target.endsWith(".json")) target += ".json";
1562
+ const found = resolvePathsFromConfig(target, seen);
1563
+ if (found) {
1564
+ const childBase = config.compilerOptions?.baseUrl;
1565
+ return childBase ? { paths: found.paths, baseDir: path4.resolve(dir, childBase) } : found;
1566
+ }
1567
+ }
1568
+ return null;
1569
+ }
1570
+ function compileAliases(paths, baseDir) {
1571
+ const entries = [];
1572
+ for (const [pattern, targets] of Object.entries(paths)) {
1573
+ const starIdx = pattern.indexOf("*");
1574
+ const star = starIdx !== -1;
1575
+ const prefix = star ? pattern.slice(0, starIdx) : pattern;
1576
+ const suffix = star ? pattern.slice(starIdx + 1) : "";
1577
+ const compiled = targets.map((t) => {
1578
+ const tStar = t.indexOf("*");
1579
+ const tPrefix = path4.resolve(baseDir, tStar === -1 ? t : t.slice(0, tStar));
1580
+ const sep = tStar !== -1 && t.slice(0, tStar).endsWith("/") ? path4.sep : "";
1581
+ return {
1582
+ prefix: tPrefix + sep,
1583
+ suffix: tStar === -1 ? "" : t.slice(tStar + 1)
1584
+ };
1585
+ });
1586
+ entries.push({ prefix, suffix, star, targets: compiled });
1587
+ }
1588
+ return entries;
1589
+ }
1590
+ function loadTsconfigAliases(convexDir2) {
1591
+ const entries = [];
1592
+ const seenPatterns = /* @__PURE__ */ new Set();
1593
+ let dir = path4.resolve(convexDir2);
1594
+ for (let depth = 0; depth < 10; depth++) {
1595
+ const configPath = path4.join(dir, "tsconfig.json");
1596
+ if (fs3.existsSync(configPath)) {
1597
+ const found = resolvePathsFromConfig(configPath, /* @__PURE__ */ new Set());
1598
+ if (found) {
1599
+ for (const entry of compileAliases(found.paths, found.baseDir)) {
1600
+ const key = `${entry.prefix}*${entry.suffix}`;
1601
+ if (!seenPatterns.has(key)) {
1602
+ seenPatterns.add(key);
1603
+ entries.push(entry);
1604
+ }
1605
+ }
1606
+ }
1607
+ }
1608
+ const parent = path4.dirname(dir);
1609
+ if (parent === dir) break;
1610
+ dir = parent;
1611
+ }
1612
+ return entries;
1613
+ }
1412
1614
 
1413
1615
  // src/public/codegen/discover.ts
1414
1616
  function walkSchemaRecursive(schema, accessPath, visited, seenCodecs, results) {
@@ -1552,7 +1754,7 @@ async function discoverModules(convexDir2) {
1552
1754
  const models = [];
1553
1755
  const functions = [];
1554
1756
  const codecs = [];
1555
- registerDiscoveryHooks();
1757
+ registerDiscoveryHooks(loadTsconfigAliases(convexDir2));
1556
1758
  const cleanupStubs = writeGeneratedStubs(convexDir2);
1557
1759
  const files = globSync(["**/*.{ts,js}"], {
1558
1760
  cwd: convexDir2,
@@ -1572,13 +1774,15 @@ async function discoverModules(convexDir2) {
1572
1774
  "crons.js"
1573
1775
  ]
1574
1776
  }).sort();
1777
+ const failedImports = [];
1575
1778
  try {
1576
1779
  for (const file of files) {
1577
- const absPath = path3.resolve(convexDir2, file);
1780
+ const absPath = path4.resolve(convexDir2, file);
1578
1781
  let moduleExports;
1579
1782
  try {
1580
1783
  moduleExports = await import(absPath);
1581
1784
  } catch (err) {
1785
+ failedImports.push({ file, message: err.message });
1582
1786
  console.warn(`[zodvex] Warning: Failed to import ${file}:`, err.message);
1583
1787
  continue;
1584
1788
  }
@@ -1622,8 +1826,7 @@ async function discoverModules(convexDir2) {
1622
1826
  }
1623
1827
  }
1624
1828
  if (value instanceof $ZodCodec) {
1625
- const isZxDate = value._zod.def.in instanceof $ZodNumber && value._zod.def.out instanceof $ZodCustom;
1626
- if (!isZxDate) {
1829
+ if (!isZxDateCodec(value)) {
1627
1830
  if (!codecs.some((c) => c.schema === value)) {
1628
1831
  codecs.push({
1629
1832
  exportName,
@@ -1646,6 +1849,16 @@ async function discoverModules(convexDir2) {
1646
1849
  modelCodecs.push(...found);
1647
1850
  }
1648
1851
  const functionCodecs = walkFunctionCodecs(functions);
1852
+ if (failedImports.length > 0 && !process.env.ZODVEX_ALLOW_IMPORT_FAILURES) {
1853
+ const shown = failedImports.slice(0, 10).map((f) => ` - ${f.file}: ${f.message}`).join("\n");
1854
+ const more = failedImports.length > 10 ? `
1855
+ \u2026 and ${failedImports.length - 10} more` : "";
1856
+ throw new Error(
1857
+ `[zodvex] ${failedImports.length} module(s) failed to import during discovery \u2014 the generated registry would be incomplete:
1858
+ ${shown}${more}
1859
+ Common causes: Node < 22.18 (no native TypeScript type stripping \u2014 use Bun or upgrade Node), or an import style the discovery resolver does not handle. tsconfig path aliases and extensionless relative imports are supported. Set ZODVEX_ALLOW_IMPORT_FAILURES=1 to generate anyway (skipping the failed modules).`
1860
+ );
1861
+ }
1649
1862
  return { models, functions, codecs, modelCodecs, functionCodecs };
1650
1863
  } finally {
1651
1864
  cleanupStubs();
@@ -1668,7 +1881,7 @@ function zodToSource(schema, ctx) {
1668
1881
  return `zx.id("${tableName}")`;
1669
1882
  }
1670
1883
  }
1671
- if (schema instanceof $ZodCodec && schema._zod.def.in instanceof $ZodNumber && schema._zod.def.out instanceof $ZodCustom) {
1884
+ if (schema instanceof $ZodCodec && isZxDateCodec(schema)) {
1672
1885
  return "zx.date()";
1673
1886
  }
1674
1887
  if (schema instanceof $ZodCodec) {
@@ -2191,7 +2404,7 @@ ${dtsDeclarations.join("\n")}
2191
2404
  // src/public/cli/commands.ts
2192
2405
  async function generate(convexDir2, options) {
2193
2406
  const resolved = resolveConvexDir(convexDir2);
2194
- const zodvexDir = path3.join(resolved, "_zodvex");
2407
+ const zodvexDir = path4.join(resolved, "_zodvex");
2195
2408
  writeStubApi(zodvexDir);
2196
2409
  const result = await discoverModules(resolved);
2197
2410
  const schemaContent = generateSchemaFile(result.models);
@@ -2205,15 +2418,15 @@ async function generate(convexDir2, options) {
2205
2418
  );
2206
2419
  const clientContent = generateClientFile({ mini: options?.mini });
2207
2420
  const serverContent = generateServerFile();
2208
- fs2.mkdirSync(zodvexDir, { recursive: true });
2209
- writeIfChanged(path3.join(zodvexDir, "schema.js"), schemaContent.js);
2210
- writeIfChanged(path3.join(zodvexDir, "schema.d.ts"), schemaContent.dts);
2211
- writeIfChanged(path3.join(zodvexDir, "api.js"), apiContent.js);
2212
- writeIfChanged(path3.join(zodvexDir, "api.d.ts"), apiContent.dts);
2213
- writeIfChanged(path3.join(zodvexDir, "client.js"), clientContent.js);
2214
- writeIfChanged(path3.join(zodvexDir, "client.d.ts"), clientContent.dts);
2215
- writeIfChanged(path3.join(zodvexDir, "server.js"), serverContent.js);
2216
- writeIfChanged(path3.join(zodvexDir, "server.d.ts"), serverContent.dts);
2421
+ fs3.mkdirSync(zodvexDir, { recursive: true });
2422
+ writeIfChanged(path4.join(zodvexDir, "schema.js"), schemaContent.js);
2423
+ writeIfChanged(path4.join(zodvexDir, "schema.d.ts"), schemaContent.dts);
2424
+ writeIfChanged(path4.join(zodvexDir, "api.js"), apiContent.js);
2425
+ writeIfChanged(path4.join(zodvexDir, "api.d.ts"), apiContent.dts);
2426
+ writeIfChanged(path4.join(zodvexDir, "client.js"), clientContent.js);
2427
+ writeIfChanged(path4.join(zodvexDir, "client.d.ts"), clientContent.dts);
2428
+ writeIfChanged(path4.join(zodvexDir, "server.js"), serverContent.js);
2429
+ writeIfChanged(path4.join(zodvexDir, "server.d.ts"), serverContent.dts);
2217
2430
  const totalCodecs = result.codecs.length + result.modelCodecs.length + result.functionCodecs.length;
2218
2431
  console.log(
2219
2432
  `[zodvex] Generated ${result.models.length} model(s), ${result.functions.length} function(s), ${totalCodecs} codec(s)`
@@ -2224,7 +2437,7 @@ async function dev(convexDir2, options) {
2224
2437
  console.log("[zodvex] Starting watch mode...");
2225
2438
  await generate(resolved, options);
2226
2439
  let debounceTimer = null;
2227
- const watcher = fs2.watch(resolved, { recursive: true }, (_event, filename) => {
2440
+ const watcher = fs3.watch(resolved, { recursive: true }, (_event, filename) => {
2228
2441
  if (!filename) return;
2229
2442
  if (filename.startsWith("_zodvex") || filename.startsWith("_generated") || !filename.endsWith(".ts") && !filename.endsWith(".js")) {
2230
2443
  return;
@@ -2258,34 +2471,34 @@ function regenerate(resolved, options) {
2258
2471
  });
2259
2472
  }
2260
2473
  function writeStubApi(zodvexDir) {
2261
- fs2.mkdirSync(zodvexDir, { recursive: true });
2262
- fs2.writeFileSync(
2263
- path3.join(zodvexDir, "api.js"),
2474
+ fs3.mkdirSync(zodvexDir, { recursive: true });
2475
+ fs3.writeFileSync(
2476
+ path4.join(zodvexDir, "api.js"),
2264
2477
  "// AUTO-GENERATED by zodvex \u2014 do not edit\n// Stub created for codegen bootstrap\n\nexport const zodvexRegistry = {}\n"
2265
2478
  );
2266
- fs2.writeFileSync(
2267
- path3.join(zodvexDir, "api.d.ts"),
2479
+ fs3.writeFileSync(
2480
+ path4.join(zodvexDir, "api.d.ts"),
2268
2481
  "// AUTO-GENERATED by zodvex \u2014 do not edit\n// Stub created for codegen bootstrap\n\nexport declare const zodvexRegistry: Record<string, any>\n"
2269
2482
  );
2270
2483
  }
2271
2484
  function writeIfChanged(filePath, content) {
2272
2485
  try {
2273
- const existing = fs2.readFileSync(filePath, "utf-8");
2486
+ const existing = fs3.readFileSync(filePath, "utf-8");
2274
2487
  if (existing === content) return;
2275
2488
  } catch {
2276
2489
  }
2277
- fs2.writeFileSync(filePath, content);
2490
+ fs3.writeFileSync(filePath, content);
2278
2491
  }
2279
2492
  function resolveConvexDir(dir) {
2280
2493
  if (dir) {
2281
- const resolved = path3.resolve(dir);
2282
- if (!fs2.existsSync(resolved)) {
2494
+ const resolved = path4.resolve(dir);
2495
+ if (!fs3.existsSync(resolved)) {
2283
2496
  throw new Error(`Convex directory not found: ${resolved}`);
2284
2497
  }
2285
2498
  return resolved;
2286
2499
  }
2287
- const defaultDir = path3.resolve("convex");
2288
- if (!fs2.existsSync(defaultDir)) {
2500
+ const defaultDir = path4.resolve("convex");
2501
+ if (!fs3.existsSync(defaultDir)) {
2289
2502
  throw new Error("No convex/ directory found. Specify the path: zodvex generate <path>");
2290
2503
  }
2291
2504
  return defaultDir;