@pylonsync/functions 0.3.331 → 0.3.332
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/define.d.ts +33 -9
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +28 -1
- package/dist/validators.d.ts +17 -17
- package/package.json +2 -1
- package/src/define.ts +96 -9
- package/src/define.types.test.ts +109 -0
- package/src/index.ts +5 -0
- package/src/types.ts +43 -1
- package/src/validators.ts +44 -20
package/dist/define.d.ts
CHANGED
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
* forgetting an auth check — when `auth: "user"` is in effect,
|
|
21
21
|
* an anonymous request never reaches the handler at all.
|
|
22
22
|
*/
|
|
23
|
-
import type { FnDefinition, QueryCtx, MutationCtx, ActionCtx,
|
|
24
|
-
interface CommonDef {
|
|
25
|
-
args?:
|
|
23
|
+
import type { FnDefinition, QueryCtx, MutationCtx, ActionCtx, ValidatorSchema, InferArgs } from "./types";
|
|
24
|
+
interface CommonDef<TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined> {
|
|
25
|
+
args?: TSchema;
|
|
26
26
|
/**
|
|
27
27
|
* When true, the function is callable only via `ctx.runQuery()` /
|
|
28
28
|
* `ctx.runMutation()` / `ctx.runAction()` from another function
|
|
@@ -56,28 +56,28 @@ interface CommonDef {
|
|
|
56
56
|
*/
|
|
57
57
|
timeout?: number;
|
|
58
58
|
}
|
|
59
|
-
interface QueryDefRequired<TArgs, TReturn> extends CommonDef {
|
|
59
|
+
interface QueryDefRequired<TArgs, TReturn, TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined> extends CommonDef<TSchema> {
|
|
60
60
|
/** Defaults to `"user"`. See [`AuthMode`] for the full surface. */
|
|
61
61
|
auth?: "user" | "admin";
|
|
62
62
|
handler: (ctx: QueryCtx<"required">, args: TArgs) => Promise<TReturn>;
|
|
63
63
|
}
|
|
64
|
-
interface QueryDefOptional<TArgs, TReturn> extends CommonDef {
|
|
64
|
+
interface QueryDefOptional<TArgs, TReturn, TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined> extends CommonDef<TSchema> {
|
|
65
65
|
auth: "public" | "guest";
|
|
66
66
|
handler: (ctx: QueryCtx<"optional">, args: TArgs) => Promise<TReturn>;
|
|
67
67
|
}
|
|
68
|
-
interface MutationDefRequired<TArgs, TReturn> extends CommonDef {
|
|
68
|
+
interface MutationDefRequired<TArgs, TReturn, TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined> extends CommonDef<TSchema> {
|
|
69
69
|
auth?: "user" | "admin";
|
|
70
70
|
handler: (ctx: MutationCtx<"required">, args: TArgs) => Promise<TReturn>;
|
|
71
71
|
}
|
|
72
|
-
interface MutationDefOptional<TArgs, TReturn> extends CommonDef {
|
|
72
|
+
interface MutationDefOptional<TArgs, TReturn, TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined> extends CommonDef<TSchema> {
|
|
73
73
|
auth: "public" | "guest";
|
|
74
74
|
handler: (ctx: MutationCtx<"optional">, args: TArgs) => Promise<TReturn>;
|
|
75
75
|
}
|
|
76
|
-
interface ActionDefRequired<TArgs, TReturn> extends CommonDef {
|
|
76
|
+
interface ActionDefRequired<TArgs, TReturn, TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined> extends CommonDef<TSchema> {
|
|
77
77
|
auth?: "user" | "admin";
|
|
78
78
|
handler: (ctx: ActionCtx<"required">, args: TArgs) => Promise<TReturn>;
|
|
79
79
|
}
|
|
80
|
-
interface ActionDefOptional<TArgs, TReturn> extends CommonDef {
|
|
80
|
+
interface ActionDefOptional<TArgs, TReturn, TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined> extends CommonDef<TSchema> {
|
|
81
81
|
auth: "public" | "guest";
|
|
82
82
|
handler: (ctx: ActionCtx<"optional">, args: TArgs) => Promise<TReturn>;
|
|
83
83
|
}
|
|
@@ -113,6 +113,14 @@ interface ActionDefOptional<TArgs, TReturn> extends CommonDef {
|
|
|
113
113
|
* });
|
|
114
114
|
* ```
|
|
115
115
|
*/
|
|
116
|
+
export declare function query<const TSchema extends ValidatorSchema, TReturn, TAuth extends "user" | "admin" | undefined>(def: QueryDefRequired<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
117
|
+
args: TSchema;
|
|
118
|
+
auth?: TAuth;
|
|
119
|
+
}): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
120
|
+
export declare function query<const TSchema extends ValidatorSchema, TReturn, TAuth extends "public" | "guest">(def: QueryDefOptional<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
121
|
+
args: TSchema;
|
|
122
|
+
auth: TAuth;
|
|
123
|
+
}): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
116
124
|
export declare function query<TArgs = Record<string, unknown>, TReturn = unknown>(def: QueryDefRequired<TArgs, TReturn>): FnDefinition<TArgs, TReturn>;
|
|
117
125
|
export declare function query<TArgs = Record<string, unknown>, TReturn = unknown>(def: QueryDefOptional<TArgs, TReturn>): FnDefinition<TArgs, TReturn>;
|
|
118
126
|
/**
|
|
@@ -144,6 +152,14 @@ export declare function query<TArgs = Record<string, unknown>, TReturn = unknown
|
|
|
144
152
|
* });
|
|
145
153
|
* ```
|
|
146
154
|
*/
|
|
155
|
+
export declare function mutation<const TSchema extends ValidatorSchema, TReturn, TAuth extends "user" | "admin" | undefined>(def: MutationDefRequired<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
156
|
+
args: TSchema;
|
|
157
|
+
auth?: TAuth;
|
|
158
|
+
}): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
159
|
+
export declare function mutation<const TSchema extends ValidatorSchema, TReturn, TAuth extends "public" | "guest">(def: MutationDefOptional<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
160
|
+
args: TSchema;
|
|
161
|
+
auth: TAuth;
|
|
162
|
+
}): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
147
163
|
export declare function mutation<TArgs = Record<string, unknown>, TReturn = unknown>(def: MutationDefRequired<TArgs, TReturn>): FnDefinition<TArgs, TReturn>;
|
|
148
164
|
export declare function mutation<TArgs = Record<string, unknown>, TReturn = unknown>(def: MutationDefOptional<TArgs, TReturn>): FnDefinition<TArgs, TReturn>;
|
|
149
165
|
/**
|
|
@@ -190,6 +206,14 @@ export declare function mutation<TArgs = Record<string, unknown>, TReturn = unkn
|
|
|
190
206
|
* });
|
|
191
207
|
* ```
|
|
192
208
|
*/
|
|
209
|
+
export declare function action<const TSchema extends ValidatorSchema, TReturn, TAuth extends "user" | "admin" | undefined>(def: ActionDefRequired<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
210
|
+
args: TSchema;
|
|
211
|
+
auth?: TAuth;
|
|
212
|
+
}): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
213
|
+
export declare function action<const TSchema extends ValidatorSchema, TReturn, TAuth extends "public" | "guest">(def: ActionDefOptional<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
214
|
+
args: TSchema;
|
|
215
|
+
auth: TAuth;
|
|
216
|
+
}): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
193
217
|
export declare function action<TArgs = Record<string, unknown>, TReturn = unknown>(def: ActionDefRequired<TArgs, TReturn>): FnDefinition<TArgs, TReturn>;
|
|
194
218
|
export declare function action<TArgs = Record<string, unknown>, TReturn = unknown>(def: ActionDefOptional<TArgs, TReturn>): FnDefinition<TArgs, TReturn>;
|
|
195
219
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -22,4 +22,4 @@ export { v } from "./validators";
|
|
|
22
22
|
export { resetDb, installTestIsolation } from "./testing";
|
|
23
23
|
export { slugifyName, availableSlug } from "./slugify";
|
|
24
24
|
export type { SsrResponse, SsrCookieOptions, SsrMetadata, Sitemap, SitemapEntry, Robots, RobotsRule, } from "./ssr-runtime";
|
|
25
|
-
export type { QueryCtx, MutationCtx, ActionCtx, DbReader, DbWriter, Stream, Scheduler, AuthInfo, AuthMode, AuthRequirement, FnDefinition, RequireMember, RequireMemberOptions, MemberRow, } from "./types";
|
|
25
|
+
export type { QueryCtx, MutationCtx, ActionCtx, DbReader, DbWriter, Stream, Scheduler, AuthInfo, AuthMode, AuthRequirement, FnDefinition, Validator, AnyValidator, ValidatorSchema, InferValidator, InferArgs, RequireMember, RequireMemberOptions, MemberRow, } from "./types";
|
package/dist/types.d.ts
CHANGED
|
@@ -545,9 +545,19 @@ export interface FnDefinition<TArgs = unknown, TReturn = unknown> {
|
|
|
545
545
|
*/
|
|
546
546
|
timeout?: number;
|
|
547
547
|
}
|
|
548
|
-
|
|
548
|
+
declare const validatorType: unique symbol;
|
|
549
|
+
declare const validatorOptional: unique symbol;
|
|
550
|
+
/**
|
|
551
|
+
* Runtime validator carrying its validated TypeScript type.
|
|
552
|
+
*
|
|
553
|
+
* The symbol properties are type-only markers: they emit no runtime data and
|
|
554
|
+
* let the function constructors derive handler arguments from an `args` schema.
|
|
555
|
+
*/
|
|
556
|
+
export interface Validator<T = unknown, TOptional extends boolean = boolean> {
|
|
549
557
|
type: string;
|
|
550
558
|
optional?: boolean;
|
|
559
|
+
readonly [validatorType]?: T;
|
|
560
|
+
readonly [validatorOptional]?: TOptional;
|
|
551
561
|
/** For v.id("tableName") */
|
|
552
562
|
table?: string;
|
|
553
563
|
/** For v.array(v.string()) */
|
|
@@ -559,3 +569,20 @@ export interface Validator {
|
|
|
559
569
|
/** For v.literal("value") */
|
|
560
570
|
value?: unknown;
|
|
561
571
|
}
|
|
572
|
+
/** Any validator accepted in an argument schema. */
|
|
573
|
+
export type AnyValidator = Validator<any, boolean>;
|
|
574
|
+
/** A function argument schema keyed by argument name. */
|
|
575
|
+
export type ValidatorSchema = Record<string, AnyValidator>;
|
|
576
|
+
/** Extract the value accepted by a validator. */
|
|
577
|
+
export type InferValidator<TValidator extends AnyValidator> = TValidator extends Validator<infer TValue, boolean> ? TValue : never;
|
|
578
|
+
type OptionalValidatorKeys<TSchema extends ValidatorSchema> = {
|
|
579
|
+
[TKey in keyof TSchema]-?: TSchema[TKey] extends Validator<any, true> ? TKey : never;
|
|
580
|
+
}[keyof TSchema];
|
|
581
|
+
type RequiredValidatorKeys<TSchema extends ValidatorSchema> = Exclude<keyof TSchema, OptionalValidatorKeys<TSchema>>;
|
|
582
|
+
/** Derive a handler argument object from a validator schema. */
|
|
583
|
+
export type InferArgs<TSchema extends ValidatorSchema> = {
|
|
584
|
+
[TKey in RequiredValidatorKeys<TSchema>]: InferValidator<TSchema[TKey]>;
|
|
585
|
+
} & {
|
|
586
|
+
[TKey in OptionalValidatorKeys<TSchema>]?: InferValidator<TSchema[TKey]>;
|
|
587
|
+
};
|
|
588
|
+
export {};
|
package/dist/validators.d.ts
CHANGED
|
@@ -21,52 +21,52 @@
|
|
|
21
21
|
* });
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
import type { Validator } from "./types";
|
|
24
|
+
import type { AnyValidator, InferArgs, InferValidator, Validator, ValidatorSchema } from "./types";
|
|
25
25
|
export declare const v: {
|
|
26
26
|
/** String value. */
|
|
27
|
-
string: () => Validator
|
|
27
|
+
string: () => Validator<string, false>;
|
|
28
28
|
/** Number (float64). Same as `v.float()`. */
|
|
29
|
-
number: () => Validator
|
|
29
|
+
number: () => Validator<number, false>;
|
|
30
30
|
/**
|
|
31
31
|
* 64-bit float. Alias for `v.number()` so the validator API matches the
|
|
32
32
|
* schema DSL (which uses `field.float()`). Prefer this in new code.
|
|
33
33
|
*/
|
|
34
|
-
float: () => Validator
|
|
34
|
+
float: () => Validator<number, false>;
|
|
35
35
|
/** Integer. */
|
|
36
|
-
int: () => Validator
|
|
36
|
+
int: () => Validator<number, false>;
|
|
37
37
|
/** Boolean. Same as `v.bool()`. */
|
|
38
|
-
boolean: () => Validator
|
|
38
|
+
boolean: () => Validator<boolean, false>;
|
|
39
39
|
/**
|
|
40
40
|
* Boolean. Alias for `v.boolean()` so the validator API matches the
|
|
41
41
|
* schema DSL (which uses `field.bool()`). Prefer this in new code.
|
|
42
42
|
*/
|
|
43
|
-
bool: () => Validator
|
|
43
|
+
bool: () => Validator<boolean, false>;
|
|
44
44
|
/**
|
|
45
45
|
* ISO-8601 datetime string. Validates the shape of a string value; the
|
|
46
46
|
* stored column type comes from the schema (`field.datetime()`).
|
|
47
47
|
*/
|
|
48
|
-
datetime: () => Validator
|
|
48
|
+
datetime: () => Validator<string, false>;
|
|
49
49
|
/**
|
|
50
50
|
* Richtext string. Same runtime validation as `v.string()`; named
|
|
51
51
|
* explicitly so server functions read as the matching schema type.
|
|
52
52
|
*/
|
|
53
|
-
richtext: () => Validator
|
|
53
|
+
richtext: () => Validator<string, false>;
|
|
54
54
|
/** ID reference to another entity. */
|
|
55
|
-
id: (table: string) => Validator
|
|
55
|
+
id: (table: string) => Validator<string, false>;
|
|
56
56
|
/** Null value. */
|
|
57
|
-
null: () => Validator
|
|
57
|
+
null: () => Validator<null, false>;
|
|
58
58
|
/** Array of values. */
|
|
59
|
-
array: (items:
|
|
59
|
+
array: <TValidator extends AnyValidator>(items: TValidator) => Validator<InferValidator<TValidator>[], false>;
|
|
60
60
|
/** Object with typed fields. */
|
|
61
|
-
object: (fields:
|
|
61
|
+
object: <TSchema extends ValidatorSchema>(fields: TSchema) => Validator<InferArgs<TSchema>, false>;
|
|
62
62
|
/** Optional value (may be omitted). */
|
|
63
|
-
optional: (inner:
|
|
63
|
+
optional: <TValidator extends AnyValidator>(inner: TValidator) => Validator<InferValidator<TValidator>, true>;
|
|
64
64
|
/** Union of multiple types. */
|
|
65
|
-
union: (...variants:
|
|
65
|
+
union: <TVariants extends AnyValidator[]>(...variants: TVariants) => Validator<InferValidator<TVariants[number]>, false>;
|
|
66
66
|
/** Exact literal value. */
|
|
67
|
-
literal:
|
|
67
|
+
literal: <const TValue extends string | number | boolean>(value: TValue) => Validator<TValue, false>;
|
|
68
68
|
/** Any valid JSON value. */
|
|
69
|
-
any: () => Validator
|
|
69
|
+
any: () => Validator<any, false>;
|
|
70
70
|
};
|
|
71
71
|
export declare function validateArgs(args: unknown, schema: Record<string, Validator>): {
|
|
72
72
|
valid: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pylonsync/functions",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.332",
|
|
4
4
|
"description": "TypeScript function runtime for pylon — defines server-side queries, mutations, and actions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"README.md"
|
|
33
33
|
],
|
|
34
34
|
"scripts": {
|
|
35
|
+
"check": "tsc --noEmit",
|
|
35
36
|
"typecheck": "tsc --noEmit",
|
|
36
37
|
"build": "tsc -p tsconfig.build.json",
|
|
37
38
|
"test": "bun test",
|
package/src/define.ts
CHANGED
|
@@ -26,15 +26,18 @@ import type {
|
|
|
26
26
|
QueryCtx,
|
|
27
27
|
MutationCtx,
|
|
28
28
|
ActionCtx,
|
|
29
|
-
|
|
29
|
+
ValidatorSchema,
|
|
30
|
+
InferArgs,
|
|
30
31
|
AuthMode,
|
|
31
32
|
} from "./types";
|
|
32
33
|
|
|
33
34
|
/** Default auth mode applied when a definition omits `auth`. */
|
|
34
35
|
const DEFAULT_AUTH: AuthMode = "user";
|
|
35
36
|
|
|
36
|
-
interface CommonDef
|
|
37
|
-
|
|
37
|
+
interface CommonDef<
|
|
38
|
+
TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined,
|
|
39
|
+
> {
|
|
40
|
+
args?: TSchema;
|
|
38
41
|
/**
|
|
39
42
|
* When true, the function is callable only via `ctx.runQuery()` /
|
|
40
43
|
* `ctx.runMutation()` / `ctx.runAction()` from another function
|
|
@@ -69,7 +72,11 @@ interface CommonDef {
|
|
|
69
72
|
timeout?: number;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
interface QueryDefRequired<
|
|
75
|
+
interface QueryDefRequired<
|
|
76
|
+
TArgs,
|
|
77
|
+
TReturn,
|
|
78
|
+
TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined,
|
|
79
|
+
> extends CommonDef<TSchema> {
|
|
73
80
|
/** Defaults to `"user"`. See [`AuthMode`] for the full surface. */
|
|
74
81
|
auth?: "user" | "admin";
|
|
75
82
|
handler: (
|
|
@@ -78,7 +85,11 @@ interface QueryDefRequired<TArgs, TReturn> extends CommonDef {
|
|
|
78
85
|
) => Promise<TReturn>;
|
|
79
86
|
}
|
|
80
87
|
|
|
81
|
-
interface QueryDefOptional<
|
|
88
|
+
interface QueryDefOptional<
|
|
89
|
+
TArgs,
|
|
90
|
+
TReturn,
|
|
91
|
+
TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined,
|
|
92
|
+
> extends CommonDef<TSchema> {
|
|
82
93
|
auth: "public" | "guest";
|
|
83
94
|
handler: (
|
|
84
95
|
ctx: QueryCtx<"optional">,
|
|
@@ -86,7 +97,11 @@ interface QueryDefOptional<TArgs, TReturn> extends CommonDef {
|
|
|
86
97
|
) => Promise<TReturn>;
|
|
87
98
|
}
|
|
88
99
|
|
|
89
|
-
interface MutationDefRequired<
|
|
100
|
+
interface MutationDefRequired<
|
|
101
|
+
TArgs,
|
|
102
|
+
TReturn,
|
|
103
|
+
TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined,
|
|
104
|
+
> extends CommonDef<TSchema> {
|
|
90
105
|
auth?: "user" | "admin";
|
|
91
106
|
handler: (
|
|
92
107
|
ctx: MutationCtx<"required">,
|
|
@@ -94,7 +109,11 @@ interface MutationDefRequired<TArgs, TReturn> extends CommonDef {
|
|
|
94
109
|
) => Promise<TReturn>;
|
|
95
110
|
}
|
|
96
111
|
|
|
97
|
-
interface MutationDefOptional<
|
|
112
|
+
interface MutationDefOptional<
|
|
113
|
+
TArgs,
|
|
114
|
+
TReturn,
|
|
115
|
+
TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined,
|
|
116
|
+
> extends CommonDef<TSchema> {
|
|
98
117
|
auth: "public" | "guest";
|
|
99
118
|
handler: (
|
|
100
119
|
ctx: MutationCtx<"optional">,
|
|
@@ -102,7 +121,11 @@ interface MutationDefOptional<TArgs, TReturn> extends CommonDef {
|
|
|
102
121
|
) => Promise<TReturn>;
|
|
103
122
|
}
|
|
104
123
|
|
|
105
|
-
interface ActionDefRequired<
|
|
124
|
+
interface ActionDefRequired<
|
|
125
|
+
TArgs,
|
|
126
|
+
TReturn,
|
|
127
|
+
TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined,
|
|
128
|
+
> extends CommonDef<TSchema> {
|
|
106
129
|
auth?: "user" | "admin";
|
|
107
130
|
handler: (
|
|
108
131
|
ctx: ActionCtx<"required">,
|
|
@@ -110,7 +133,11 @@ interface ActionDefRequired<TArgs, TReturn> extends CommonDef {
|
|
|
110
133
|
) => Promise<TReturn>;
|
|
111
134
|
}
|
|
112
135
|
|
|
113
|
-
interface ActionDefOptional<
|
|
136
|
+
interface ActionDefOptional<
|
|
137
|
+
TArgs,
|
|
138
|
+
TReturn,
|
|
139
|
+
TSchema extends ValidatorSchema | undefined = ValidatorSchema | undefined,
|
|
140
|
+
> extends CommonDef<TSchema> {
|
|
114
141
|
auth: "public" | "guest";
|
|
115
142
|
handler: (
|
|
116
143
|
ctx: ActionCtx<"optional">,
|
|
@@ -150,6 +177,26 @@ interface ActionDefOptional<TArgs, TReturn> extends CommonDef {
|
|
|
150
177
|
* });
|
|
151
178
|
* ```
|
|
152
179
|
*/
|
|
180
|
+
export function query<
|
|
181
|
+
const TSchema extends ValidatorSchema,
|
|
182
|
+
TReturn,
|
|
183
|
+
TAuth extends "user" | "admin" | undefined,
|
|
184
|
+
>(
|
|
185
|
+
def: QueryDefRequired<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
186
|
+
args: TSchema;
|
|
187
|
+
auth?: TAuth;
|
|
188
|
+
},
|
|
189
|
+
): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
190
|
+
export function query<
|
|
191
|
+
const TSchema extends ValidatorSchema,
|
|
192
|
+
TReturn,
|
|
193
|
+
TAuth extends "public" | "guest",
|
|
194
|
+
>(
|
|
195
|
+
def: QueryDefOptional<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
196
|
+
args: TSchema;
|
|
197
|
+
auth: TAuth;
|
|
198
|
+
},
|
|
199
|
+
): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
153
200
|
export function query<TArgs = Record<string, unknown>, TReturn = unknown>(
|
|
154
201
|
def: QueryDefRequired<TArgs, TReturn>,
|
|
155
202
|
): FnDefinition<TArgs, TReturn>;
|
|
@@ -200,6 +247,26 @@ export function query<TArgs, TReturn>(
|
|
|
200
247
|
* });
|
|
201
248
|
* ```
|
|
202
249
|
*/
|
|
250
|
+
export function mutation<
|
|
251
|
+
const TSchema extends ValidatorSchema,
|
|
252
|
+
TReturn,
|
|
253
|
+
TAuth extends "user" | "admin" | undefined,
|
|
254
|
+
>(
|
|
255
|
+
def: MutationDefRequired<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
256
|
+
args: TSchema;
|
|
257
|
+
auth?: TAuth;
|
|
258
|
+
},
|
|
259
|
+
): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
260
|
+
export function mutation<
|
|
261
|
+
const TSchema extends ValidatorSchema,
|
|
262
|
+
TReturn,
|
|
263
|
+
TAuth extends "public" | "guest",
|
|
264
|
+
>(
|
|
265
|
+
def: MutationDefOptional<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
266
|
+
args: TSchema;
|
|
267
|
+
auth: TAuth;
|
|
268
|
+
},
|
|
269
|
+
): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
203
270
|
export function mutation<TArgs = Record<string, unknown>, TReturn = unknown>(
|
|
204
271
|
def: MutationDefRequired<TArgs, TReturn>,
|
|
205
272
|
): FnDefinition<TArgs, TReturn>;
|
|
@@ -265,6 +332,26 @@ export function mutation<TArgs, TReturn>(
|
|
|
265
332
|
* });
|
|
266
333
|
* ```
|
|
267
334
|
*/
|
|
335
|
+
export function action<
|
|
336
|
+
const TSchema extends ValidatorSchema,
|
|
337
|
+
TReturn,
|
|
338
|
+
TAuth extends "user" | "admin" | undefined,
|
|
339
|
+
>(
|
|
340
|
+
def: ActionDefRequired<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
341
|
+
args: TSchema;
|
|
342
|
+
auth?: TAuth;
|
|
343
|
+
},
|
|
344
|
+
): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
345
|
+
export function action<
|
|
346
|
+
const TSchema extends ValidatorSchema,
|
|
347
|
+
TReturn,
|
|
348
|
+
TAuth extends "public" | "guest",
|
|
349
|
+
>(
|
|
350
|
+
def: ActionDefOptional<InferArgs<TSchema>, TReturn, TSchema> & {
|
|
351
|
+
args: TSchema;
|
|
352
|
+
auth: TAuth;
|
|
353
|
+
},
|
|
354
|
+
): FnDefinition<InferArgs<TSchema>, TReturn>;
|
|
268
355
|
export function action<TArgs = Record<string, unknown>, TReturn = unknown>(
|
|
269
356
|
def: ActionDefRequired<TArgs, TReturn>,
|
|
270
357
|
): FnDefinition<TArgs, TReturn>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { action, mutation, query } from "./define";
|
|
4
|
+
import type { FnDefinition } from "./types";
|
|
5
|
+
import { v } from "./validators";
|
|
6
|
+
|
|
7
|
+
type Equal<TLeft, TRight> =
|
|
8
|
+
(<T>() => T extends TLeft ? 1 : 2) extends
|
|
9
|
+
(<T>() => T extends TRight ? 1 : 2)
|
|
10
|
+
? true
|
|
11
|
+
: false;
|
|
12
|
+
type Expect<T extends true> = T;
|
|
13
|
+
type ArgsOf<TDefinition> =
|
|
14
|
+
TDefinition extends FnDefinition<infer TArgs, unknown> ? TArgs : never;
|
|
15
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
16
|
+
|
|
17
|
+
const inferredMutation = mutation({
|
|
18
|
+
args: {
|
|
19
|
+
required: v.string(),
|
|
20
|
+
optional: v.optional(v.number()),
|
|
21
|
+
list: v.array(v.int()),
|
|
22
|
+
nested: v.object({
|
|
23
|
+
enabled: v.boolean(),
|
|
24
|
+
label: v.optional(v.string()),
|
|
25
|
+
}),
|
|
26
|
+
union: v.union(v.literal("open"), v.literal(404), v.null()),
|
|
27
|
+
literal: v.literal("fixed"),
|
|
28
|
+
anything: v.any(),
|
|
29
|
+
},
|
|
30
|
+
async handler(_ctx, args) {
|
|
31
|
+
const required: string = args.required;
|
|
32
|
+
const optional: number | undefined = args.optional;
|
|
33
|
+
const list: number[] = args.list;
|
|
34
|
+
const nested: { enabled: boolean; label?: string } = args.nested;
|
|
35
|
+
const union: "open" | 404 | null = args.union;
|
|
36
|
+
const literal: "fixed" = args.literal;
|
|
37
|
+
const anything: any = args.anything;
|
|
38
|
+
|
|
39
|
+
return { required, optional, list, nested, union, literal, anything };
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
type InferredArgs = ArgsOf<typeof inferredMutation>;
|
|
44
|
+
type _RequiredValue = Expect<Equal<InferredArgs["required"], string>>;
|
|
45
|
+
type _RequiredKey = Expect<
|
|
46
|
+
Equal<{} extends Pick<InferredArgs, "required"> ? true : false, false>
|
|
47
|
+
>;
|
|
48
|
+
type _OptionalValue = Expect<
|
|
49
|
+
Equal<InferredArgs["optional"], number | undefined>
|
|
50
|
+
>;
|
|
51
|
+
type _OptionalKey = Expect<
|
|
52
|
+
Equal<{} extends Pick<InferredArgs, "optional"> ? true : false, true>
|
|
53
|
+
>;
|
|
54
|
+
type _Array = Expect<Equal<InferredArgs["list"], number[]>>;
|
|
55
|
+
type _ObjectRequired = Expect<
|
|
56
|
+
Equal<InferredArgs["nested"]["enabled"], boolean>
|
|
57
|
+
>;
|
|
58
|
+
type _ObjectOptional = Expect<
|
|
59
|
+
Equal<InferredArgs["nested"]["label"], string | undefined>
|
|
60
|
+
>;
|
|
61
|
+
type _Union = Expect<Equal<InferredArgs["union"], "open" | 404 | null>>;
|
|
62
|
+
type _Literal = Expect<Equal<InferredArgs["literal"], "fixed">>;
|
|
63
|
+
type _Any = Expect<IsAny<InferredArgs["anything"]>>;
|
|
64
|
+
|
|
65
|
+
const publicQuery = query({
|
|
66
|
+
auth: "public",
|
|
67
|
+
args: { id: v.id("Item") },
|
|
68
|
+
async handler(ctx, args) {
|
|
69
|
+
const userId: string | null = ctx.auth.userId;
|
|
70
|
+
const id: string = args.id;
|
|
71
|
+
return { id, userId };
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const guestAction = action({
|
|
76
|
+
auth: "guest",
|
|
77
|
+
args: { mode: v.union(v.literal("fast"), v.literal("safe")) },
|
|
78
|
+
async handler(ctx, args) {
|
|
79
|
+
const userId: string | null = ctx.auth.userId;
|
|
80
|
+
const mode: "fast" | "safe" = args.mode;
|
|
81
|
+
return { mode, userId };
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
type ExplicitArgs = { name: string; note?: string };
|
|
86
|
+
type ExplicitReturn = { ok: boolean };
|
|
87
|
+
|
|
88
|
+
const explicitMutation = mutation<ExplicitArgs, ExplicitReturn>({
|
|
89
|
+
args: {
|
|
90
|
+
name: v.string(),
|
|
91
|
+
note: v.optional(v.string()),
|
|
92
|
+
},
|
|
93
|
+
async handler(_ctx, args) {
|
|
94
|
+
const explicit: ExplicitArgs = args;
|
|
95
|
+
return { ok: explicit.name.length > 0 };
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
type _ExplicitArgs = Expect<
|
|
100
|
+
Equal<ArgsOf<typeof explicitMutation>, ExplicitArgs>
|
|
101
|
+
>;
|
|
102
|
+
|
|
103
|
+
test("validator schemas remain available at runtime", () => {
|
|
104
|
+
expect(inferredMutation.args?.required.type).toBe("string");
|
|
105
|
+
expect(inferredMutation.args?.optional.optional).toBe(true);
|
|
106
|
+
expect(publicQuery.auth).toBe("public");
|
|
107
|
+
expect(guestAction.auth).toBe("guest");
|
|
108
|
+
expect(explicitMutation.type).toBe("mutation");
|
|
109
|
+
});
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -658,9 +658,23 @@ export interface FnDefinition<TArgs = unknown, TReturn = unknown> {
|
|
|
658
658
|
// Validators
|
|
659
659
|
// ---------------------------------------------------------------------------
|
|
660
660
|
|
|
661
|
-
|
|
661
|
+
declare const validatorType: unique symbol;
|
|
662
|
+
declare const validatorOptional: unique symbol;
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Runtime validator carrying its validated TypeScript type.
|
|
666
|
+
*
|
|
667
|
+
* The symbol properties are type-only markers: they emit no runtime data and
|
|
668
|
+
* let the function constructors derive handler arguments from an `args` schema.
|
|
669
|
+
*/
|
|
670
|
+
export interface Validator<
|
|
671
|
+
T = unknown,
|
|
672
|
+
TOptional extends boolean = boolean,
|
|
673
|
+
> {
|
|
662
674
|
type: string;
|
|
663
675
|
optional?: boolean;
|
|
676
|
+
readonly [validatorType]?: T;
|
|
677
|
+
readonly [validatorOptional]?: TOptional;
|
|
664
678
|
/** For v.id("tableName") */
|
|
665
679
|
table?: string;
|
|
666
680
|
/** For v.array(v.string()) */
|
|
@@ -672,3 +686,31 @@ export interface Validator {
|
|
|
672
686
|
/** For v.literal("value") */
|
|
673
687
|
value?: unknown;
|
|
674
688
|
}
|
|
689
|
+
|
|
690
|
+
/** Any validator accepted in an argument schema. */
|
|
691
|
+
export type AnyValidator = Validator<any, boolean>;
|
|
692
|
+
|
|
693
|
+
/** A function argument schema keyed by argument name. */
|
|
694
|
+
export type ValidatorSchema = Record<string, AnyValidator>;
|
|
695
|
+
|
|
696
|
+
/** Extract the value accepted by a validator. */
|
|
697
|
+
export type InferValidator<TValidator extends AnyValidator> =
|
|
698
|
+
TValidator extends Validator<infer TValue, boolean> ? TValue : never;
|
|
699
|
+
|
|
700
|
+
type OptionalValidatorKeys<TSchema extends ValidatorSchema> = {
|
|
701
|
+
[TKey in keyof TSchema]-?: TSchema[TKey] extends Validator<any, true>
|
|
702
|
+
? TKey
|
|
703
|
+
: never;
|
|
704
|
+
}[keyof TSchema];
|
|
705
|
+
|
|
706
|
+
type RequiredValidatorKeys<TSchema extends ValidatorSchema> = Exclude<
|
|
707
|
+
keyof TSchema,
|
|
708
|
+
OptionalValidatorKeys<TSchema>
|
|
709
|
+
>;
|
|
710
|
+
|
|
711
|
+
/** Derive a handler argument object from a validator schema. */
|
|
712
|
+
export type InferArgs<TSchema extends ValidatorSchema> = {
|
|
713
|
+
[TKey in RequiredValidatorKeys<TSchema>]: InferValidator<TSchema[TKey]>;
|
|
714
|
+
} & {
|
|
715
|
+
[TKey in OptionalValidatorKeys<TSchema>]?: InferValidator<TSchema[TKey]>;
|
|
716
|
+
};
|
package/src/validators.ts
CHANGED
|
@@ -22,75 +22,99 @@
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import type {
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
import type {
|
|
26
|
+
AnyValidator,
|
|
27
|
+
InferArgs,
|
|
28
|
+
InferValidator,
|
|
29
|
+
Validator,
|
|
30
|
+
ValidatorSchema,
|
|
31
|
+
} from "./types";
|
|
32
|
+
|
|
33
|
+
function validator<T>(
|
|
34
|
+
type: string,
|
|
35
|
+
extra?: Partial<Validator<T, false>>,
|
|
36
|
+
): Validator<T, false> {
|
|
28
37
|
return { type, ...extra };
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
export const v = {
|
|
32
41
|
/** String value. */
|
|
33
|
-
string: (): Validator => validator("string"),
|
|
42
|
+
string: (): Validator<string, false> => validator("string"),
|
|
34
43
|
|
|
35
44
|
/** Number (float64). Same as `v.float()`. */
|
|
36
|
-
number: (): Validator => validator("number"),
|
|
45
|
+
number: (): Validator<number, false> => validator("number"),
|
|
37
46
|
|
|
38
47
|
/**
|
|
39
48
|
* 64-bit float. Alias for `v.number()` so the validator API matches the
|
|
40
49
|
* schema DSL (which uses `field.float()`). Prefer this in new code.
|
|
41
50
|
*/
|
|
42
|
-
float: (): Validator => validator("number"),
|
|
51
|
+
float: (): Validator<number, false> => validator("number"),
|
|
43
52
|
|
|
44
53
|
/** Integer. */
|
|
45
|
-
int: (): Validator => validator("int"),
|
|
54
|
+
int: (): Validator<number, false> => validator("int"),
|
|
46
55
|
|
|
47
56
|
/** Boolean. Same as `v.bool()`. */
|
|
48
|
-
boolean: (): Validator => validator("boolean"),
|
|
57
|
+
boolean: (): Validator<boolean, false> => validator("boolean"),
|
|
49
58
|
|
|
50
59
|
/**
|
|
51
60
|
* Boolean. Alias for `v.boolean()` so the validator API matches the
|
|
52
61
|
* schema DSL (which uses `field.bool()`). Prefer this in new code.
|
|
53
62
|
*/
|
|
54
|
-
bool: (): Validator => validator("boolean"),
|
|
63
|
+
bool: (): Validator<boolean, false> => validator("boolean"),
|
|
55
64
|
|
|
56
65
|
/**
|
|
57
66
|
* ISO-8601 datetime string. Validates the shape of a string value; the
|
|
58
67
|
* stored column type comes from the schema (`field.datetime()`).
|
|
59
68
|
*/
|
|
60
|
-
datetime: (): Validator => validator("string"),
|
|
69
|
+
datetime: (): Validator<string, false> => validator("string"),
|
|
61
70
|
|
|
62
71
|
/**
|
|
63
72
|
* Richtext string. Same runtime validation as `v.string()`; named
|
|
64
73
|
* explicitly so server functions read as the matching schema type.
|
|
65
74
|
*/
|
|
66
|
-
richtext: (): Validator => validator("string"),
|
|
75
|
+
richtext: (): Validator<string, false> => validator("string"),
|
|
67
76
|
|
|
68
77
|
/** ID reference to another entity. */
|
|
69
|
-
id: (table: string): Validator
|
|
78
|
+
id: (table: string): Validator<string, false> =>
|
|
79
|
+
validator("id", { table }),
|
|
70
80
|
|
|
71
81
|
/** Null value. */
|
|
72
|
-
null: (): Validator => validator("null"),
|
|
82
|
+
null: (): Validator<null, false> => validator("null"),
|
|
73
83
|
|
|
74
84
|
/** Array of values. */
|
|
75
|
-
array:
|
|
85
|
+
array: <TValidator extends AnyValidator>(
|
|
86
|
+
items: TValidator,
|
|
87
|
+
): Validator<InferValidator<TValidator>[], false> =>
|
|
88
|
+
validator("array", { items }),
|
|
76
89
|
|
|
77
90
|
/** Object with typed fields. */
|
|
78
|
-
object:
|
|
91
|
+
object: <TSchema extends ValidatorSchema>(
|
|
92
|
+
fields: TSchema,
|
|
93
|
+
): Validator<InferArgs<TSchema>, false> =>
|
|
79
94
|
validator("object", { fields }),
|
|
80
95
|
|
|
81
96
|
/** Optional value (may be omitted). */
|
|
82
|
-
optional:
|
|
97
|
+
optional: <TValidator extends AnyValidator>(
|
|
98
|
+
inner: TValidator,
|
|
99
|
+
): Validator<InferValidator<TValidator>, true> =>
|
|
100
|
+
({
|
|
101
|
+
...inner,
|
|
102
|
+
optional: true,
|
|
103
|
+
}) as Validator<InferValidator<TValidator>, true>,
|
|
83
104
|
|
|
84
105
|
/** Union of multiple types. */
|
|
85
|
-
union:
|
|
106
|
+
union: <TVariants extends AnyValidator[]>(
|
|
107
|
+
...variants: TVariants
|
|
108
|
+
): Validator<InferValidator<TVariants[number]>, false> =>
|
|
86
109
|
validator("union", { variants }),
|
|
87
110
|
|
|
88
111
|
/** Exact literal value. */
|
|
89
|
-
literal:
|
|
90
|
-
|
|
112
|
+
literal: <const TValue extends string | number | boolean>(
|
|
113
|
+
value: TValue,
|
|
114
|
+
): Validator<TValue, false> => validator("literal", { value }),
|
|
91
115
|
|
|
92
116
|
/** Any valid JSON value. */
|
|
93
|
-
any: (): Validator => validator("any"),
|
|
117
|
+
any: (): Validator<any, false> => validator("any"),
|
|
94
118
|
};
|
|
95
119
|
|
|
96
120
|
// ---------------------------------------------------------------------------
|