zodvex 0.3.2 → 0.4.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.
Files changed (62) hide show
  1. package/dist/__type-tests__/infer-returns.d.ts +2 -0
  2. package/dist/__type-tests__/infer-returns.d.ts.map +1 -0
  3. package/dist/__type-tests__/zodTable-inference.d.ts +2 -0
  4. package/dist/__type-tests__/zodTable-inference.d.ts.map +1 -0
  5. package/dist/builders.d.ts +173 -0
  6. package/dist/builders.d.ts.map +1 -0
  7. package/dist/codec.d.ts +11 -0
  8. package/dist/codec.d.ts.map +1 -0
  9. package/dist/custom.d.ts +142 -0
  10. package/dist/custom.d.ts.map +1 -0
  11. package/dist/ids.d.ts +23 -0
  12. package/dist/ids.d.ts.map +1 -0
  13. package/dist/index.d.ts +10 -599
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +27 -11
  16. package/dist/index.js.map +1 -1
  17. package/dist/mapping/core.d.ts +5 -0
  18. package/dist/mapping/core.d.ts.map +1 -0
  19. package/dist/mapping/handlers/enum.d.ts +4 -0
  20. package/dist/mapping/handlers/enum.d.ts.map +1 -0
  21. package/dist/mapping/handlers/index.d.ts +5 -0
  22. package/dist/mapping/handlers/index.d.ts.map +1 -0
  23. package/dist/mapping/handlers/nullable.d.ts +7 -0
  24. package/dist/mapping/handlers/nullable.d.ts.map +1 -0
  25. package/dist/mapping/handlers/record.d.ts +4 -0
  26. package/dist/mapping/handlers/record.d.ts.map +1 -0
  27. package/dist/mapping/handlers/union.d.ts +5 -0
  28. package/dist/mapping/handlers/union.d.ts.map +1 -0
  29. package/dist/mapping/index.d.ts +4 -0
  30. package/dist/mapping/index.d.ts.map +1 -0
  31. package/dist/mapping/types.d.ts +43 -0
  32. package/dist/mapping/types.d.ts.map +1 -0
  33. package/dist/mapping/utils.d.ts +6 -0
  34. package/dist/mapping/utils.d.ts.map +1 -0
  35. package/dist/registry.d.ts +110 -0
  36. package/dist/registry.d.ts.map +1 -0
  37. package/dist/tables.d.ts +122 -0
  38. package/dist/tables.d.ts.map +1 -0
  39. package/dist/transform/index.d.ts +26 -0
  40. package/dist/transform/index.d.ts.map +1 -0
  41. package/dist/transform/index.js +442 -0
  42. package/dist/transform/index.js.map +1 -0
  43. package/dist/transform/transform.d.ts +47 -0
  44. package/dist/transform/transform.d.ts.map +1 -0
  45. package/dist/transform/traverse.d.ts +62 -0
  46. package/dist/transform/traverse.d.ts.map +1 -0
  47. package/dist/transform/types.d.ts +115 -0
  48. package/dist/transform/types.d.ts.map +1 -0
  49. package/dist/types.d.ts +29 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/utils.d.ts +39 -0
  52. package/dist/utils.d.ts.map +1 -0
  53. package/dist/wrappers.d.ts +22 -0
  54. package/dist/wrappers.d.ts.map +1 -0
  55. package/package.json +13 -6
  56. package/src/__type-tests__/infer-returns.ts +24 -0
  57. package/src/custom.ts +170 -15
  58. package/src/transform/index.ts +38 -0
  59. package/src/transform/transform.ts +409 -0
  60. package/src/transform/traverse.ts +320 -0
  61. package/src/transform/types.ts +128 -0
  62. package/src/types.ts +3 -2
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Transform layer type definitions.
3
+ *
4
+ * General-purpose types for schema traversal and value transformation.
5
+ */
6
+ import type { z } from 'zod';
7
+ /**
8
+ * Information about a field during schema traversal.
9
+ */
10
+ export type FieldInfo = {
11
+ /** Dot-notation path (e.g., 'profile.email', 'contacts[].email') */
12
+ path: string;
13
+ /** The Zod schema for this field */
14
+ schema: z.ZodTypeAny;
15
+ /** Metadata from schema.meta() if present */
16
+ meta: Record<string, unknown> | undefined;
17
+ /** Whether the field is wrapped in optional/nullable */
18
+ isOptional: boolean;
19
+ };
20
+ /**
21
+ * Visitor functions for walkSchema().
22
+ */
23
+ export type SchemaVisitor = {
24
+ /** Called for every field. Return 'skip' to skip children. */
25
+ onField?: (info: FieldInfo) => void | 'skip';
26
+ /** Called when entering an object schema */
27
+ onObject?: (info: FieldInfo) => void;
28
+ /** Called when entering an array schema */
29
+ onArray?: (info: FieldInfo) => void;
30
+ /** Called when entering a union schema */
31
+ onUnion?: (info: FieldInfo, variants: z.ZodTypeAny[]) => void;
32
+ };
33
+ /**
34
+ * Options for walkSchema().
35
+ */
36
+ export type WalkSchemaOptions = {
37
+ /** Starting path prefix */
38
+ path?: string;
39
+ };
40
+ /**
41
+ * Context passed to transform functions.
42
+ */
43
+ export type TransformContext<TCtx = unknown> = {
44
+ /** Current field path */
45
+ path: string;
46
+ /** The Zod schema for this field */
47
+ schema: z.ZodTypeAny;
48
+ /** Metadata from schema.meta() if present */
49
+ meta: Record<string, unknown> | undefined;
50
+ /** User-provided context */
51
+ ctx: TCtx;
52
+ };
53
+ /**
54
+ * Synchronous transform function signature.
55
+ */
56
+ export type TransformFn<TCtx = unknown> = (value: unknown, context: TransformContext<TCtx>) => unknown;
57
+ /**
58
+ * Async transform function signature.
59
+ */
60
+ export type AsyncTransformFn<TCtx = unknown> = (value: unknown, context: TransformContext<TCtx>) => unknown | Promise<unknown>;
61
+ /**
62
+ * Options for transformBySchema().
63
+ */
64
+ export type TransformOptions = {
65
+ /** Starting path prefix */
66
+ path?: string;
67
+ /**
68
+ * How to handle values that don't match any union variant.
69
+ * - 'passthrough': Return value unchanged (default)
70
+ * - 'error': Throw an error
71
+ * - 'null': Replace with null (fail-closed for security)
72
+ */
73
+ unmatchedUnion?: 'passthrough' | 'error' | 'null';
74
+ /** Callback when a union doesn't match */
75
+ onUnmatchedUnion?: (path: string) => void;
76
+ /**
77
+ * Fast predicate to check if a schema needs transformation.
78
+ *
79
+ * When provided, this predicate is called before the transform callback.
80
+ * If it returns false, the transform callback is skipped for this schema
81
+ * (but recursion into children continues).
82
+ *
83
+ * This optimization avoids callback overhead for schemas that don't need
84
+ * transformation, which is useful when only a small subset of fields
85
+ * require processing (e.g., only sensitive fields).
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * // Only call transform for schemas with sensitive metadata
90
+ * transformBySchema(value, schema, ctx, transform, {
91
+ * shouldTransform: (sch) => getSensitiveMetadata(sch) !== undefined
92
+ * })
93
+ * ```
94
+ */
95
+ shouldTransform?: (schema: z.ZodTypeAny) => boolean;
96
+ /**
97
+ * Process array elements in parallel (async only).
98
+ *
99
+ * When true, array elements are processed with Promise.all() instead of
100
+ * sequentially. This can significantly improve performance for large arrays
101
+ * when transforms involve async operations like entitlement checks.
102
+ *
103
+ * Default: false (sequential processing for backwards compatibility)
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * // Process user entitlements for all items in parallel
108
+ * const result = await transformBySchemaAsync(docs, schema, ctx, transform, {
109
+ * parallel: true
110
+ * })
111
+ * ```
112
+ */
113
+ parallel?: boolean;
114
+ };
115
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transform/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAA;IACZ,oCAAoC;IACpC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAA;IACpB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACzC,wDAAwD;IACxD,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,CAAA;IAC5C,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACpC,2CAA2C;IAC3C,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,UAAU,EAAE,KAAK,IAAI,CAAA;CAC9D,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,IAAI,GAAG,OAAO,IAAI;IAC7C,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,oCAAoC;IACpC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAA;IACpB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IACzC,4BAA4B;IAC5B,GAAG,EAAE,IAAI,CAAA;CACV,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,IAAI,GAAG,OAAO,IAAI,CACxC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,KAC5B,OAAO,CAAA;AAEZ;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,IAAI,GAAG,OAAO,IAAI,CAC7C,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,KAC5B,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAE/B;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,cAAc,CAAC,EAAE,aAAa,GAAG,OAAO,GAAG,MAAM,CAAA;IACjD,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC;;;;;;;;;;;;;;;;;;OAkBG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,KAAK,OAAO,CAAA;IACnD;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { type DefaultFunctionArgs, type RegisteredAction, type RegisteredMutation, type RegisteredQuery } from 'convex/server';
2
+ import { z } from 'zod';
3
+ export type InferArgs<A> = A extends z.ZodObject<infer S> ? z.infer<z.ZodObject<S>> : A extends Record<string, z.ZodTypeAny> ? {
4
+ [K in keyof A]: z.infer<A[K]>;
5
+ } : A extends z.ZodTypeAny ? z.infer<A> : Record<string, never>;
6
+ export type InferReturns<R> = R extends z.ZodType<any, any, any> ? z.output<R> : R extends undefined ? any : R;
7
+ export type InferHandlerReturns<R> = R extends z.ZodType<any, any, any> ? z.output<R> : any;
8
+ /**
9
+ * Extract the visibility type from a Convex builder function
10
+ */
11
+ export type ExtractVisibility<Builder extends (...args: any) => any> = ReturnType<Builder> extends RegisteredQuery<infer V, any, any> ? V : ReturnType<Builder> extends RegisteredMutation<infer V, any, any> ? V : ReturnType<Builder> extends RegisteredAction<infer V, any, any> ? V : 'public';
12
+ /**
13
+ * @deprecated Use GenericQueryCtx, GenericMutationCtx, or GenericActionCtx directly instead
14
+ */
15
+ export type ExtractCtx<Builder> = Builder extends {
16
+ (fn: {
17
+ handler: (ctx: infer Ctx, ...args: any[]) => any;
18
+ }): any;
19
+ } ? Ctx : never;
20
+ /**
21
+ * @deprecated Return types are now specified explicitly using RegisteredQuery, RegisteredMutation, or RegisteredAction
22
+ */
23
+ export type PreserveReturnType<Builder extends (...args: any) => any, ArgsType, ReturnsType> = ReturnType<Builder> extends RegisteredQuery<infer V, any, any> ? RegisteredQuery<V, ArgsType extends DefaultFunctionArgs ? ArgsType : DefaultFunctionArgs, Promise<ReturnsType>> : ReturnType<Builder> extends RegisteredMutation<infer V, any, any> ? RegisteredMutation<V, ArgsType extends DefaultFunctionArgs ? ArgsType : DefaultFunctionArgs, Promise<ReturnsType>> : ReturnType<Builder> extends RegisteredAction<infer V, any, any> ? RegisteredAction<V, ArgsType extends DefaultFunctionArgs ? ArgsType : DefaultFunctionArgs, Promise<ReturnsType>> : ReturnType<Builder>;
24
+ export type ZodToConvexArgs<A> = A extends z.ZodObject<any> ? z.infer<A> : A extends Record<string, z.ZodTypeAny> ? {
25
+ [K in keyof A]: z.infer<A[K]>;
26
+ } : A extends z.ZodTypeAny ? {
27
+ value: z.infer<A>;
28
+ } : Record<string, never>;
29
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { z } from 'zod';
2
+ export declare function pick<T extends Record<string, any>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>;
3
+ export declare function returnsAs<R extends z.ZodTypeAny>(): <T extends z.input<R>>(v: T) => T;
4
+ export declare function formatZodIssues(error: z.ZodError, context?: 'args' | 'returns' | 'input' | 'output' | 'codec'): {
5
+ error: string;
6
+ context: "output" | "input" | "args" | "returns" | "codec" | undefined;
7
+ issues: {
8
+ path: string;
9
+ code: "custom" | "invalid_type" | "unrecognized_keys" | "invalid_union" | "invalid_value" | "invalid_key" | "too_big" | "too_small" | "invalid_format" | "not_multiple_of" | "invalid_element";
10
+ message: string;
11
+ }[];
12
+ flatten: any;
13
+ };
14
+ export declare function handleZodValidationError(e: unknown, context: 'args' | 'returns' | 'input' | 'output' | 'codec'): never;
15
+ export declare function zPaginated<T extends z.ZodTypeAny>(item: T): z.ZodObject<{
16
+ page: z.ZodArray<T>;
17
+ isDone: z.ZodBoolean;
18
+ continueCursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
19
+ }, z.core.$strip>;
20
+ /**
21
+ * Maps Date fields to number fields for docSchema generation.
22
+ * Handles Date, Date.optional(), Date.nullable(), and Date.default() cases.
23
+ * Returns the original field for non-Date types.
24
+ */
25
+ export declare function mapDateFieldToNumber(field: z.ZodTypeAny): z.ZodTypeAny;
26
+ type Mask = readonly string[] | Record<string, boolean | 1 | true>;
27
+ /**
28
+ * Returns a plain shape object containing only the selected fields.
29
+ * Accepts either a ZodObject or a raw shape object.
30
+ */
31
+ export declare function pickShape(schemaOrShape: z.ZodObject<any> | Record<string, any>, mask: Mask): Record<string, any>;
32
+ export declare function safePick(schema: z.ZodObject<any>, mask: Mask): z.ZodObject<any>;
33
+ /**
34
+ * Convenience: omit a set of keys by building the complement.
35
+ * Avoids using Zod's .omit() which can cause type depth issues.
36
+ */
37
+ export declare function safeOmit(schema: z.ZodObject<any>, mask: Mask): z.ZodObject<any>;
38
+ export {};
39
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EACnE,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,CAAC,EAAE,GACR,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAMZ;AAGD,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,MACtC,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,OACnC;AAGD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,CAAC,CAAC,QAAQ,EACjB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO;;;;;;;;;EAa5D;AAID,wBAAgB,wBAAwB,CACtC,CAAC,EAAE,OAAO,EACV,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GACzD,KAAK,CAKP;AAGD,wBAAgB,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;;;;kBAMzD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CA0BtE;AAGD,KAAK,IAAI,GAAG,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;AAOlE;;;GAGG;AACH,wBAAgB,SAAS,CACvB,aAAa,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,IAAI,EAAE,IAAI,GACT,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAUrB;AAGD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAE/E;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAM/E"}
@@ -0,0 +1,22 @@
1
+ import type { FunctionVisibility, RegisteredAction, RegisteredMutation, RegisteredQuery } from 'convex/server';
2
+ import { z } from 'zod';
3
+ import type { ExtractCtx, ExtractVisibility, InferHandlerReturns, InferReturns, ZodToConvexArgs } from './types';
4
+ export declare function zQuery<Builder extends (fn: any) => any, A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>, R extends z.ZodTypeAny | undefined = undefined, Visibility extends FunctionVisibility = ExtractVisibility<Builder>>(query: Builder, input: A, handler: (ctx: ExtractCtx<Builder>, args: ZodToConvexArgs<A>) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>, options?: {
5
+ returns?: R;
6
+ }): RegisteredQuery<Visibility, ZodToConvexArgs<A>, Promise<InferReturns<R>>>;
7
+ export declare function zInternalQuery<Builder extends (fn: any) => any, A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>, R extends z.ZodTypeAny | undefined = undefined, Visibility extends FunctionVisibility = ExtractVisibility<Builder>>(internalQuery: Builder, input: A, handler: (ctx: ExtractCtx<Builder>, args: ZodToConvexArgs<A>) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>, options?: {
8
+ returns?: R;
9
+ }): RegisteredQuery<Visibility, ZodToConvexArgs<A>, Promise<InferReturns<R>>>;
10
+ export declare function zMutation<Builder extends (fn: any) => any, A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>, R extends z.ZodTypeAny | undefined = undefined, Visibility extends FunctionVisibility = ExtractVisibility<Builder>>(mutation: Builder, input: A, handler: (ctx: ExtractCtx<Builder>, args: ZodToConvexArgs<A>) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>, options?: {
11
+ returns?: R;
12
+ }): RegisteredMutation<Visibility, ZodToConvexArgs<A>, Promise<InferReturns<R>>>;
13
+ export declare function zInternalMutation<Builder extends (fn: any) => any, A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>, R extends z.ZodTypeAny | undefined = undefined, Visibility extends FunctionVisibility = ExtractVisibility<Builder>>(internalMutation: Builder, input: A, handler: (ctx: ExtractCtx<Builder>, args: ZodToConvexArgs<A>) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>, options?: {
14
+ returns?: R;
15
+ }): RegisteredMutation<Visibility, ZodToConvexArgs<A>, Promise<InferReturns<R>>>;
16
+ export declare function zAction<Builder extends (fn: any) => any, A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>, R extends z.ZodTypeAny | undefined = undefined, Visibility extends FunctionVisibility = ExtractVisibility<Builder>>(action: Builder, input: A, handler: (ctx: ExtractCtx<Builder>, args: ZodToConvexArgs<A>) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>, options?: {
17
+ returns?: R;
18
+ }): RegisteredAction<Visibility, ZodToConvexArgs<A>, Promise<InferReturns<R>>>;
19
+ export declare function zInternalAction<Builder extends (fn: any) => any, A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>, R extends z.ZodTypeAny | undefined = undefined, Visibility extends FunctionVisibility = ExtractVisibility<Builder>>(internalAction: Builder, input: A, handler: (ctx: ExtractCtx<Builder>, args: ZodToConvexArgs<A>) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>, options?: {
20
+ returns?: R;
21
+ }): RegisteredAction<Visibility, ZodToConvexArgs<A>, Promise<InferReturns<R>>>;
22
+ //# sourceMappingURL=wrappers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrappers.d.ts","sourceRoot":"","sources":["../src/wrappers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zodvex",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Zod <=> Convex integration, supporting Zod 4",
5
5
  "keywords": ["zod", "convex", "validators", "codec", "mapping", "schema", "validation"],
6
6
  "homepage": "https://github.com/panzacoder/zodvex#readme",
@@ -21,18 +21,23 @@
21
21
  "types": "./dist/index.d.ts",
22
22
  "import": "./dist/index.js",
23
23
  "default": "./dist/index.js"
24
+ },
25
+ "./transform": {
26
+ "types": "./dist/transform/index.d.ts",
27
+ "import": "./dist/transform/index.js",
28
+ "default": "./dist/transform/index.js"
24
29
  }
25
30
  },
26
31
  "files": ["dist", "src", "README.md", "LICENSE"],
27
32
  "scripts": {
28
- "build": "tsup",
33
+ "build": "tsup && tsc --emitDeclarationOnly",
29
34
  "dev": "bun run tsup --watch",
30
35
  "type-check": "bun run tsc --noEmit",
31
36
  "test": "bun test",
32
37
  "lint": "bun x biome check src __tests__",
33
38
  "lint:fix": "bun x biome check --write src __tests__",
34
39
  "format": "bun x biome format --write src __tests__",
35
- "prepare": "tsup",
40
+ "prepare": "tsup && tsc --emitDeclarationOnly",
36
41
  "prepublishOnly": "bun test && bun run type-check"
37
42
  },
38
43
  "peerDependencies": {
@@ -40,13 +45,15 @@
40
45
  "convex-helpers": "^0.1.104",
41
46
  "zod": "^4.1.12"
42
47
  },
48
+ "dependencies": {
49
+ "tsup": "^8.5.0",
50
+ "typescript": "^5.9.3"
51
+ },
43
52
  "devDependencies": {
44
53
  "@biomejs/biome": "^2.2.6",
45
54
  "@types/bun": "^1.3.0",
46
55
  "@types/node": "^24.8.0",
47
- "ai": "^6.0.6",
48
- "tsup": "^8.5.0",
49
- "typescript": "^5.9.3"
56
+ "ai": "^6.0.6"
50
57
  },
51
58
  "engines": {
52
59
  "node": ">=20",
@@ -152,3 +152,27 @@ const treeNodeSchema = z.object({
152
152
  })
153
153
  declare const treeNodeResult: InferReturns<typeof treeNodeSchema>
154
154
  expectNotAny(treeNodeResult)
155
+
156
+ // --- Codec/Transform schemas: handler returns z.output (internal type), not z.input (wire type) ---
157
+ // This tests the fix for Zod 4.1 codec support where input !== output
158
+
159
+ // Simulate a pipe/transform schema where input !== output
160
+ // z.input = string (wire format), z.output = Date (internal representation)
161
+ declare const codecSchema: z.ZodPipe<z.ZodString, z.ZodDate>
162
+ declare const codecResult: InferReturns<typeof codecSchema>
163
+ expectNotAny(codecResult)
164
+ // Handler should return Date (z.output), not string (z.input)
165
+ // @ts-expect-error - If this errors with "unused directive", Result is string (the bug)
166
+ const _codecInvalid: typeof codecResult = 'not a date'
167
+
168
+ // Test with transform (coerce pattern)
169
+ const coerceNumberSchema = z.coerce.number()
170
+ declare const coerceResult: InferReturns<typeof coerceNumberSchema>
171
+ expectNotAny(coerceResult)
172
+ // @ts-expect-error - Result should be number, not string/any
173
+ const _coerceInvalid: typeof coerceResult = 'not a number'
174
+
175
+ // Test with preprocess (another transform pattern)
176
+ const preprocessedSchema = z.preprocess(val => String(val), z.string())
177
+ declare const preprocessResult: InferReturns<typeof preprocessedSchema>
178
+ expectNotAny(preprocessResult)
package/src/custom.ts CHANGED
@@ -18,6 +18,127 @@ import { type ZodValidator, zodToConvex, zodToConvexFields } from './mapping'
18
18
  import type { ExtractCtx, ExtractVisibility } from './types'
19
19
  import { handleZodValidationError, pick } from './utils'
20
20
 
21
+ /**
22
+ * Hooks for observing the function execution (side effects, no return value).
23
+ */
24
+ export type CustomizationHooks = {
25
+ /** Called after successful execution with access to ctx, args, and result */
26
+ onSuccess?: (info: {
27
+ ctx: unknown
28
+ args: Record<string, unknown>
29
+ result: unknown
30
+ }) => void | Promise<void>
31
+ }
32
+
33
+ /**
34
+ * Transforms for modifying data in the function flow.
35
+ */
36
+ export type CustomizationTransforms = {
37
+ /** Transform the output after validation but before wire encoding */
38
+ output?: (result: unknown, schema: z.ZodTypeAny) => unknown | Promise<unknown>
39
+ }
40
+
41
+ /**
42
+ * Result returned from a customization input function.
43
+ * Separates Convex concepts (ctx, args) from hooks (side effects) and transforms (data modifications).
44
+ */
45
+ export type CustomizationResult<
46
+ CustomCtx extends Record<string, any> = Record<string, any>,
47
+ CustomArgs extends Record<string, any> = Record<string, any>
48
+ > = {
49
+ /** Custom context to merge with base context */
50
+ ctx?: CustomCtx
51
+ /** Custom args to merge with parsed args */
52
+ args?: CustomArgs
53
+ /** Hooks for observing execution (side effects) */
54
+ hooks?: CustomizationHooks
55
+ /** Transforms for modifying the data flow */
56
+ transforms?: CustomizationTransforms
57
+ }
58
+
59
+ /**
60
+ * Extended input result that includes hooks and transforms.
61
+ * This is what the input function returns internally.
62
+ */
63
+ export type CustomizationInputResult<
64
+ OutCtx extends Record<string, any>,
65
+ OutArgs extends Record<string, any>
66
+ > = {
67
+ ctx: OutCtx
68
+ args: OutArgs
69
+ hooks?: CustomizationHooks
70
+ transforms?: CustomizationTransforms
71
+ }
72
+
73
+ /**
74
+ * Customization type that supports hooks and transforms.
75
+ * This extends convex-helpers' Customization pattern to include
76
+ * hooks (side effects) and transforms (data modifications).
77
+ *
78
+ * Use customCtxWithHooks() to create instances of this type.
79
+ */
80
+ export type CustomizationWithHooks<
81
+ InCtx extends Record<string, any>,
82
+ OutCtx extends Record<string, any> = Record<string, any>,
83
+ OutArgs extends Record<string, any> = Record<string, any>,
84
+ ExtraArgs extends Record<string, any> = Record<string, any>
85
+ > = {
86
+ args: Record<string, never>
87
+ input: (
88
+ ctx: InCtx,
89
+ args?: Record<string, unknown>,
90
+ extra?: ExtraArgs
91
+ ) =>
92
+ | Promise<CustomizationInputResult<OutCtx, OutArgs>>
93
+ | CustomizationInputResult<OutCtx, OutArgs>
94
+ }
95
+
96
+ /**
97
+ * A helper for defining a Customization with full support for hooks and transforms.
98
+ * Use this instead of customCtx when you need onSuccess, transforms.output, etc.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * const secureQuery = zCustomQueryBuilder(
103
+ * query,
104
+ * customCtxWithHooks(async (ctx: QueryCtx) => {
105
+ * const securityCtx = await getSecurityContext(ctx)
106
+ * return {
107
+ * ctx: { securityCtx },
108
+ * hooks: {
109
+ * onSuccess: ({ result }) => console.log('Query returned:', result),
110
+ * },
111
+ * transforms: {
112
+ * output: (result, schema) => transformSensitiveFields(result, securityCtx),
113
+ * },
114
+ * }
115
+ * })
116
+ * )
117
+ * ```
118
+ */
119
+ export function customCtxWithHooks<
120
+ InCtx extends Record<string, any>,
121
+ OutCtx extends Record<string, any> = Record<string, any>,
122
+ OutArgs extends Record<string, any> = Record<string, any>
123
+ >(
124
+ fn: (
125
+ ctx: InCtx
126
+ ) => Promise<CustomizationResult<OutCtx, OutArgs>> | CustomizationResult<OutCtx, OutArgs>
127
+ ): CustomizationWithHooks<InCtx, OutCtx, OutArgs> {
128
+ return {
129
+ args: {},
130
+ input: async (ctx: InCtx): Promise<CustomizationInputResult<OutCtx, OutArgs>> => {
131
+ const result = await fn(ctx)
132
+ return {
133
+ ctx: result.ctx ?? ({} as OutCtx),
134
+ args: result.args ?? ({} as OutArgs),
135
+ hooks: result.hooks,
136
+ transforms: result.transforms
137
+ }
138
+ }
139
+ }
140
+ }
141
+
21
142
  // Type helpers for args transformation (from zodV3 example)
22
143
  type OneArgArray<ArgsObject extends DefaultFunctionArgs = DefaultFunctionArgs> = [ArgsObject]
23
144
 
@@ -26,12 +147,14 @@ type NullToUndefinedOrNull<T> = T extends null ? T | undefined | void : T
26
147
  type Returns<T> = Promise<NullToUndefinedOrNull<T>> | NullToUndefinedOrNull<T>
27
148
 
28
149
  // The return value before it's been validated: returned by the handler
150
+ // Uses z.output since the handler produces the internal representation (e.g., Date),
151
+ // which is then encoded to wire format (e.g., string) before sending to the client
29
152
  type ReturnValueInput<ReturnsValidator extends z.ZodTypeAny | ZodValidator | void> = [
30
153
  ReturnsValidator
31
154
  ] extends [z.ZodTypeAny]
32
- ? Returns<z.input<ReturnsValidator>>
155
+ ? Returns<z.output<ReturnsValidator>>
33
156
  : [ReturnsValidator] extends [ZodValidator]
34
- ? Returns<z.input<z.ZodObject<ReturnsValidator>>>
157
+ ? Returns<z.output<z.ZodObject<ReturnsValidator>>>
35
158
  : any
36
159
 
37
160
  // The return value after it's been validated: returned to the client
@@ -171,7 +294,9 @@ export function customFnBuilder<
171
294
  ExtraArgs extends Record<string, any> = Record<string, any>
172
295
  >(
173
296
  builder: Builder,
174
- customization: Customization<Ctx, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
297
+ customization:
298
+ | Customization<Ctx, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
299
+ | CustomizationWithHooks<Ctx, CustomCtx, CustomMadeArgs, ExtraArgs>
175
300
  ) {
176
301
  const customInput = customization.input ?? NoOp.input
177
302
  const inputArgs = customization.args ?? NoOp.args
@@ -213,6 +338,13 @@ export function customFnBuilder<
213
338
  args: convexArgs,
214
339
  ...returnValidator,
215
340
  handler: async (ctx: Ctx, allArgs: any) => {
341
+ // Cast justification: customInput expects ObjectType<CustomArgsValidator>, but pick()
342
+ // returns Partial<T>. The cast is safe because inputArgs keys are derived from
343
+ // CustomArgsValidator at the type level. The 'added' result is typed as 'any' because
344
+ // it may include hooks/transforms from CustomizationWithHooks which aren't in the
345
+ // convex-helpers Customization type.
346
+ // TODO: Create a type-safe pickArgs<T>() helper that preserves the ObjectType<T>
347
+ // return type when the keys are statically known from the validator.
216
348
  const added: any = await customInput(
217
349
  ctx,
218
350
  pick(allArgs, Object.keys(inputArgs)) as any,
@@ -239,17 +371,21 @@ export function customFnBuilder<
239
371
  } catch (e) {
240
372
  handleZodValidationError(e, 'returns')
241
373
  }
242
- if (added?.onSuccess) {
243
- await added.onSuccess({
374
+ if (added?.hooks?.onSuccess) {
375
+ await added.hooks.onSuccess({
244
376
  ctx,
245
377
  args: parsed.data,
246
378
  result: validated
247
379
  })
248
380
  }
249
- return toConvexJS(returns as z.ZodTypeAny, validated)
381
+ // Apply output transform if provided
382
+ const transformed = added?.transforms?.output
383
+ ? await added.transforms.output(validated, returns as z.ZodTypeAny)
384
+ : validated
385
+ return toConvexJS(returns as z.ZodTypeAny, transformed)
250
386
  }
251
- if (added?.onSuccess) {
252
- await added.onSuccess({ ctx, args: parsed.data, result: ret })
387
+ if (added?.hooks?.onSuccess) {
388
+ await added.hooks.onSuccess({ ctx, args: parsed.data, result: ret })
253
389
  }
254
390
  return ret
255
391
  }
@@ -259,6 +395,9 @@ export function customFnBuilder<
259
395
  args: inputArgs,
260
396
  ...returnValidator,
261
397
  handler: async (ctx: Ctx, allArgs: any) => {
398
+ // Cast justification: Same as above - customInput expects ObjectType<CustomArgsValidator>
399
+ // but pick() returns Partial<T>. Safe because inputArgs keys match CustomArgsValidator.
400
+ // TODO: Create a type-safe pickArgs<T>() helper (see comment in with-args path above).
262
401
  const added: any = await customInput(
263
402
  ctx,
264
403
  pick(allArgs, Object.keys(inputArgs)) as any,
@@ -277,13 +416,17 @@ export function customFnBuilder<
277
416
  } catch (e) {
278
417
  handleZodValidationError(e, 'returns')
279
418
  }
280
- if (added?.onSuccess) {
281
- await added.onSuccess({ ctx, args: allArgs, result: validated })
419
+ if (added?.hooks?.onSuccess) {
420
+ await added.hooks.onSuccess({ ctx, args: allArgs, result: validated })
282
421
  }
283
- return toConvexJS(returns as z.ZodTypeAny, validated)
422
+ // Apply output transform if provided
423
+ const transformed = added?.transforms?.output
424
+ ? await added.transforms.output(validated, returns as z.ZodTypeAny)
425
+ : validated
426
+ return toConvexJS(returns as z.ZodTypeAny, transformed)
284
427
  }
285
- if (added?.onSuccess) {
286
- await added.onSuccess({ ctx, args: allArgs, result: ret })
428
+ if (added?.hooks?.onSuccess) {
429
+ await added.hooks.onSuccess({ ctx, args: allArgs, result: ret })
287
430
  }
288
431
  return ret
289
432
  }
@@ -343,8 +486,16 @@ export function zCustomQuery<
343
486
  query: QueryBuilder<any, Visibility>,
344
487
  customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
345
488
  ) {
346
- // Implementation deliberately uses 'any' ctx to preserve overload behavior
347
- // while avoiding a GenericDataModel constraint at the implementation level.
489
+ // Cast justification: This is the TypeScript overload implementation pattern. The function
490
+ // has two overloads (with/without DataModel constraint) that provide precise types to callers.
491
+ // The implementation must satisfy both overloads, which requires a broader signature.
492
+ // The 'as any' casts allow the implementation to delegate to customFnBuilder without
493
+ // TypeScript complaining about the generic parameter differences between overloads.
494
+ // This is type-safe because: (1) callers only see the overload signatures which are strict,
495
+ // (2) the runtime behavior is identical regardless of which overload matched.
496
+ // TODO: Consider using a conditional type or branded types to create a single signature
497
+ // that satisfies both overloads without casts. Alternatively, accept this as idiomatic
498
+ // TypeScript for overloaded functions and keep the casts.
348
499
  return customFnBuilder<
349
500
  any,
350
501
  typeof query,
@@ -388,6 +539,8 @@ export function zCustomMutation<
388
539
  mutation: Builder,
389
540
  customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
390
541
  ) {
542
+ // Cast justification: Same overload implementation pattern as zCustomQuery.
543
+ // See detailed comment there. Type safety is enforced by the overload signature above.
391
544
  return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
392
545
  mutation as any,
393
546
  customization as any
@@ -427,6 +580,8 @@ export function zCustomAction<
427
580
  action: Builder,
428
581
  customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
429
582
  ) {
583
+ // Cast justification: Same overload implementation pattern as zCustomQuery.
584
+ // See detailed comment there. Type safety is enforced by the overload signature above.
430
585
  return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
431
586
  action as any,
432
587
  customization as any
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Transform layer - General-purpose schema traversal and value transformation utilities.
3
+ *
4
+ * This module provides primitives for:
5
+ * - Walking Zod schemas (walkSchema, findFieldsWithMeta)
6
+ * - Extracting metadata from schemas (getMetadata, hasMetadata)
7
+ * - Recursively transforming values based on schema structure (transformBySchema, transformBySchemaAsync)
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { findFieldsWithMeta, transformBySchema } from 'zodvex/transform'
12
+ *
13
+ * // Find all fields with custom metadata
14
+ * const sensitiveFields = findFieldsWithMeta(schema, meta => meta?.sensitive === true)
15
+ *
16
+ * // Transform values based on metadata
17
+ * const masked = transformBySchema(value, schema, ctx, (val, info) => {
18
+ * if (info.meta?.pii) return '[REDACTED]'
19
+ * return val
20
+ * })
21
+ * ```
22
+ */
23
+
24
+ // Transformation
25
+ export { transformBySchema, transformBySchemaAsync } from './transform'
26
+
27
+ // Traversal
28
+ export { findFieldsWithMeta, getMetadata, hasMetadata, walkSchema } from './traverse'
29
+ // Types
30
+ export type {
31
+ AsyncTransformFn,
32
+ FieldInfo,
33
+ SchemaVisitor,
34
+ TransformContext,
35
+ TransformFn,
36
+ TransformOptions,
37
+ WalkSchemaOptions
38
+ } from './types'