zodvex 0.7.1 → 0.7.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +186 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/codegen/index.js +137 -14
- package/dist/codegen/index.js.map +1 -1
- package/dist/core/index.js +56 -10
- package/dist/core/index.js.map +1 -1
- package/dist/index.js +56 -10
- package/dist/index.js.map +1 -1
- package/dist/internal/db.d.ts +14 -11
- package/dist/internal/db.d.ts.map +1 -1
- package/dist/internal/meta.d.ts +11 -0
- package/dist/internal/meta.d.ts.map +1 -1
- package/dist/internal/model.d.ts +21 -0
- package/dist/internal/model.d.ts.map +1 -1
- package/dist/internal/zx.d.ts +14 -0
- package/dist/internal/zx.d.ts.map +1 -1
- package/dist/labs/index.js +29 -4
- package/dist/labs/index.js.map +1 -1
- package/dist/legacy/index.js +13 -2
- package/dist/legacy/index.js.map +1 -1
- package/dist/mini/index.js +58 -12
- package/dist/mini/index.js.map +1 -1
- package/dist/mini/server/index.js +37 -27
- package/dist/mini/server/index.js.map +1 -1
- package/dist/public/cli/commands.d.ts +12 -0
- package/dist/public/cli/commands.d.ts.map +1 -1
- package/dist/public/codegen/discover.d.ts +8 -0
- package/dist/public/codegen/discover.d.ts.map +1 -1
- package/dist/public/codegen/generate.d.ts.map +1 -1
- package/dist/public/mini/model.d.ts +6 -0
- package/dist/public/mini/model.d.ts.map +1 -1
- package/dist/public/mini/zx.d.ts +2 -0
- package/dist/public/mini/zx.d.ts.map +1 -1
- package/dist/public/model.d.ts +6 -0
- package/dist/public/model.d.ts.map +1 -1
- package/dist/server/index.js +37 -27
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/db.ts +33 -52
- package/src/internal/meta.ts +26 -0
- package/src/internal/model.ts +76 -9
- package/src/internal/zx.ts +18 -2
- package/src/public/cli/commands.ts +35 -6
- package/src/public/codegen/discover.ts +9 -1
- package/src/public/codegen/generate.ts +203 -15
- package/src/public/mini/model.ts +6 -0
- package/src/public/mini/zx.ts +3 -2
- package/src/public/model.ts +6 -0
package/dist/codegen/index.js
CHANGED
|
@@ -20,6 +20,20 @@ function readMeta(target) {
|
|
|
20
20
|
}
|
|
21
21
|
return target[META_KEY];
|
|
22
22
|
}
|
|
23
|
+
var CODEC_BRAND_KEY = "__zodvexCodecBrand";
|
|
24
|
+
function attachCodecBrand(target, brand) {
|
|
25
|
+
Object.defineProperty(target, CODEC_BRAND_KEY, {
|
|
26
|
+
value: brand,
|
|
27
|
+
enumerable: false,
|
|
28
|
+
writable: false,
|
|
29
|
+
configurable: false
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function readCodecBrand(target) {
|
|
33
|
+
if (target == null || typeof target !== "object") return void 0;
|
|
34
|
+
const value = target[CODEC_BRAND_KEY];
|
|
35
|
+
return typeof value === "string" ? value : void 0;
|
|
36
|
+
}
|
|
23
37
|
var metadata = /* @__PURE__ */ new WeakMap();
|
|
24
38
|
var registryHelpers = {
|
|
25
39
|
getMetadata: (type) => metadata.get(type),
|
|
@@ -60,8 +74,10 @@ function id(tableName) {
|
|
|
60
74
|
branded._tableName = tableName;
|
|
61
75
|
return branded;
|
|
62
76
|
}
|
|
63
|
-
function codec(wire, runtime, transforms) {
|
|
64
|
-
|
|
77
|
+
function codec(wire, runtime, transforms, opts) {
|
|
78
|
+
const built = zodvexCodec(wire, runtime, transforms);
|
|
79
|
+
if (opts?.brand) attachCodecBrand(built, opts.brand);
|
|
80
|
+
return built;
|
|
65
81
|
}
|
|
66
82
|
function sharedWeakMap(name) {
|
|
67
83
|
const key = /* @__PURE__ */ Symbol.for(`zodvex.zx.cache.${name}`);
|
|
@@ -567,7 +583,7 @@ async function discoverModules(convexDir) {
|
|
|
567
583
|
"crons.ts",
|
|
568
584
|
"crons.js"
|
|
569
585
|
]
|
|
570
|
-
});
|
|
586
|
+
}).sort();
|
|
571
587
|
try {
|
|
572
588
|
for (const file of files) {
|
|
573
589
|
const absPath = path.resolve(convexDir, file);
|
|
@@ -728,7 +744,35 @@ var HEADER = `// AUTO-GENERATED by zodvex \u2014 do not edit
|
|
|
728
744
|
`;
|
|
729
745
|
function fingerprintCodec(schema) {
|
|
730
746
|
if (!(schema instanceof $ZodCodec)) return "";
|
|
731
|
-
|
|
747
|
+
const def = schema._zod.def;
|
|
748
|
+
const transform = typeof def.transform === "function" ? def.transform.toString() : "";
|
|
749
|
+
const reverse = typeof def.reverseTransform === "function" ? def.reverseTransform.toString() : "";
|
|
750
|
+
return `${fingerprintLeaf(def.in)}|${fingerprintLeaf(def.out)}|${transform}|${reverse}`;
|
|
751
|
+
}
|
|
752
|
+
function fingerprintLeaf(schema) {
|
|
753
|
+
return `${zodToSource(schema)}#${fingerprintChecks(schema)}`;
|
|
754
|
+
}
|
|
755
|
+
function fingerprintChecks(schema) {
|
|
756
|
+
const checks = schema?._zod?.def?.checks;
|
|
757
|
+
if (!Array.isArray(checks) || checks.length === 0) return "";
|
|
758
|
+
const parts = [];
|
|
759
|
+
for (const check of checks) {
|
|
760
|
+
const def = check?._zod?.def;
|
|
761
|
+
if (!def) continue;
|
|
762
|
+
const checkType = def.check ?? def.type ?? "check";
|
|
763
|
+
const data = {};
|
|
764
|
+
for (const [k, v7] of Object.entries(def)) {
|
|
765
|
+
if (k === "check" || k === "type" || k === "error" || k === "message") continue;
|
|
766
|
+
const t = typeof v7;
|
|
767
|
+
if (t === "string" || t === "number" || t === "boolean" || v7 === null) {
|
|
768
|
+
data[k] = v7;
|
|
769
|
+
} else if (v7 instanceof RegExp) {
|
|
770
|
+
data[k] = v7.source;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
parts.push(`${checkType}(${JSON.stringify(data)})`);
|
|
774
|
+
}
|
|
775
|
+
return parts.sort().join("&");
|
|
732
776
|
}
|
|
733
777
|
function generateSchemaFile(models) {
|
|
734
778
|
const exports$1 = models.map((m) => {
|
|
@@ -815,6 +859,30 @@ function deriveCodecVarName(modelExportName, accessPath) {
|
|
|
815
859
|
return `_${prefix}${fieldPart[0].toUpperCase() + fieldPart.slice(1)}`;
|
|
816
860
|
}
|
|
817
861
|
function generateApiFile(functions, models, codecs, modelCodecs, functionCodecs, options) {
|
|
862
|
+
const sortedModels = [...models].sort(
|
|
863
|
+
(a, b) => `${a.sourceFile}|${a.exportName}`.localeCompare(`${b.sourceFile}|${b.exportName}`)
|
|
864
|
+
);
|
|
865
|
+
const sortedFunctions = [...functions].sort(
|
|
866
|
+
(a, b) => a.functionPath.localeCompare(b.functionPath)
|
|
867
|
+
);
|
|
868
|
+
const sortedCodecs = codecs ? [...codecs].sort(
|
|
869
|
+
(a, b) => `${a.sourceFile}|${a.exportName}`.localeCompare(`${b.sourceFile}|${b.exportName}`)
|
|
870
|
+
) : void 0;
|
|
871
|
+
const sortedModelCodecs = modelCodecs ? [...modelCodecs].sort(
|
|
872
|
+
(a, b) => `${a.modelSourceFile}|${a.modelExportName}|${a.schemaKey}|${a.accessPath}`.localeCompare(
|
|
873
|
+
`${b.modelSourceFile}|${b.modelExportName}|${b.schemaKey}|${b.accessPath}`
|
|
874
|
+
)
|
|
875
|
+
) : void 0;
|
|
876
|
+
const sortedFunctionCodecs = functionCodecs ? [...functionCodecs].sort(
|
|
877
|
+
(a, b) => `${a.functionSourceFile}|${a.functionExportName}|${a.schemaSource}|${a.accessPath}`.localeCompare(
|
|
878
|
+
`${b.functionSourceFile}|${b.functionExportName}|${b.schemaSource}|${b.accessPath}`
|
|
879
|
+
)
|
|
880
|
+
) : void 0;
|
|
881
|
+
models = sortedModels;
|
|
882
|
+
functions = sortedFunctions;
|
|
883
|
+
codecs = sortedCodecs;
|
|
884
|
+
modelCodecs = sortedModelCodecs;
|
|
885
|
+
functionCodecs = sortedFunctionCodecs;
|
|
818
886
|
const identityMap = /* @__PURE__ */ new Map();
|
|
819
887
|
const neededModelImports = /* @__PURE__ */ new Set();
|
|
820
888
|
for (const model of models) {
|
|
@@ -898,21 +966,74 @@ function generateApiFile(functions, models, codecs, modelCodecs, functionCodecs,
|
|
|
898
966
|
}
|
|
899
967
|
if (functionCodecs) {
|
|
900
968
|
const fingerprintMap = /* @__PURE__ */ new Map();
|
|
969
|
+
const brandMap = /* @__PURE__ */ new Map();
|
|
970
|
+
const codecSchemaToSourceFile = /* @__PURE__ */ new Map();
|
|
971
|
+
for (const c of codecs ?? []) {
|
|
972
|
+
codecSchemaToSourceFile.set(c.schema, c.sourceFile);
|
|
973
|
+
}
|
|
974
|
+
for (const mc of modelCodecs ?? []) {
|
|
975
|
+
codecSchemaToSourceFile.set(mc.codec, mc.modelSourceFile);
|
|
976
|
+
}
|
|
901
977
|
for (const [codecSchema, ref] of codecMap) {
|
|
978
|
+
const sourceFile = codecSchemaToSourceFile.get(codecSchema);
|
|
979
|
+
const brand = readCodecBrand(codecSchema);
|
|
980
|
+
const candidate = { ref, sourceFile, brand };
|
|
902
981
|
const fp = fingerprintCodec(codecSchema);
|
|
903
|
-
if (fp)
|
|
982
|
+
if (fp) {
|
|
983
|
+
const existing = fingerprintMap.get(fp);
|
|
984
|
+
if (existing) existing.push(candidate);
|
|
985
|
+
else fingerprintMap.set(fp, [candidate]);
|
|
986
|
+
}
|
|
987
|
+
if (brand) {
|
|
988
|
+
const existing = brandMap.get(brand);
|
|
989
|
+
if (existing) existing.push(candidate);
|
|
990
|
+
else brandMap.set(brand, [candidate]);
|
|
991
|
+
}
|
|
904
992
|
}
|
|
993
|
+
const undiscoverable = [];
|
|
905
994
|
for (const fc of functionCodecs) {
|
|
906
995
|
if (codecMap.has(fc.codec)) continue;
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
if (
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
`[zodvex] Warning: Codec in ${fc.functionExportName}() (${fc.accessPath}) has no matching model or exported codec. Export it standalone for full client-side codec support.`
|
|
996
|
+
const brand = readCodecBrand(fc.codec);
|
|
997
|
+
let candidates = brand ? brandMap.get(brand) : void 0;
|
|
998
|
+
if (!candidates || candidates.length === 0) {
|
|
999
|
+
const fp = fingerprintCodec(fc.codec);
|
|
1000
|
+
candidates = (fp ? fingerprintMap.get(fp) : void 0)?.filter(
|
|
1001
|
+
(c) => c.brand === void 0 || c.brand === brand
|
|
914
1002
|
);
|
|
915
1003
|
}
|
|
1004
|
+
if (!candidates || candidates.length === 0) {
|
|
1005
|
+
undiscoverable.push({ fn: fc.functionExportName, path: fc.accessPath });
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
let chosen;
|
|
1009
|
+
if (candidates.length === 1) {
|
|
1010
|
+
chosen = candidates[0].ref;
|
|
1011
|
+
} else {
|
|
1012
|
+
const sameFile = candidates.filter((c) => c.sourceFile === fc.functionSourceFile);
|
|
1013
|
+
if (sameFile.length === 1) {
|
|
1014
|
+
chosen = sameFile[0].ref;
|
|
1015
|
+
} else {
|
|
1016
|
+
const sorted = [...candidates].sort(
|
|
1017
|
+
(a, b) => `${a.sourceFile ?? ""}|${a.ref.exportName}`.localeCompare(
|
|
1018
|
+
`${b.sourceFile ?? ""}|${b.ref.exportName}`
|
|
1019
|
+
)
|
|
1020
|
+
);
|
|
1021
|
+
chosen = sorted[0].ref;
|
|
1022
|
+
if (!brand) {
|
|
1023
|
+
console.warn(
|
|
1024
|
+
`[zodvex] Note: Codec in ${fc.functionExportName}() (${fc.accessPath}) matches ${candidates.length} fingerprint-equivalent codecs (${candidates.map((c) => c.ref.exportName).join(", ")}). Referencing '${chosen.exportName}'. Export the codec standalone or give it a brand if you want the reference to be explicit.`
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
codecMap.set(fc.codec, chosen);
|
|
1030
|
+
}
|
|
1031
|
+
if (undiscoverable.length > 0) {
|
|
1032
|
+
const list = undiscoverable.map((u) => ` - ${u.fn}() at ${u.path}`).join("\n");
|
|
1033
|
+
throw new Error(
|
|
1034
|
+
`[zodvex] ${undiscoverable.length} codec(s) in function args/returns have no importable reference (not exported standalone, not embedded in a model). The generated client cannot encode or decode them, which silently breaks the codec boundary. Export each codec standalone (or add it to a model) so codegen can import it:
|
|
1035
|
+
${list}`
|
|
1036
|
+
);
|
|
916
1037
|
}
|
|
917
1038
|
}
|
|
918
1039
|
const zodToSourceCtx = {
|
|
@@ -971,15 +1092,17 @@ function generateApiFile(functions, models, codecs, modelCodecs, functionCodecs,
|
|
|
971
1092
|
if (coreImports.length > 0) {
|
|
972
1093
|
imports.push(`import { ${coreImports.join(", ")} } from '${zodvexImport}'`);
|
|
973
1094
|
}
|
|
974
|
-
for (const exportName of neededModelImports) {
|
|
1095
|
+
for (const exportName of [...neededModelImports].sort()) {
|
|
975
1096
|
const model = models.find((m) => m.exportName === exportName);
|
|
976
1097
|
if (model) {
|
|
977
1098
|
const importPath = `../${model.sourceFile.replace(/\.ts$/, ".js")}`;
|
|
978
1099
|
imports.push(`import { ${exportName} } from '${importPath}'`);
|
|
979
1100
|
}
|
|
980
1101
|
}
|
|
981
|
-
|
|
1102
|
+
const sortedCodecImportPaths = [...zodToSourceCtx.neededCodecImports.keys()].sort();
|
|
1103
|
+
for (const importPath of sortedCodecImportPaths) {
|
|
982
1104
|
if (importPath === MODEL_CODEC_SENTINEL) continue;
|
|
1105
|
+
const exportNames = zodToSourceCtx.neededCodecImports.get(importPath) ?? /* @__PURE__ */ new Set();
|
|
983
1106
|
const names = Array.from(exportNames).sort().join(", ");
|
|
984
1107
|
imports.push(`import { ${names} } from '${importPath}'`);
|
|
985
1108
|
}
|