fluent-convex 0.2.0 → 0.4.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/builder.d.ts +13 -12
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +6 -4
- package/dist/builder.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +1 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/zod_support.d.ts +8 -1
- package/dist/zod_support.d.ts.map +1 -1
- package/dist/zod_support.js.map +1 -1
- package/package.json +1 -2
- package/src/builder.test-d.ts +172 -1
- package/src/builder.ts +51 -15
- package/src/index.ts +1 -1
- package/src/middleware.ts +2 -3
- package/src/types.ts +8 -3
- package/src/zod_support.ts +42 -1
- package/README.md +0 -147
package/dist/builder.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type RegisteredQuery, type RegisteredMutation, type RegisteredAction } from "convex/server";
|
|
1
|
+
import { type RegisteredQuery, type RegisteredMutation, type RegisteredAction, type GenericDataModel, type DataModelFromSchemaDefinition, type SchemaDefinition } from "convex/server";
|
|
2
2
|
import type { ConvexMiddleware, AnyConvexMiddleware } from "./middleware";
|
|
3
3
|
import type { Context, ConvexArgsValidator, ConvexReturnsValidator, InferArgs, FunctionType, Visibility, QueryCtx, MutationCtx, ActionCtx } from "./types";
|
|
4
|
-
import { type ValidatorInput, type ReturnsValidatorInput } from "./zod_support";
|
|
4
|
+
import { type ValidatorInput, type ReturnsValidatorInput, type ToConvexArgsValidator, type ToConvexReturnsValidator } from "./zod_support";
|
|
5
5
|
type InferredArgs<T extends ConvexArgsValidator | undefined> = T extends ConvexArgsValidator ? InferArgs<T> : Record<never, never>;
|
|
6
6
|
interface ConvexBuilderDef<TFunctionType extends FunctionType | undefined, TArgsValidator extends ConvexArgsValidator | undefined, TReturnsValidator extends ConvexReturnsValidator | undefined, TVisibility extends Visibility> {
|
|
7
7
|
functionType?: TFunctionType;
|
|
@@ -10,23 +10,24 @@ interface ConvexBuilderDef<TFunctionType extends FunctionType | undefined, TArgs
|
|
|
10
10
|
returnsValidator?: TReturnsValidator;
|
|
11
11
|
visibility: TVisibility;
|
|
12
12
|
}
|
|
13
|
-
export declare class ConvexBuilder<TFunctionType extends FunctionType | undefined = undefined, TInitialContext extends Context = Record<never, never>, TCurrentContext extends Context = Record<never, never>, TArgsValidator extends ConvexArgsValidator | undefined = undefined, TReturnsValidator extends ConvexReturnsValidator | undefined = undefined, TVisibility extends Visibility = "public"> {
|
|
13
|
+
export declare class ConvexBuilder<TDataModel extends GenericDataModel = GenericDataModel, TFunctionType extends FunctionType | undefined = undefined, TInitialContext extends Context = Record<never, never>, TCurrentContext extends Context = Record<never, never>, TArgsValidator extends ConvexArgsValidator | undefined = undefined, TReturnsValidator extends ConvexReturnsValidator | undefined = undefined, TVisibility extends Visibility = "public"> {
|
|
14
14
|
private def;
|
|
15
15
|
constructor(def: ConvexBuilderDef<TFunctionType, TArgsValidator, TReturnsValidator, TVisibility>);
|
|
16
|
-
$context<U extends Context>(): ConvexBuilder<TFunctionType, U & Record<never, never>, U, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
16
|
+
$context<U extends Context>(): ConvexBuilder<TDataModel, TFunctionType, U & Record<never, never>, U, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
17
|
+
middleware<UInContext extends Context, UOutContext extends Context>(middleware: ConvexMiddleware<UInContext, UOutContext>): ConvexMiddleware<UInContext, UOutContext>;
|
|
17
18
|
middleware<UOutContext extends Context>(middleware: ConvexMiddleware<TInitialContext, UOutContext>): ConvexMiddleware<TInitialContext, UOutContext>;
|
|
18
|
-
use<UOutContext extends Context>(middleware: ConvexMiddleware<TCurrentContext, UOutContext>): ConvexBuilder<TFunctionType, TInitialContext, TCurrentContext & UOutContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
19
|
-
query(): ConvexBuilder<"query", TInitialContext extends Record<never, never> ? QueryCtx : TInitialContext, TCurrentContext extends Record<never, never> ? QueryCtx : TCurrentContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
20
|
-
mutation(): ConvexBuilder<"mutation", TInitialContext extends Record<never, never> ? MutationCtx : TInitialContext, TCurrentContext extends Record<never, never> ? MutationCtx : TCurrentContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
21
|
-
action(): ConvexBuilder<"action", TInitialContext extends Record<never, never> ? ActionCtx : TInitialContext, TCurrentContext extends Record<never, never> ? ActionCtx : TCurrentContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
22
|
-
internal(): ConvexBuilder<TFunctionType, TInitialContext, TCurrentContext, TArgsValidator, TReturnsValidator, "internal">;
|
|
23
|
-
input<UInput extends ValidatorInput>(validator: UInput): ConvexBuilder<TFunctionType, TInitialContext, TCurrentContext,
|
|
24
|
-
returns<UReturns extends ReturnsValidatorInput>(validator: UReturns): ConvexBuilder<TFunctionType, TInitialContext, TCurrentContext, TArgsValidator,
|
|
19
|
+
use<UOutContext extends Context>(middleware: ConvexMiddleware<TCurrentContext, UOutContext>): ConvexBuilder<TDataModel, TFunctionType, TInitialContext, TCurrentContext & UOutContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
20
|
+
query(): ConvexBuilder<TDataModel, "query", TInitialContext extends Record<never, never> ? QueryCtx<TDataModel> : TInitialContext, TCurrentContext extends Record<never, never> ? QueryCtx<TDataModel> : TCurrentContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
21
|
+
mutation(): ConvexBuilder<TDataModel, "mutation", TInitialContext extends Record<never, never> ? MutationCtx<TDataModel> : TInitialContext, TCurrentContext extends Record<never, never> ? MutationCtx<TDataModel> : TCurrentContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
22
|
+
action(): ConvexBuilder<TDataModel, "action", TInitialContext extends Record<never, never> ? ActionCtx<TDataModel> : TInitialContext, TCurrentContext extends Record<never, never> ? ActionCtx<TDataModel> : TCurrentContext, TArgsValidator, TReturnsValidator, TVisibility>;
|
|
23
|
+
internal(): ConvexBuilder<TDataModel, TFunctionType, TInitialContext, TCurrentContext, TArgsValidator, TReturnsValidator, "internal">;
|
|
24
|
+
input<UInput extends ValidatorInput>(validator: UInput): ConvexBuilder<TDataModel, TFunctionType, TInitialContext, TCurrentContext, ToConvexArgsValidator<UInput>, TReturnsValidator, TVisibility>;
|
|
25
|
+
returns<UReturns extends ReturnsValidatorInput>(validator: UReturns): ConvexBuilder<TDataModel, TFunctionType, TInitialContext, TCurrentContext, TArgsValidator, ToConvexReturnsValidator<UReturns>, TVisibility>;
|
|
25
26
|
handler<TReturn>(handlerFn: (options: {
|
|
26
27
|
context: TCurrentContext;
|
|
27
28
|
input: InferredArgs<TArgsValidator>;
|
|
28
29
|
}) => Promise<TReturn>): TFunctionType extends "query" ? RegisteredQuery<TVisibility, InferredArgs<TArgsValidator>, Promise<TReturn>> : TFunctionType extends "mutation" ? RegisteredMutation<TVisibility, InferredArgs<TArgsValidator>, Promise<TReturn>> : TFunctionType extends "action" ? RegisteredAction<TVisibility, InferredArgs<TArgsValidator>, Promise<TReturn>> : never;
|
|
29
30
|
}
|
|
30
|
-
export declare
|
|
31
|
+
export declare function createBuilder<TSchema extends SchemaDefinition<any, boolean>>(_schema: TSchema): ConvexBuilder<DataModelFromSchemaDefinition<TSchema>, undefined, Record<never, never>, Record<never, never>, undefined, undefined, "public">;
|
|
31
32
|
export {};
|
|
32
33
|
//# sourceMappingURL=builder.d.ts.map
|
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/builder.js
CHANGED
|
@@ -95,8 +95,10 @@ export class ConvexBuilder {
|
|
|
95
95
|
return registrationFn(config);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
-
export
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
export function createBuilder(_schema) {
|
|
99
|
+
return new ConvexBuilder({
|
|
100
|
+
middlewares: [],
|
|
101
|
+
visibility: "public",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
102
104
|
//# sourceMappingURL=builder.js.map
|
package/dist/builder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,qBAAqB,
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ConvexBuilder,
|
|
1
|
+
export { ConvexBuilder, createBuilder } from "./builder";
|
|
2
2
|
export type { ConvexMiddleware, ConvexMiddlewareOptions, AnyConvexMiddleware, } from "./middleware";
|
|
3
3
|
export type { Context, ConvexArgsValidator, ConvexReturnsValidator, InferArgs, QueryCtx, MutationCtx, ActionCtx, FunctionType, Visibility, } from "./types";
|
|
4
4
|
export { isZodSchema, toConvexValidator, type ValidatorInput, type ReturnsValidatorInput, } from "./zod_support";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAEzD,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,OAAO,EACP,mBAAmB,EACnB,sBAAsB,EACtB,SAAS,EACT,QAAQ,EACR,WAAW,EACX,SAAS,EACT,YAAY,EACZ,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAoBzD,OAAO,EACL,WAAW,EACX,iBAAiB,GAGlB,MAAM,eAAe,CAAC"}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { Context } from "./types";
|
|
1
|
+
import { Context, Promisable } from "./types";
|
|
3
2
|
export interface ConvexMiddlewareOptions<TInContext extends Context> {
|
|
4
3
|
context: TInContext;
|
|
5
4
|
next: <U extends Context>(options: {
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE9C,MAAM,WAAW,uBAAuB,CAAC,UAAU,SAAS,OAAO;IACjE,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE;QACjC,OAAO,EAAE,CAAC,CAAC;KACZ,KAAK,UAAU,CAAC;QAAE,OAAO,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB,CAC/B,UAAU,SAAS,OAAO,EAC1B,WAAW,SAAS,OAAO;IAE3B,CACE,OAAO,EAAE,uBAAuB,CAAC,UAAU,CAAC,GAC3C,UAAU,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,9 +6,10 @@ export type ConvexReturnsValidator = GenericValidator;
|
|
|
6
6
|
export type InferArgs<T extends ConvexArgsValidator> = T extends GenericValidator ? T["type"] : {
|
|
7
7
|
[K in keyof T]: T[K] extends GenericValidator ? T[K]["isOptional"] extends true ? T[K]["type"] | undefined : T[K]["type"] : never;
|
|
8
8
|
};
|
|
9
|
-
export type
|
|
10
|
-
export type
|
|
11
|
-
export type
|
|
9
|
+
export type Promisable<T> = T | PromiseLike<T>;
|
|
10
|
+
export type QueryCtx<DataModel extends GenericDataModel = GenericDataModel> = GenericQueryCtx<DataModel>;
|
|
11
|
+
export type MutationCtx<DataModel extends GenericDataModel = GenericDataModel> = GenericMutationCtx<DataModel>;
|
|
12
|
+
export type ActionCtx<DataModel extends GenericDataModel = GenericDataModel> = GenericActionCtx<DataModel>;
|
|
12
13
|
export type FunctionType = "query" | "mutation" | "action";
|
|
13
14
|
export type Visibility = "public" | "internal";
|
|
14
15
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAE/C,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AACxE,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,CAAC;AAEtD,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,mBAAmB,IACjD,CAAC,SAAS,gBAAgB,GACtB,CAAC,CAAC,MAAM,CAAC,GACT;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,gBAAgB,GACzC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,IAAI,GAC7B,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,GACxB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GACd,KAAK;CACV,CAAC;AAER,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AAE/C,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AACxE,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,CAAC;AAEtD,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,mBAAmB,IACjD,CAAC,SAAS,gBAAgB,GACtB,CAAC,CAAC,MAAM,CAAC,GACT;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,gBAAgB,GACzC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS,IAAI,GAC7B,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,GACxB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GACd,KAAK;CACV,CAAC;AAER,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;AAE/C,MAAM,MAAM,QAAQ,CAAC,SAAS,SAAS,gBAAgB,GAAG,gBAAgB,IACxE,eAAe,CAAC,SAAS,CAAC,CAAC;AAC7B,MAAM,MAAM,WAAW,CAAC,SAAS,SAAS,gBAAgB,GAAG,gBAAgB,IAC3E,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAChC,MAAM,MAAM,SAAS,CAAC,SAAS,SAAS,gBAAgB,GAAG,gBAAgB,IACzE,gBAAgB,CAAC,SAAS,CAAC,CAAC;AAE9B,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC"}
|
package/dist/zod_support.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
|
-
import type { PropertyValidators, GenericValidator } from "convex/values";
|
|
2
|
+
import type { PropertyValidators, GenericValidator, Validator } from "convex/values";
|
|
3
|
+
import type { ConvexArgsValidator } from "./types";
|
|
3
4
|
export declare function isZodSchema(value: any): value is z.ZodTypeAny;
|
|
4
5
|
export declare function toConvexValidator<T extends z.ZodTypeAny>(schema: T): PropertyValidators | GenericValidator;
|
|
5
6
|
export type ValidatorInput = PropertyValidators | GenericValidator | z.ZodObject<any> | z.ZodEffects<any>;
|
|
6
7
|
export type ReturnsValidatorInput = GenericValidator | z.ZodType;
|
|
8
|
+
export type ToConvexArgsValidator<T extends ValidatorInput> = T extends z.ZodObject<infer Shape> ? {
|
|
9
|
+
[K in keyof Shape]: Shape[K] extends z.ZodType<infer Output> ? Validator<Output, Shape[K] extends z.ZodOptional<any> ? "optional" : "required", any> : never;
|
|
10
|
+
} : T extends z.ZodEffects<infer Schema> ? Schema extends z.ZodObject<infer Shape> ? {
|
|
11
|
+
[K in keyof Shape]: Shape[K] extends z.ZodType<infer FieldOutput> ? Validator<FieldOutput, Shape[K] extends z.ZodOptional<any> ? "optional" : "required", any> : never;
|
|
12
|
+
} : ConvexArgsValidator : T extends ConvexArgsValidator ? T : ConvexArgsValidator;
|
|
13
|
+
export type ToConvexReturnsValidator<T extends ReturnsValidatorInput> = T extends z.ZodType<infer Output> ? Validator<Output, "required", any> : T extends GenericValidator ? T : GenericValidator;
|
|
7
14
|
//# sourceMappingURL=zod_support.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod_support.d.ts","sourceRoot":"","sources":["../src/zod_support.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"zod_support.d.ts","sourceRoot":"","sources":["../src/zod_support.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,UAAU,CAI7D;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACtD,MAAM,EAAE,CAAC,GACR,kBAAkB,GAAG,gBAAgB,CAEvC;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,gBAAgB,GAChB,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAChB,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;AAEtB,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,CAAC,CAAC,OAAO,CAAC;AAGjE,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,cAAc,IACxD,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,GAC9B;KACG,CAAC,IAAI,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,MAAM,CAAC,GACxD,SAAS,CACP,MAAM,EACN,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,UAAU,GAAG,UAAU,EAC7D,GAAG,CACJ,GACD,KAAK;CACV,GACD,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,MAAM,MAAM,CAAC,GAClC,MAAM,SAAS,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,GACrC;KACG,CAAC,IAAI,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,WAAW,CAAC,GAC7D,SAAS,CACP,WAAW,EACX,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,UAAU,GAAG,UAAU,EAC7D,GAAG,CACJ,GACD,KAAK;CACV,GACD,mBAAmB,GACrB,CAAC,SAAS,mBAAmB,GAC3B,CAAC,GACD,mBAAmB,CAAC;AAG9B,MAAM,MAAM,wBAAwB,CAAC,CAAC,SAAS,qBAAqB,IAClE,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,MAAM,CAAC,GAC7B,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,GAClC,CAAC,SAAS,gBAAgB,GACxB,CAAC,GACD,gBAAgB,CAAC"}
|
package/dist/zod_support.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod_support.js","sourceRoot":"","sources":["../src/zod_support.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"zod_support.js","sourceRoot":"","sources":["../src/zod_support.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAQxD,MAAM,UAAU,WAAW,CAAC,KAAU;IACpC,OAAO,CACL,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,CAC1E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAAS;IAET,OAAO,WAAW,CAAC,MAAM,CAAQ,CAAC;AACpC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluent-convex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "A fluent API builder for Convex functions with middleware support, inspired by oRPC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"zod": "^3.0.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@orpc/shared": "^1.10.2",
|
|
53
52
|
"convex-helpers": "^0.1.104"
|
|
54
53
|
},
|
|
55
54
|
"devDependencies": {
|
package/src/builder.test-d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { describe, it, assertType } from "vitest";
|
|
2
|
-
import { convex } from "./builder";
|
|
3
2
|
import { v } from "convex/values";
|
|
4
3
|
import { z } from "zod";
|
|
4
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
5
|
+
import { createBuilder } from "./builder";
|
|
6
|
+
|
|
7
|
+
const schema = defineSchema({
|
|
8
|
+
numbers: defineTable({
|
|
9
|
+
value: v.number(),
|
|
10
|
+
}),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const convex = createBuilder(schema);
|
|
5
14
|
|
|
6
15
|
describe("ConvexBuilder Type Tests", () => {
|
|
7
16
|
describe("input validation", () => {
|
|
@@ -307,6 +316,168 @@ describe("ConvexBuilder Type Tests", () => {
|
|
|
307
316
|
return { success: true };
|
|
308
317
|
});
|
|
309
318
|
});
|
|
319
|
+
|
|
320
|
+
it("should infer optional fields as T | undefined", () => {
|
|
321
|
+
convex
|
|
322
|
+
.mutation()
|
|
323
|
+
.input({
|
|
324
|
+
id: v.id("numbers"),
|
|
325
|
+
name: v.optional(v.string()),
|
|
326
|
+
value: v.optional(v.number()),
|
|
327
|
+
})
|
|
328
|
+
.handler(async ({ input }) => {
|
|
329
|
+
assertType<string>(input.id);
|
|
330
|
+
assertType<string | undefined>(input.name);
|
|
331
|
+
assertType<number | undefined>(input.value);
|
|
332
|
+
return null;
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("should handle many optional fields with complex types", () => {
|
|
337
|
+
convex
|
|
338
|
+
.mutation()
|
|
339
|
+
.input({
|
|
340
|
+
id: v.id("numbers"),
|
|
341
|
+
name: v.optional(v.string()),
|
|
342
|
+
count: v.optional(v.number()),
|
|
343
|
+
status: v.optional(v.union(v.string(), v.null())),
|
|
344
|
+
items: v.optional(
|
|
345
|
+
v.array(
|
|
346
|
+
v.object({
|
|
347
|
+
x: v.number(),
|
|
348
|
+
y: v.number(),
|
|
349
|
+
})
|
|
350
|
+
)
|
|
351
|
+
),
|
|
352
|
+
})
|
|
353
|
+
.handler(async ({ input }) => {
|
|
354
|
+
assertType<string>(input.id);
|
|
355
|
+
assertType<string | undefined>(input.name);
|
|
356
|
+
assertType<number | undefined>(input.count);
|
|
357
|
+
assertType<string | null | undefined>(input.status);
|
|
358
|
+
assertType<Array<{ x: number; y: number }> | undefined>(input.items);
|
|
359
|
+
return null;
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("should allow all fields to be optional", () => {
|
|
364
|
+
convex
|
|
365
|
+
.query()
|
|
366
|
+
.input({
|
|
367
|
+
name: v.optional(v.string()),
|
|
368
|
+
minValue: v.optional(v.number()),
|
|
369
|
+
maxValue: v.optional(v.number()),
|
|
370
|
+
})
|
|
371
|
+
.handler(async ({ input }) => {
|
|
372
|
+
assertType<string | undefined>(input.name);
|
|
373
|
+
assertType<number | undefined>(input.minValue);
|
|
374
|
+
assertType<number | undefined>(input.maxValue);
|
|
375
|
+
return [];
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("should make all properties required but allow undefined for optional fields", () => {
|
|
380
|
+
convex
|
|
381
|
+
.mutation()
|
|
382
|
+
.input({
|
|
383
|
+
id: v.id("numbers"),
|
|
384
|
+
name: v.optional(v.string()),
|
|
385
|
+
value: v.optional(v.number()),
|
|
386
|
+
})
|
|
387
|
+
.handler(async ({ input }) => {
|
|
388
|
+
// All properties are present in the type
|
|
389
|
+
assertType<string>(input.id);
|
|
390
|
+
assertType<string | undefined>(input.name);
|
|
391
|
+
assertType<number | undefined>(input.value);
|
|
392
|
+
|
|
393
|
+
// Valid assignments with all properties
|
|
394
|
+
const testArgs1: typeof input = {
|
|
395
|
+
id: "123" as any,
|
|
396
|
+
name: undefined,
|
|
397
|
+
value: undefined,
|
|
398
|
+
};
|
|
399
|
+
const testArgs2: typeof input = {
|
|
400
|
+
id: "123" as any,
|
|
401
|
+
name: "test",
|
|
402
|
+
value: undefined,
|
|
403
|
+
};
|
|
404
|
+
const testArgs3: typeof input = {
|
|
405
|
+
id: "123" as any,
|
|
406
|
+
name: undefined,
|
|
407
|
+
value: 42,
|
|
408
|
+
};
|
|
409
|
+
const testArgs4: typeof input = {
|
|
410
|
+
id: "123" as any,
|
|
411
|
+
name: "test",
|
|
412
|
+
value: 42,
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
return { id: input.id };
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("should work with Partial for truly optional calling patterns", () => {
|
|
420
|
+
convex
|
|
421
|
+
.mutation()
|
|
422
|
+
.input({
|
|
423
|
+
id: v.id("numbers"),
|
|
424
|
+
name: v.optional(v.string()),
|
|
425
|
+
count: v.optional(v.number()),
|
|
426
|
+
status: v.optional(v.union(v.string(), v.null())),
|
|
427
|
+
})
|
|
428
|
+
.handler(async ({ input }) => {
|
|
429
|
+
// When calling, use Partial to make properties truly optional
|
|
430
|
+
type CallArgs = Partial<typeof input> & Pick<typeof input, "id">;
|
|
431
|
+
|
|
432
|
+
// Test various combinations are valid
|
|
433
|
+
const testArgs1: CallArgs = { id: "123" as any };
|
|
434
|
+
const testArgs2: CallArgs = { id: "123" as any, name: "test" };
|
|
435
|
+
const testArgs3: CallArgs = { id: "123" as any, count: 10 };
|
|
436
|
+
const testArgs4: CallArgs = { id: "123" as any, status: "active" };
|
|
437
|
+
const testArgs5: CallArgs = { id: "123" as any, status: null };
|
|
438
|
+
const testArgs6: CallArgs = {
|
|
439
|
+
id: "123" as any,
|
|
440
|
+
name: "test",
|
|
441
|
+
count: 10,
|
|
442
|
+
};
|
|
443
|
+
const testArgs7: CallArgs = {
|
|
444
|
+
id: "123" as any,
|
|
445
|
+
name: "test",
|
|
446
|
+
count: 10,
|
|
447
|
+
status: "active",
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
return null;
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should allow empty object when all fields are optional", () => {
|
|
455
|
+
convex
|
|
456
|
+
.query()
|
|
457
|
+
.input({
|
|
458
|
+
name: v.optional(v.string()),
|
|
459
|
+
minValue: v.optional(v.number()),
|
|
460
|
+
maxValue: v.optional(v.number()),
|
|
461
|
+
})
|
|
462
|
+
.handler(async ({ input }) => {
|
|
463
|
+
// For calling, use Partial
|
|
464
|
+
type CallArgs = Partial<typeof input>;
|
|
465
|
+
|
|
466
|
+
// Test various combinations are valid
|
|
467
|
+
const testArgs1: CallArgs = {};
|
|
468
|
+
const testArgs2: CallArgs = { name: "test" };
|
|
469
|
+
const testArgs3: CallArgs = { minValue: 10 };
|
|
470
|
+
const testArgs4: CallArgs = { maxValue: 20 };
|
|
471
|
+
const testArgs5: CallArgs = { minValue: 10, maxValue: 20 };
|
|
472
|
+
const testArgs6: CallArgs = {
|
|
473
|
+
name: "test",
|
|
474
|
+
minValue: 10,
|
|
475
|
+
maxValue: 20,
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
return [];
|
|
479
|
+
});
|
|
480
|
+
});
|
|
310
481
|
});
|
|
311
482
|
|
|
312
483
|
describe("complex types", () => {
|
package/src/builder.ts
CHANGED
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
internalMutationGeneric,
|
|
9
9
|
actionGeneric,
|
|
10
10
|
internalActionGeneric,
|
|
11
|
+
type GenericDataModel,
|
|
12
|
+
type DataModelFromSchemaDefinition,
|
|
13
|
+
type SchemaDefinition,
|
|
11
14
|
} from "convex/server";
|
|
12
15
|
import type { ConvexMiddleware, AnyConvexMiddleware } from "./middleware";
|
|
13
16
|
import type {
|
|
@@ -26,6 +29,8 @@ import {
|
|
|
26
29
|
toConvexValidator,
|
|
27
30
|
type ValidatorInput,
|
|
28
31
|
type ReturnsValidatorInput,
|
|
32
|
+
type ToConvexArgsValidator,
|
|
33
|
+
type ToConvexReturnsValidator,
|
|
29
34
|
} from "./zod_support";
|
|
30
35
|
|
|
31
36
|
type InferredArgs<T extends ConvexArgsValidator | undefined> =
|
|
@@ -45,6 +50,7 @@ interface ConvexBuilderDef<
|
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
export class ConvexBuilder<
|
|
53
|
+
TDataModel extends GenericDataModel = GenericDataModel,
|
|
48
54
|
TFunctionType extends FunctionType | undefined = undefined,
|
|
49
55
|
TInitialContext extends Context = Record<never, never>,
|
|
50
56
|
TCurrentContext extends Context = Record<never, never>,
|
|
@@ -71,6 +77,7 @@ export class ConvexBuilder<
|
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
$context<U extends Context>(): ConvexBuilder<
|
|
80
|
+
TDataModel,
|
|
74
81
|
TFunctionType,
|
|
75
82
|
U & Record<never, never>,
|
|
76
83
|
U,
|
|
@@ -84,15 +91,22 @@ export class ConvexBuilder<
|
|
|
84
91
|
});
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
middleware<UInContext extends Context, UOutContext extends Context>(
|
|
95
|
+
middleware: ConvexMiddleware<UInContext, UOutContext>
|
|
96
|
+
): ConvexMiddleware<UInContext, UOutContext>;
|
|
87
97
|
middleware<UOutContext extends Context>(
|
|
88
98
|
middleware: ConvexMiddleware<TInitialContext, UOutContext>
|
|
89
|
-
): ConvexMiddleware<TInitialContext, UOutContext
|
|
99
|
+
): ConvexMiddleware<TInitialContext, UOutContext>;
|
|
100
|
+
middleware<UInContext extends Context, UOutContext extends Context>(
|
|
101
|
+
middleware: ConvexMiddleware<UInContext, UOutContext>
|
|
102
|
+
): ConvexMiddleware<UInContext, UOutContext> {
|
|
90
103
|
return middleware;
|
|
91
104
|
}
|
|
92
105
|
|
|
93
106
|
use<UOutContext extends Context>(
|
|
94
107
|
middleware: ConvexMiddleware<TCurrentContext, UOutContext>
|
|
95
108
|
): ConvexBuilder<
|
|
109
|
+
TDataModel,
|
|
96
110
|
TFunctionType,
|
|
97
111
|
TInitialContext,
|
|
98
112
|
TCurrentContext & UOutContext,
|
|
@@ -107,9 +121,14 @@ export class ConvexBuilder<
|
|
|
107
121
|
}
|
|
108
122
|
|
|
109
123
|
query(): ConvexBuilder<
|
|
124
|
+
TDataModel,
|
|
110
125
|
"query",
|
|
111
|
-
TInitialContext extends Record<never, never>
|
|
112
|
-
|
|
126
|
+
TInitialContext extends Record<never, never>
|
|
127
|
+
? QueryCtx<TDataModel>
|
|
128
|
+
: TInitialContext,
|
|
129
|
+
TCurrentContext extends Record<never, never>
|
|
130
|
+
? QueryCtx<TDataModel>
|
|
131
|
+
: TCurrentContext,
|
|
113
132
|
TArgsValidator,
|
|
114
133
|
TReturnsValidator,
|
|
115
134
|
TVisibility
|
|
@@ -121,12 +140,13 @@ export class ConvexBuilder<
|
|
|
121
140
|
}
|
|
122
141
|
|
|
123
142
|
mutation(): ConvexBuilder<
|
|
143
|
+
TDataModel,
|
|
124
144
|
"mutation",
|
|
125
145
|
TInitialContext extends Record<never, never>
|
|
126
|
-
? MutationCtx
|
|
146
|
+
? MutationCtx<TDataModel>
|
|
127
147
|
: TInitialContext,
|
|
128
148
|
TCurrentContext extends Record<never, never>
|
|
129
|
-
? MutationCtx
|
|
149
|
+
? MutationCtx<TDataModel>
|
|
130
150
|
: TCurrentContext,
|
|
131
151
|
TArgsValidator,
|
|
132
152
|
TReturnsValidator,
|
|
@@ -139,9 +159,14 @@ export class ConvexBuilder<
|
|
|
139
159
|
}
|
|
140
160
|
|
|
141
161
|
action(): ConvexBuilder<
|
|
162
|
+
TDataModel,
|
|
142
163
|
"action",
|
|
143
|
-
TInitialContext extends Record<never, never>
|
|
144
|
-
|
|
164
|
+
TInitialContext extends Record<never, never>
|
|
165
|
+
? ActionCtx<TDataModel>
|
|
166
|
+
: TInitialContext,
|
|
167
|
+
TCurrentContext extends Record<never, never>
|
|
168
|
+
? ActionCtx<TDataModel>
|
|
169
|
+
: TCurrentContext,
|
|
145
170
|
TArgsValidator,
|
|
146
171
|
TReturnsValidator,
|
|
147
172
|
TVisibility
|
|
@@ -153,6 +178,7 @@ export class ConvexBuilder<
|
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
internal(): ConvexBuilder<
|
|
181
|
+
TDataModel,
|
|
156
182
|
TFunctionType,
|
|
157
183
|
TInitialContext,
|
|
158
184
|
TCurrentContext,
|
|
@@ -169,10 +195,11 @@ export class ConvexBuilder<
|
|
|
169
195
|
input<UInput extends ValidatorInput>(
|
|
170
196
|
validator: UInput
|
|
171
197
|
): ConvexBuilder<
|
|
198
|
+
TDataModel,
|
|
172
199
|
TFunctionType,
|
|
173
200
|
TInitialContext,
|
|
174
201
|
TCurrentContext,
|
|
175
|
-
|
|
202
|
+
ToConvexArgsValidator<UInput>,
|
|
176
203
|
TReturnsValidator,
|
|
177
204
|
TVisibility
|
|
178
205
|
> {
|
|
@@ -189,11 +216,12 @@ export class ConvexBuilder<
|
|
|
189
216
|
returns<UReturns extends ReturnsValidatorInput>(
|
|
190
217
|
validator: UReturns
|
|
191
218
|
): ConvexBuilder<
|
|
219
|
+
TDataModel,
|
|
192
220
|
TFunctionType,
|
|
193
221
|
TInitialContext,
|
|
194
222
|
TCurrentContext,
|
|
195
223
|
TArgsValidator,
|
|
196
|
-
|
|
224
|
+
ToConvexReturnsValidator<UReturns>,
|
|
197
225
|
TVisibility
|
|
198
226
|
> {
|
|
199
227
|
const convexValidator = isZodSchema(validator)
|
|
@@ -245,7 +273,10 @@ export class ConvexBuilder<
|
|
|
245
273
|
}
|
|
246
274
|
|
|
247
275
|
const composedHandler = async (
|
|
248
|
-
baseCtx:
|
|
276
|
+
baseCtx:
|
|
277
|
+
| QueryCtx<TDataModel>
|
|
278
|
+
| MutationCtx<TDataModel>
|
|
279
|
+
| ActionCtx<TDataModel>,
|
|
249
280
|
baseArgs: InferredArgs<TArgsValidator>
|
|
250
281
|
) => {
|
|
251
282
|
let currentContext: any = baseCtx;
|
|
@@ -281,14 +312,19 @@ export class ConvexBuilder<
|
|
|
281
312
|
}
|
|
282
313
|
}
|
|
283
314
|
|
|
284
|
-
export
|
|
315
|
+
export function createBuilder<TSchema extends SchemaDefinition<any, boolean>>(
|
|
316
|
+
_schema: TSchema
|
|
317
|
+
): ConvexBuilder<
|
|
318
|
+
DataModelFromSchemaDefinition<TSchema>,
|
|
285
319
|
undefined,
|
|
286
320
|
Record<never, never>,
|
|
287
321
|
Record<never, never>,
|
|
288
322
|
undefined,
|
|
289
323
|
undefined,
|
|
290
324
|
"public"
|
|
291
|
-
>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
325
|
+
> {
|
|
326
|
+
return new ConvexBuilder({
|
|
327
|
+
middlewares: [],
|
|
328
|
+
visibility: "public",
|
|
329
|
+
});
|
|
330
|
+
}
|
package/src/index.ts
CHANGED
package/src/middleware.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { Context } from "./types";
|
|
1
|
+
import { Context, Promisable } from "./types";
|
|
3
2
|
|
|
4
3
|
export interface ConvexMiddlewareOptions<TInContext extends Context> {
|
|
5
4
|
context: TInContext;
|
|
@@ -13,7 +12,7 @@ export interface ConvexMiddleware<
|
|
|
13
12
|
TOutContext extends Context,
|
|
14
13
|
> {
|
|
15
14
|
(
|
|
16
|
-
options: ConvexMiddlewareOptions<TInContext
|
|
15
|
+
options: ConvexMiddlewareOptions<TInContext>,
|
|
17
16
|
): Promisable<{ context: TOutContext }>;
|
|
18
17
|
}
|
|
19
18
|
|
package/src/types.ts
CHANGED
|
@@ -22,9 +22,14 @@ export type InferArgs<T extends ConvexArgsValidator> =
|
|
|
22
22
|
: never;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
export type
|
|
26
|
-
|
|
27
|
-
export type
|
|
25
|
+
export type Promisable<T> = T | PromiseLike<T>;
|
|
26
|
+
|
|
27
|
+
export type QueryCtx<DataModel extends GenericDataModel = GenericDataModel> =
|
|
28
|
+
GenericQueryCtx<DataModel>;
|
|
29
|
+
export type MutationCtx<DataModel extends GenericDataModel = GenericDataModel> =
|
|
30
|
+
GenericMutationCtx<DataModel>;
|
|
31
|
+
export type ActionCtx<DataModel extends GenericDataModel = GenericDataModel> =
|
|
32
|
+
GenericActionCtx<DataModel>;
|
|
28
33
|
|
|
29
34
|
export type FunctionType = "query" | "mutation" | "action";
|
|
30
35
|
export type Visibility = "public" | "internal";
|
package/src/zod_support.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
import { zodToConvex } from "convex-helpers/server/zod";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
PropertyValidators,
|
|
5
|
+
GenericValidator,
|
|
6
|
+
Validator,
|
|
7
|
+
} from "convex/values";
|
|
8
|
+
import type { ConvexArgsValidator } from "./types";
|
|
4
9
|
|
|
5
10
|
export function isZodSchema(value: any): value is z.ZodTypeAny {
|
|
6
11
|
return (
|
|
@@ -21,3 +26,39 @@ export type ValidatorInput =
|
|
|
21
26
|
| z.ZodEffects<any>;
|
|
22
27
|
|
|
23
28
|
export type ReturnsValidatorInput = GenericValidator | z.ZodType;
|
|
29
|
+
|
|
30
|
+
// Helper type to convert ValidatorInput to a proper ConvexArgsValidator while preserving types
|
|
31
|
+
export type ToConvexArgsValidator<T extends ValidatorInput> =
|
|
32
|
+
T extends z.ZodObject<infer Shape>
|
|
33
|
+
? {
|
|
34
|
+
[K in keyof Shape]: Shape[K] extends z.ZodType<infer Output>
|
|
35
|
+
? Validator<
|
|
36
|
+
Output,
|
|
37
|
+
Shape[K] extends z.ZodOptional<any> ? "optional" : "required",
|
|
38
|
+
any
|
|
39
|
+
>
|
|
40
|
+
: never;
|
|
41
|
+
}
|
|
42
|
+
: T extends z.ZodEffects<infer Schema>
|
|
43
|
+
? Schema extends z.ZodObject<infer Shape>
|
|
44
|
+
? {
|
|
45
|
+
[K in keyof Shape]: Shape[K] extends z.ZodType<infer FieldOutput>
|
|
46
|
+
? Validator<
|
|
47
|
+
FieldOutput,
|
|
48
|
+
Shape[K] extends z.ZodOptional<any> ? "optional" : "required",
|
|
49
|
+
any
|
|
50
|
+
>
|
|
51
|
+
: never;
|
|
52
|
+
}
|
|
53
|
+
: ConvexArgsValidator
|
|
54
|
+
: T extends ConvexArgsValidator
|
|
55
|
+
? T
|
|
56
|
+
: ConvexArgsValidator;
|
|
57
|
+
|
|
58
|
+
// Helper type to convert ReturnsValidatorInput to a proper ConvexReturnsValidator while preserving types
|
|
59
|
+
export type ToConvexReturnsValidator<T extends ReturnsValidatorInput> =
|
|
60
|
+
T extends z.ZodType<infer Output>
|
|
61
|
+
? Validator<Output, "required", any>
|
|
62
|
+
: T extends GenericValidator
|
|
63
|
+
? T
|
|
64
|
+
: GenericValidator;
|
package/README.md
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Fluent Convex
|
|
2
|
-
|
|
3
|
-
A fluent API builder for Convex functions with middleware support, inspired by [oRPC](https://orpc.unnoq.com/).
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🔄 **Middleware support** - Compose reusable middleware for authentication, logging, and more
|
|
8
|
-
- 🎯 **Type-safe** - Full TypeScript support with type inference
|
|
9
|
-
- ✨ **Fluent API** - Chain methods for a clean, readable syntax
|
|
10
|
-
- 🔌 **Zod integration** - Use Zod schemas alongside Convex validators
|
|
11
|
-
- 🚀 **Works with Convex** - Built on top of Convex's function system
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install fluent-convex convex convex-helpers zod
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Quick Start
|
|
20
|
-
|
|
21
|
-
```ts
|
|
22
|
-
import { convex } from "fluent-convex";
|
|
23
|
-
import { v } from "convex/values";
|
|
24
|
-
|
|
25
|
-
// Simple query
|
|
26
|
-
export const listNumbers = convex
|
|
27
|
-
.query()
|
|
28
|
-
.input({ count: v.number() })
|
|
29
|
-
.handler(async ({ context, input }) => {
|
|
30
|
-
const numbers = await context.db
|
|
31
|
-
.query("numbers")
|
|
32
|
-
.order("desc")
|
|
33
|
-
.take(input.count);
|
|
34
|
-
|
|
35
|
-
return { numbers: numbers.map((n) => n.value) };
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// With middleware
|
|
39
|
-
const authMiddleware = convex.query().middleware(async ({ context, next }) => {
|
|
40
|
-
const identity = await context.auth.getUserIdentity();
|
|
41
|
-
if (!identity) {
|
|
42
|
-
throw new Error("Unauthorized");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return next({
|
|
46
|
-
context: {
|
|
47
|
-
...context,
|
|
48
|
-
user: {
|
|
49
|
-
id: identity.subject,
|
|
50
|
-
name: identity.name ?? "Unknown",
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
export const listNumbersAuth = convex
|
|
57
|
-
.query()
|
|
58
|
-
.use(authMiddleware)
|
|
59
|
-
.input({ count: v.number() })
|
|
60
|
-
.handler(async ({ context, input }) => {
|
|
61
|
-
const numbers = await context.db
|
|
62
|
-
.query("numbers")
|
|
63
|
-
.order("desc")
|
|
64
|
-
.take(input.count);
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
viewer: context.user.name, // user is available from middleware!
|
|
68
|
-
numbers: numbers.map((n) => n.value),
|
|
69
|
-
};
|
|
70
|
-
});
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Using Zod
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
import { z } from "zod";
|
|
77
|
-
import { convex } from "fluent-convex";
|
|
78
|
-
|
|
79
|
-
export const listNumbersWithZod = convex
|
|
80
|
-
.query()
|
|
81
|
-
.input(
|
|
82
|
-
z.object({
|
|
83
|
-
count: z.number().int().min(1).max(100),
|
|
84
|
-
})
|
|
85
|
-
)
|
|
86
|
-
.handler(async ({ context, input }) => {
|
|
87
|
-
// input.count is properly typed as number
|
|
88
|
-
const numbers = await context.db.query("numbers").take(input.count);
|
|
89
|
-
|
|
90
|
-
return { numbers: numbers.map((n) => n.value) };
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## API
|
|
95
|
-
|
|
96
|
-
### Methods
|
|
97
|
-
|
|
98
|
-
- `.query()` - Define a Convex query
|
|
99
|
-
- `.mutation()` - Define a Convex mutation
|
|
100
|
-
- `.action()` - Define a Convex action
|
|
101
|
-
- `.internal()` - Make the function internal (private)
|
|
102
|
-
- `.input(validator)` - Set input validation (Convex or Zod)
|
|
103
|
-
- `.returns(validator)` - Set return validation (Convex or Zod)
|
|
104
|
-
- `.use(middleware)` - Apply middleware
|
|
105
|
-
- `.middleware(fn)` - Create a middleware function
|
|
106
|
-
- `.handler(fn)` - Define the function handler
|
|
107
|
-
|
|
108
|
-
## Example
|
|
109
|
-
|
|
110
|
-
Check out the `/example` directory for a complete working example with various use cases including:
|
|
111
|
-
|
|
112
|
-
- Simple queries and mutations
|
|
113
|
-
- Middleware composition
|
|
114
|
-
- Zod integration
|
|
115
|
-
- Internal functions
|
|
116
|
-
- Type-safe context transformations
|
|
117
|
-
|
|
118
|
-
## Development
|
|
119
|
-
|
|
120
|
-
This is a monorepo structure:
|
|
121
|
-
|
|
122
|
-
- `/src` - The npm package source code
|
|
123
|
-
- `/example` - Example Convex app using the package
|
|
124
|
-
|
|
125
|
-
### Building the package
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
npm run build
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### Running tests
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
npm test
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Running the example
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
cd example
|
|
141
|
-
npm install
|
|
142
|
-
npm run dev
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
## Credits
|
|
146
|
-
|
|
147
|
-
Borrowed heavily from [oRPC](https://orpc.unnoq.com/learn-and-contribute/overview) and helped out by AI.
|