better-call 0.0.0-experimental.06264e12

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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +254 -0
  3. package/dist/error.cjs +67 -0
  4. package/dist/error.cjs.map +1 -0
  5. package/dist/error.d.cts +46 -0
  6. package/dist/error.d.mts +46 -0
  7. package/dist/error.mjs +64 -0
  8. package/dist/error.mjs.map +1 -0
  9. package/dist/fn.cjs +335 -0
  10. package/dist/fn.cjs.map +1 -0
  11. package/dist/fn.d.cts +312 -0
  12. package/dist/fn.d.mts +312 -0
  13. package/dist/fn.mjs +335 -0
  14. package/dist/fn.mjs.map +1 -0
  15. package/dist/index.cjs +37 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +46 -0
  18. package/dist/index.d.mts +46 -0
  19. package/dist/index.mjs +24 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/module.cjs +111 -0
  22. package/dist/module.cjs.map +1 -0
  23. package/dist/module.d.cts +249 -0
  24. package/dist/module.d.mts +249 -0
  25. package/dist/module.mjs +102 -0
  26. package/dist/module.mjs.map +1 -0
  27. package/dist/plugins/http.cjs +185 -0
  28. package/dist/plugins/http.cjs.map +1 -0
  29. package/dist/plugins/http.d.cts +1261 -0
  30. package/dist/plugins/http.d.mts +1261 -0
  31. package/dist/plugins/http.mjs +175 -0
  32. package/dist/plugins/http.mjs.map +1 -0
  33. package/dist/plugins/read-only.cjs +19 -0
  34. package/dist/plugins/read-only.cjs.map +1 -0
  35. package/dist/plugins/read-only.d.cts +17 -0
  36. package/dist/plugins/read-only.d.mts +17 -0
  37. package/dist/plugins/read-only.mjs +19 -0
  38. package/dist/plugins/read-only.mjs.map +1 -0
  39. package/dist/schema.cjs +167 -0
  40. package/dist/schema.cjs.map +1 -0
  41. package/dist/schema.d.cts +225 -0
  42. package/dist/schema.d.mts +225 -0
  43. package/dist/schema.mjs +159 -0
  44. package/dist/schema.mjs.map +1 -0
  45. package/dist/scope.d.cts +18 -0
  46. package/dist/scope.d.mts +18 -0
  47. package/dist/storage.cjs +256 -0
  48. package/dist/storage.cjs.map +1 -0
  49. package/dist/storage.d.cts +195 -0
  50. package/dist/storage.d.mts +195 -0
  51. package/dist/storage.mjs +253 -0
  52. package/dist/storage.mjs.map +1 -0
  53. package/dist/types.d.cts +8 -0
  54. package/dist/types.d.mts +8 -0
  55. package/dist/var.cjs +162 -0
  56. package/dist/var.cjs.map +1 -0
  57. package/dist/var.d.cts +36 -0
  58. package/dist/var.d.mts +36 -0
  59. package/dist/var.mjs +154 -0
  60. package/dist/var.mjs.map +1 -0
  61. package/package.json +92 -0
@@ -0,0 +1,225 @@
1
+ import { LiteralString, Prettify } from "./types.cjs";
2
+ //#region src/schema.d.ts
3
+ type Rules = {
4
+ /** Strings: length. Numbers: value. */
5
+ min?: number;
6
+ /** Strings: length. Numbers: value. */
7
+ max?: number;
8
+ /** Strings only: exact length. */
9
+ length?: number;
10
+ regex?: RegExp;
11
+ email?: boolean;
12
+ url?: boolean;
13
+ startsWith?: string;
14
+ endsWith?: string;
15
+ /** Numbers only. */
16
+ int?: boolean;
17
+ /** Allowed values. */
18
+ enum?: readonly unknown[];
19
+ /** Escape hatch - return true, or a message to fail with. */
20
+ check?: (value: any) => boolean | string;
21
+ };
22
+ interface TypeDefination<T, O, D = never> extends Rules {
23
+ name: LiteralString;
24
+ type?: T;
25
+ output?: O;
26
+ shape?: unknown;
27
+ /** `function` types only: the declared input of the expected fn -
28
+ * plain closures get it validated at their door on every call. */
29
+ fnInput?: unknown;
30
+ /** Used when the incoming value is `undefined`. */
31
+ default?: D;
32
+ /** When true, `undefined` passes straight through unvalidated. */
33
+ optional?: boolean;
34
+ transform?: (value: any) => O;
35
+ }
36
+ type TypeOptions<T, O> = {
37
+ transform?: (value: T) => O;
38
+ };
39
+ type WithDefault<D> = {
40
+ default?: D;
41
+ };
42
+ type WithOptional<Opt> = {
43
+ optional?: Opt;
44
+ };
45
+ /**
46
+ * `optional` widens the output; `default` keeps it narrow because a value
47
+ * is always produced. Declaring both means optional to send, never absent.
48
+ */
49
+ type OutOf<O, D, Opt> = [Opt] extends [true] ? [D] extends [never] ? O | undefined : O : O;
50
+ /** Either marker makes the key omittable in `InferArgs`. */
51
+ type DefOf<D, Opt> = [Opt] extends [true] ? [D] extends [never] ? undefined : D : D;
52
+ type StringOptions<E extends string, O> = TypeOptions<E, O> & Pick<Rules, "min" | "max" | "length" | "regex" | "email" | "url" | "startsWith" | "endsWith" | "check"> & {
53
+ enum?: readonly E[];
54
+ };
55
+ type ArrayOptions<E, O> = TypeOptions<FieldOut<E>[], O> & Pick<Rules, "min" | "max" | "length" | "check">;
56
+ type NumberOptions<O> = TypeOptions<number, O> & Pick<Rules, "min" | "max" | "int" | "check"> & {
57
+ enum?: readonly number[];
58
+ };
59
+ type InferType<T> = T extends TypeDefination<infer T2, any, any> ? T2 : never;
60
+ type InferOutput<T> = T extends TypeDefination<any, infer O, any> ? O : never;
61
+ type DefineInput<I> = Prettify<{ [K in keyof I]: FieldIn<I[K]>; }>;
62
+ /** Keys whose field is OPTIONAL with no default: absent from the output
63
+ * too, so they mark `?`. A defaulted field always produces a value and
64
+ * stays required. */
65
+ type OutOptional<O> = { [K in keyof O]: [DefaultOf<O[K]>] extends [never] ? never : [DefaultOf<O[K]>] extends [undefined] ? K : never; }[keyof O];
66
+ type DefineOutput<O> = Prettify<{ [K in keyof O as K extends OutOptional<O> ? never : K]: FieldOut<O[K]>; } & { [K in keyof O as K extends OutOptional<O> ? K : never]?: FieldOut<O[K]>; }>;
67
+ /**
68
+ * A handler-less `v.fn({ input, output })` used as a schema describes
69
+ * "a fn from `input` to `output`" - the VALUE is the fn itself. Two
70
+ * views of the same signature:
71
+ *
72
+ * `SchemaFnIn` is the PROVIDER's side - what a caller must hand over.
73
+ * Like any handler, their fn receives the PARSED input (validation runs
74
+ * at its door) and returns the declared output, sync or async.
75
+ *
76
+ * `SchemaFnOut` is the CONSUMER's side (`c.input.x`) - the handler calls
77
+ * it with RAW args, exactly like calling the fn it stands in for.
78
+ *
79
+ * No declared input means the signature is UNSPECIFIED, not zero-arg -
80
+ * any fn fits (`create: v.fn`), so the args stay open.
81
+ */
82
+ type SchemaFnIn<FI, FO> = (...args: unknown extends FI ? any[] : [input: InferInput<FI>]) => unknown extends FO ? any : InferInput<OutputSchemaOf<FO>> | Promise<InferInput<OutputSchemaOf<FO>>>;
83
+ type SchemaFnOut<FI, FO> = (...args: unknown extends FI ? any[] : [input: InferArgs<FI>]) => unknown extends FO ? any : InferInput<OutputSchemaOf<FO>> | Promise<InferInput<OutputSchemaOf<FO>>>;
84
+ /**
85
+ * A fn schema whose input IS a var carries that var's name as an optional
86
+ * phantom (`$fnVar`, tuple-wrapped so a plain fn can never false-match).
87
+ * Scope resolution reads it to WIDEN the fn's args with everything the
88
+ * scope mounts on that var - see `WidenSchemaFns`. Optional, so any plain
89
+ * closure still satisfies the type.
90
+ */
91
+ type FnVarBrand<FI> = FI extends {
92
+ $var: true;
93
+ name: infer N extends string;
94
+ } ? {
95
+ readonly $fnVar?: [N];
96
+ } : unknown;
97
+ /**
98
+ * A declared `output` comes in two forms: a bare schema (the signature
99
+ * AND the exit check), or the wrapper `{ def?, validation? }` splitting
100
+ * what the fn PROMISES from what gets CHECKED - `{ def }` documents
101
+ * without paying runtime validation, `{ def, validation }` checks with a
102
+ * different (usually looser) schema than it documents. The wrapper is
103
+ * recognized by its keys, so an output that IS an object with only
104
+ * `def`/`validation` fields must be written `v.object({...})`.
105
+ *
106
+ * `OutputSchemaOf` is the type-level unwrap - the schema the fn's return
107
+ * type (and its rendered signature) comes from.
108
+ */
109
+ type OutputSchemaOf<O> = Exclude<keyof O, "def" | "validation"> extends never ? O extends {
110
+ def: infer D;
111
+ } ? D : O extends {
112
+ validation: infer Vl;
113
+ } ? Vl : O : O;
114
+ /** The runtime unwrap: `def` is the documented schema (falls back to
115
+ * `validation`), `validation` is what the exit check runs - undefined
116
+ * means no check. A bare schema is both. */
117
+ declare const outputContract: (output: unknown) => {
118
+ def?: unknown;
119
+ validation?: unknown;
120
+ };
121
+ /**
122
+ * One input field, in four flavours:
123
+ * - a `v.var()`, whose shape comes from the var's own `schema`
124
+ * - a handler-less `v.fn(...)` builder, which types the field as a FN
125
+ * - a type from `v.string()` / `v.object()` / ...
126
+ * - a bare nested record, which recurses
127
+ *
128
+ * The record case has to come last: a TypeDefination is itself a record,
129
+ * and so is a builder.
130
+ */
131
+ type FieldOut<F> = F extends {
132
+ $var: true;
133
+ schema?: infer S;
134
+ } ? InferInput<NonNullable<S>> : F extends {
135
+ $fnSchema: {
136
+ input?: infer FI;
137
+ output?: infer FO;
138
+ };
139
+ } ? SchemaFnOut<FI, FO> & FnVarBrand<FI> : F extends TypeDefination<any, infer O, any> ? O : F extends Record<string, unknown> ? Prettify<{ [K in keyof F]: FieldOut<F[K]>; }> : never;
140
+ type FieldIn<F> = F extends {
141
+ $var: true;
142
+ schema?: infer S;
143
+ } ? InferArgs<NonNullable<S>> : F extends {
144
+ $fnSchema: {
145
+ input?: infer FI;
146
+ output?: infer FO;
147
+ };
148
+ } ? SchemaFnIn<FI, FO> & FnVarBrand<FI> : F extends TypeDefination<infer T, any, any> ? T : F extends Record<string, unknown> ? ArgsShape<F> : never;
149
+ /**
150
+ * A field's declared default, looked through a var to its schema. Only a
151
+ * TYPE's default counts - a var's own default is its initial value, not a
152
+ * licence to omit the input. The `$fnSchema` guard mirrors `asType`'s
153
+ * ordering: a bare `v.fn` is CALLABLE, and any callable duck-matches
154
+ * TypeDefination (`.name` comes with every function), which would read a
155
+ * phantom default off it and wrongly mark the field optional.
156
+ */
157
+ type DefaultOf<F> = F extends {
158
+ $var: true;
159
+ schema?: infer S;
160
+ } ? NonNullable<S> extends TypeDefination<any, any, infer D> ? D : never : F extends {
161
+ $fnSchema: unknown;
162
+ } ? never : F extends TypeDefination<any, any, infer D> ? D : never;
163
+ type Defaulted<I> = { [K in keyof I]: [DefaultOf<I[K]>] extends [never] ? never : K; }[keyof I];
164
+ /** Defaulted keys are optional to send, but always present in the handler. */
165
+ type ArgsShape<I> = Prettify<{ [K in keyof I as K extends Defaulted<I> ? never : K]: FieldIn<I[K]>; } & { [K in keyof I as K extends Defaulted<I> ? K : never]?: FieldIn<I[K]>; }>;
166
+ /**
167
+ * Post-transform shape - what a handler sees. The `$var` branch must come
168
+ * first: a var's `name` property duck-matches TypeDefination, and falling
169
+ * into that branch reads the var's VALUE type instead of its schema. A
170
+ * TUPLE input maps position by position - the fn takes that many args.
171
+ */
172
+ type InferInput<I> = I extends {
173
+ $var: true;
174
+ schema?: infer S;
175
+ } ? InferInput<NonNullable<S>> : I extends {
176
+ $fnSchema: {
177
+ input?: infer FI;
178
+ output?: infer FO;
179
+ };
180
+ } ? SchemaFnOut<FI, FO> & FnVarBrand<FI> : I extends readonly unknown[] ? { -readonly [K in keyof I]: InferInput<I[K]>; } : I extends TypeDefination<any, infer O, any> ? O : Prettify<{ [K in keyof I]: FieldOut<I[K]>; }>;
181
+ /** Pre-transform shape - what a caller sends. Same branch order. */
182
+ type InferArgs<I> = I extends {
183
+ $var: true;
184
+ schema?: infer S;
185
+ } ? InferArgs<NonNullable<S>> : I extends {
186
+ $fnSchema: {
187
+ input?: infer FI;
188
+ output?: infer FO;
189
+ };
190
+ } ? SchemaFnIn<FI, FO> & FnVarBrand<FI> : I extends readonly unknown[] ? { -readonly [K in keyof I]: InferArgs<I[K]>; } : I extends TypeDefination<infer T, any, any> ? T : ArgsShape<I>;
191
+ declare const isType: (value: any) => value is TypeDefination<any, any>;
192
+ /** A handler-less `v.fn(...)` builder doubles as a schema: the value it
193
+ * describes is a FN with the declared signature. The builder FN itself is
194
+ * branded too, so bare `v.fn` reads as "any function". */
195
+ declare const isFnSchema: (value: any) => value is {
196
+ $fnSchema: {
197
+ input?: unknown;
198
+ output?: unknown;
199
+ };
200
+ };
201
+ declare const asType: (value: any) => TypeDefination<any, any>;
202
+ declare const typeOf: (value: unknown) => "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "null" | "array" | "NaN";
203
+ declare const isVar: (value: any) => boolean;
204
+ declare const validate: (def: TypeDefination<any, any, any>, value: unknown, path: string) => any;
205
+ declare const vTypes: {
206
+ /** An `enum` narrows both sides to the literal union: `v.string({
207
+ * enum: ["a", "b"] })` types as `"a" | "b"`, not `string`. */
208
+ string: <const E extends string = string, O = E, D = never, Opt extends boolean = false>(options?: StringOptions<E, O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<E, OutOf<O, D, Opt>, DefOf<D, Opt>>;
209
+ number: <O = number, D = never, Opt extends boolean = false>(options?: NumberOptions<O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<number, OutOf<O, D, Opt>, DefOf<D, Opt>>;
210
+ boolean: <O = boolean, D = never, Opt extends boolean = false>(options?: TypeOptions<boolean, O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<boolean, OutOf<O, D, Opt>, DefOf<D, Opt>>;
211
+ /** A Date INSTANCE - checked with `instanceof`, never parsed. */
212
+ date: <O = Date, D = never, Opt extends boolean = false>(options?: TypeOptions<Date, O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<Date, OutOf<O, D, Opt>, DefOf<D, Opt>>;
213
+ /** Passthrough - validated as-is, never coerced or stripped. */
214
+ any: <T = unknown, D = never, Opt extends boolean = false>(options?: TypeOptions<T, T> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<T, OutOf<T, D, Opt>, DefOf<D, Opt>>;
215
+ /** With a SHAPE every field validates; with NO shape (`v.object()`)
216
+ * any object passes, as-is. */
217
+ object: <S = undefined, O = { [K_1 in keyof S as K_1 extends OutOptional<S> ? never : K_1]: FieldOut<S[K_1]>; } & { [K_2 in keyof S as K_2 extends OutOptional<S> ? K_2 : never]?: FieldOut<S[K_2]> | undefined; } extends (infer T) ? { [K in keyof T]: T[K]; } : never, D = never, Opt extends boolean = false>(shape?: S, options?: TypeOptions<DefineOutput<S>, O> & WithDefault<D> & WithOptional<Opt>) => [S] extends [undefined] ? TypeDefination<Record<string, any>, Record<string, any>> : TypeDefination<ArgsShape<S>, OutOf<O, D, Opt>, DefOf<D, Opt>>;
218
+ /** With an ELEMENT every item validates - all failures report
219
+ * together, like object fields; with NO element (`v.array()`) any
220
+ * array passes, as-is. `min`/`max`/`length` count items. */
221
+ array: <E = undefined, O = FieldOut<E>[], D = never, Opt extends boolean = false>(element?: E, options?: ArrayOptions<E, O> & WithDefault<D> & WithOptional<Opt>) => [E] extends [undefined] ? TypeDefination<any[], OutOf<any[], D, Opt>, DefOf<D, Opt>> : TypeDefination<FieldIn<E>[], OutOf<O, D, Opt>, DefOf<D, Opt>>;
222
+ };
223
+ //#endregion
224
+ export { DefineInput, DefineOutput, FnVarBrand, InferArgs, InferInput, InferOutput, InferType, OutputSchemaOf, Rules, TypeDefination, TypeOptions, asType, isFnSchema, isType, isVar, outputContract, typeOf, vTypes, validate };
225
+ //# sourceMappingURL=schema.d.cts.map
@@ -0,0 +1,225 @@
1
+ import { LiteralString, Prettify } from "./types.mjs";
2
+ //#region src/schema.d.ts
3
+ type Rules = {
4
+ /** Strings: length. Numbers: value. */
5
+ min?: number;
6
+ /** Strings: length. Numbers: value. */
7
+ max?: number;
8
+ /** Strings only: exact length. */
9
+ length?: number;
10
+ regex?: RegExp;
11
+ email?: boolean;
12
+ url?: boolean;
13
+ startsWith?: string;
14
+ endsWith?: string;
15
+ /** Numbers only. */
16
+ int?: boolean;
17
+ /** Allowed values. */
18
+ enum?: readonly unknown[];
19
+ /** Escape hatch - return true, or a message to fail with. */
20
+ check?: (value: any) => boolean | string;
21
+ };
22
+ interface TypeDefination<T, O, D = never> extends Rules {
23
+ name: LiteralString;
24
+ type?: T;
25
+ output?: O;
26
+ shape?: unknown;
27
+ /** `function` types only: the declared input of the expected fn -
28
+ * plain closures get it validated at their door on every call. */
29
+ fnInput?: unknown;
30
+ /** Used when the incoming value is `undefined`. */
31
+ default?: D;
32
+ /** When true, `undefined` passes straight through unvalidated. */
33
+ optional?: boolean;
34
+ transform?: (value: any) => O;
35
+ }
36
+ type TypeOptions<T, O> = {
37
+ transform?: (value: T) => O;
38
+ };
39
+ type WithDefault<D> = {
40
+ default?: D;
41
+ };
42
+ type WithOptional<Opt> = {
43
+ optional?: Opt;
44
+ };
45
+ /**
46
+ * `optional` widens the output; `default` keeps it narrow because a value
47
+ * is always produced. Declaring both means optional to send, never absent.
48
+ */
49
+ type OutOf<O, D, Opt> = [Opt] extends [true] ? [D] extends [never] ? O | undefined : O : O;
50
+ /** Either marker makes the key omittable in `InferArgs`. */
51
+ type DefOf<D, Opt> = [Opt] extends [true] ? [D] extends [never] ? undefined : D : D;
52
+ type StringOptions<E extends string, O> = TypeOptions<E, O> & Pick<Rules, "min" | "max" | "length" | "regex" | "email" | "url" | "startsWith" | "endsWith" | "check"> & {
53
+ enum?: readonly E[];
54
+ };
55
+ type ArrayOptions<E, O> = TypeOptions<FieldOut<E>[], O> & Pick<Rules, "min" | "max" | "length" | "check">;
56
+ type NumberOptions<O> = TypeOptions<number, O> & Pick<Rules, "min" | "max" | "int" | "check"> & {
57
+ enum?: readonly number[];
58
+ };
59
+ type InferType<T> = T extends TypeDefination<infer T2, any, any> ? T2 : never;
60
+ type InferOutput<T> = T extends TypeDefination<any, infer O, any> ? O : never;
61
+ type DefineInput<I> = Prettify<{ [K in keyof I]: FieldIn<I[K]>; }>;
62
+ /** Keys whose field is OPTIONAL with no default: absent from the output
63
+ * too, so they mark `?`. A defaulted field always produces a value and
64
+ * stays required. */
65
+ type OutOptional<O> = { [K in keyof O]: [DefaultOf<O[K]>] extends [never] ? never : [DefaultOf<O[K]>] extends [undefined] ? K : never; }[keyof O];
66
+ type DefineOutput<O> = Prettify<{ [K in keyof O as K extends OutOptional<O> ? never : K]: FieldOut<O[K]>; } & { [K in keyof O as K extends OutOptional<O> ? K : never]?: FieldOut<O[K]>; }>;
67
+ /**
68
+ * A handler-less `v.fn({ input, output })` used as a schema describes
69
+ * "a fn from `input` to `output`" - the VALUE is the fn itself. Two
70
+ * views of the same signature:
71
+ *
72
+ * `SchemaFnIn` is the PROVIDER's side - what a caller must hand over.
73
+ * Like any handler, their fn receives the PARSED input (validation runs
74
+ * at its door) and returns the declared output, sync or async.
75
+ *
76
+ * `SchemaFnOut` is the CONSUMER's side (`c.input.x`) - the handler calls
77
+ * it with RAW args, exactly like calling the fn it stands in for.
78
+ *
79
+ * No declared input means the signature is UNSPECIFIED, not zero-arg -
80
+ * any fn fits (`create: v.fn`), so the args stay open.
81
+ */
82
+ type SchemaFnIn<FI, FO> = (...args: unknown extends FI ? any[] : [input: InferInput<FI>]) => unknown extends FO ? any : InferInput<OutputSchemaOf<FO>> | Promise<InferInput<OutputSchemaOf<FO>>>;
83
+ type SchemaFnOut<FI, FO> = (...args: unknown extends FI ? any[] : [input: InferArgs<FI>]) => unknown extends FO ? any : InferInput<OutputSchemaOf<FO>> | Promise<InferInput<OutputSchemaOf<FO>>>;
84
+ /**
85
+ * A fn schema whose input IS a var carries that var's name as an optional
86
+ * phantom (`$fnVar`, tuple-wrapped so a plain fn can never false-match).
87
+ * Scope resolution reads it to WIDEN the fn's args with everything the
88
+ * scope mounts on that var - see `WidenSchemaFns`. Optional, so any plain
89
+ * closure still satisfies the type.
90
+ */
91
+ type FnVarBrand<FI> = FI extends {
92
+ $var: true;
93
+ name: infer N extends string;
94
+ } ? {
95
+ readonly $fnVar?: [N];
96
+ } : unknown;
97
+ /**
98
+ * A declared `output` comes in two forms: a bare schema (the signature
99
+ * AND the exit check), or the wrapper `{ def?, validation? }` splitting
100
+ * what the fn PROMISES from what gets CHECKED - `{ def }` documents
101
+ * without paying runtime validation, `{ def, validation }` checks with a
102
+ * different (usually looser) schema than it documents. The wrapper is
103
+ * recognized by its keys, so an output that IS an object with only
104
+ * `def`/`validation` fields must be written `v.object({...})`.
105
+ *
106
+ * `OutputSchemaOf` is the type-level unwrap - the schema the fn's return
107
+ * type (and its rendered signature) comes from.
108
+ */
109
+ type OutputSchemaOf<O> = Exclude<keyof O, "def" | "validation"> extends never ? O extends {
110
+ def: infer D;
111
+ } ? D : O extends {
112
+ validation: infer Vl;
113
+ } ? Vl : O : O;
114
+ /** The runtime unwrap: `def` is the documented schema (falls back to
115
+ * `validation`), `validation` is what the exit check runs - undefined
116
+ * means no check. A bare schema is both. */
117
+ declare const outputContract: (output: unknown) => {
118
+ def?: unknown;
119
+ validation?: unknown;
120
+ };
121
+ /**
122
+ * One input field, in four flavours:
123
+ * - a `v.var()`, whose shape comes from the var's own `schema`
124
+ * - a handler-less `v.fn(...)` builder, which types the field as a FN
125
+ * - a type from `v.string()` / `v.object()` / ...
126
+ * - a bare nested record, which recurses
127
+ *
128
+ * The record case has to come last: a TypeDefination is itself a record,
129
+ * and so is a builder.
130
+ */
131
+ type FieldOut<F> = F extends {
132
+ $var: true;
133
+ schema?: infer S;
134
+ } ? InferInput<NonNullable<S>> : F extends {
135
+ $fnSchema: {
136
+ input?: infer FI;
137
+ output?: infer FO;
138
+ };
139
+ } ? SchemaFnOut<FI, FO> & FnVarBrand<FI> : F extends TypeDefination<any, infer O, any> ? O : F extends Record<string, unknown> ? Prettify<{ [K in keyof F]: FieldOut<F[K]>; }> : never;
140
+ type FieldIn<F> = F extends {
141
+ $var: true;
142
+ schema?: infer S;
143
+ } ? InferArgs<NonNullable<S>> : F extends {
144
+ $fnSchema: {
145
+ input?: infer FI;
146
+ output?: infer FO;
147
+ };
148
+ } ? SchemaFnIn<FI, FO> & FnVarBrand<FI> : F extends TypeDefination<infer T, any, any> ? T : F extends Record<string, unknown> ? ArgsShape<F> : never;
149
+ /**
150
+ * A field's declared default, looked through a var to its schema. Only a
151
+ * TYPE's default counts - a var's own default is its initial value, not a
152
+ * licence to omit the input. The `$fnSchema` guard mirrors `asType`'s
153
+ * ordering: a bare `v.fn` is CALLABLE, and any callable duck-matches
154
+ * TypeDefination (`.name` comes with every function), which would read a
155
+ * phantom default off it and wrongly mark the field optional.
156
+ */
157
+ type DefaultOf<F> = F extends {
158
+ $var: true;
159
+ schema?: infer S;
160
+ } ? NonNullable<S> extends TypeDefination<any, any, infer D> ? D : never : F extends {
161
+ $fnSchema: unknown;
162
+ } ? never : F extends TypeDefination<any, any, infer D> ? D : never;
163
+ type Defaulted<I> = { [K in keyof I]: [DefaultOf<I[K]>] extends [never] ? never : K; }[keyof I];
164
+ /** Defaulted keys are optional to send, but always present in the handler. */
165
+ type ArgsShape<I> = Prettify<{ [K in keyof I as K extends Defaulted<I> ? never : K]: FieldIn<I[K]>; } & { [K in keyof I as K extends Defaulted<I> ? K : never]?: FieldIn<I[K]>; }>;
166
+ /**
167
+ * Post-transform shape - what a handler sees. The `$var` branch must come
168
+ * first: a var's `name` property duck-matches TypeDefination, and falling
169
+ * into that branch reads the var's VALUE type instead of its schema. A
170
+ * TUPLE input maps position by position - the fn takes that many args.
171
+ */
172
+ type InferInput<I> = I extends {
173
+ $var: true;
174
+ schema?: infer S;
175
+ } ? InferInput<NonNullable<S>> : I extends {
176
+ $fnSchema: {
177
+ input?: infer FI;
178
+ output?: infer FO;
179
+ };
180
+ } ? SchemaFnOut<FI, FO> & FnVarBrand<FI> : I extends readonly unknown[] ? { -readonly [K in keyof I]: InferInput<I[K]>; } : I extends TypeDefination<any, infer O, any> ? O : Prettify<{ [K in keyof I]: FieldOut<I[K]>; }>;
181
+ /** Pre-transform shape - what a caller sends. Same branch order. */
182
+ type InferArgs<I> = I extends {
183
+ $var: true;
184
+ schema?: infer S;
185
+ } ? InferArgs<NonNullable<S>> : I extends {
186
+ $fnSchema: {
187
+ input?: infer FI;
188
+ output?: infer FO;
189
+ };
190
+ } ? SchemaFnIn<FI, FO> & FnVarBrand<FI> : I extends readonly unknown[] ? { -readonly [K in keyof I]: InferArgs<I[K]>; } : I extends TypeDefination<infer T, any, any> ? T : ArgsShape<I>;
191
+ declare const isType: (value: any) => value is TypeDefination<any, any>;
192
+ /** A handler-less `v.fn(...)` builder doubles as a schema: the value it
193
+ * describes is a FN with the declared signature. The builder FN itself is
194
+ * branded too, so bare `v.fn` reads as "any function". */
195
+ declare const isFnSchema: (value: any) => value is {
196
+ $fnSchema: {
197
+ input?: unknown;
198
+ output?: unknown;
199
+ };
200
+ };
201
+ declare const asType: (value: any) => TypeDefination<any, any>;
202
+ declare const typeOf: (value: unknown) => "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "null" | "array" | "NaN";
203
+ declare const isVar: (value: any) => boolean;
204
+ declare const validate: (def: TypeDefination<any, any, any>, value: unknown, path: string) => any;
205
+ declare const vTypes: {
206
+ /** An `enum` narrows both sides to the literal union: `v.string({
207
+ * enum: ["a", "b"] })` types as `"a" | "b"`, not `string`. */
208
+ string: <const E extends string = string, O = E, D = never, Opt extends boolean = false>(options?: StringOptions<E, O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<E, OutOf<O, D, Opt>, DefOf<D, Opt>>;
209
+ number: <O = number, D = never, Opt extends boolean = false>(options?: NumberOptions<O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<number, OutOf<O, D, Opt>, DefOf<D, Opt>>;
210
+ boolean: <O = boolean, D = never, Opt extends boolean = false>(options?: TypeOptions<boolean, O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<boolean, OutOf<O, D, Opt>, DefOf<D, Opt>>;
211
+ /** A Date INSTANCE - checked with `instanceof`, never parsed. */
212
+ date: <O = Date, D = never, Opt extends boolean = false>(options?: TypeOptions<Date, O> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<Date, OutOf<O, D, Opt>, DefOf<D, Opt>>;
213
+ /** Passthrough - validated as-is, never coerced or stripped. */
214
+ any: <T = unknown, D = never, Opt extends boolean = false>(options?: TypeOptions<T, T> & WithDefault<D> & WithOptional<Opt>) => TypeDefination<T, OutOf<T, D, Opt>, DefOf<D, Opt>>;
215
+ /** With a SHAPE every field validates; with NO shape (`v.object()`)
216
+ * any object passes, as-is. */
217
+ object: <S = undefined, O = { [K_1 in keyof S as K_1 extends OutOptional<S> ? never : K_1]: FieldOut<S[K_1]>; } & { [K_2 in keyof S as K_2 extends OutOptional<S> ? K_2 : never]?: FieldOut<S[K_2]> | undefined; } extends (infer T) ? { [K in keyof T]: T[K]; } : never, D = never, Opt extends boolean = false>(shape?: S, options?: TypeOptions<DefineOutput<S>, O> & WithDefault<D> & WithOptional<Opt>) => [S] extends [undefined] ? TypeDefination<Record<string, any>, Record<string, any>> : TypeDefination<ArgsShape<S>, OutOf<O, D, Opt>, DefOf<D, Opt>>;
218
+ /** With an ELEMENT every item validates - all failures report
219
+ * together, like object fields; with NO element (`v.array()`) any
220
+ * array passes, as-is. `min`/`max`/`length` count items. */
221
+ array: <E = undefined, O = FieldOut<E>[], D = never, Opt extends boolean = false>(element?: E, options?: ArrayOptions<E, O> & WithDefault<D> & WithOptional<Opt>) => [E] extends [undefined] ? TypeDefination<any[], OutOf<any[], D, Opt>, DefOf<D, Opt>> : TypeDefination<FieldIn<E>[], OutOf<O, D, Opt>, DefOf<D, Opt>>;
222
+ };
223
+ //#endregion
224
+ export { DefineInput, DefineOutput, FnVarBrand, InferArgs, InferInput, InferOutput, InferType, OutputSchemaOf, Rules, TypeDefination, TypeOptions, asType, isFnSchema, isType, isVar, outputContract, typeOf, vTypes, validate };
225
+ //# sourceMappingURL=schema.d.mts.map
@@ -0,0 +1,159 @@
1
+ import { ValidationError } from "./error.mjs";
2
+ //#region src/schema.ts
3
+ /** The runtime unwrap: `def` is the documented schema (falls back to
4
+ * `validation`), `validation` is what the exit check runs - undefined
5
+ * means no check. A bare schema is both. */
6
+ const outputContract = (output) => {
7
+ if (output === void 0) return {};
8
+ if (output !== null && typeof output === "object" && !Array.isArray(output) && !isType(output) && !isVar(output) && !isFnSchema(output)) {
9
+ const keys = Object.keys(output);
10
+ if (keys.length > 0 && keys.every((k) => k === "def" || k === "validation")) {
11
+ const { def, validation } = output;
12
+ return {
13
+ def: def ?? validation,
14
+ validation
15
+ };
16
+ }
17
+ }
18
+ return {
19
+ def: output,
20
+ validation: output
21
+ };
22
+ };
23
+ const isType = (value) => typeof value?.name === "string";
24
+ /** A handler-less `v.fn(...)` builder doubles as a schema: the value it
25
+ * describes is a FN with the declared signature. The builder FN itself is
26
+ * branded too, so bare `v.fn` reads as "any function". */
27
+ const isFnSchema = (value) => typeof value?.$fnSchema === "object" && value.$fnSchema !== null;
28
+ const asType = (value) => isVar(value) ? asType(value.schema ?? {}) : isFnSchema(value) ? {
29
+ name: "function",
30
+ fnInput: value.$fnSchema.input
31
+ } : isType(value) ? value : {
32
+ name: "object",
33
+ shape: value
34
+ };
35
+ const typeOf = (value) => value === null ? "null" : Array.isArray(value) ? "array" : Number.isNaN(value) ? "NaN" : typeof value;
36
+ const isVar = (value) => value?.$var === true;
37
+ const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
38
+ const fail = (path, message) => {
39
+ throw new ValidationError(path, message);
40
+ };
41
+ /** Constraint checks, run after the value's type is known to be right. */
42
+ const applyRules = (def, value, path) => {
43
+ if (def.enum && !def.enum.includes(value)) fail(path, `expected one of ${def.enum.join(", ")}, received ${value}`);
44
+ if (typeof value === "string") {
45
+ if (def.length !== void 0 && value.length !== def.length) fail(path, `expected length ${def.length}, received ${value.length}`);
46
+ if (def.min !== void 0 && value.length < def.min) fail(path, `expected at least ${def.min} characters, received ${value.length}`);
47
+ if (def.max !== void 0 && value.length > def.max) fail(path, `expected at most ${def.max} characters, received ${value.length}`);
48
+ if (def.regex && !def.regex.test(value)) fail(path, `does not match ${def.regex}`);
49
+ if (def.email && !EMAIL.test(value)) fail(path, "expected an email address");
50
+ if (def.url) try {
51
+ new URL(value);
52
+ } catch {
53
+ fail(path, "expected a URL");
54
+ }
55
+ if (def.startsWith !== void 0 && !value.startsWith(def.startsWith)) fail(path, `expected to start with "${def.startsWith}"`);
56
+ if (def.endsWith !== void 0 && !value.endsWith(def.endsWith)) fail(path, `expected to end with "${def.endsWith}"`);
57
+ }
58
+ if (Array.isArray(value)) {
59
+ if (def.length !== void 0 && value.length !== def.length) fail(path, `expected length ${def.length}, received ${value.length}`);
60
+ if (def.min !== void 0 && value.length < def.min) fail(path, `expected at least ${def.min} items, received ${value.length}`);
61
+ if (def.max !== void 0 && value.length > def.max) fail(path, `expected at most ${def.max} items, received ${value.length}`);
62
+ }
63
+ if (typeof value === "number") {
64
+ if (def.int && !Number.isInteger(value)) fail(path, `expected an integer, received ${value}`);
65
+ if (def.min !== void 0 && value < def.min) fail(path, `expected >= ${def.min}, received ${value}`);
66
+ if (def.max !== void 0 && value > def.max) fail(path, `expected <= ${def.max}, received ${value}`);
67
+ }
68
+ if (def.check) {
69
+ const result = def.check(value);
70
+ if (result !== true) fail(path, typeof result === "string" ? result : "failed check");
71
+ }
72
+ };
73
+ const validate = (def, value, path) => {
74
+ if (value === void 0) {
75
+ if (def.default !== void 0) value = def.default;
76
+ else if (def.optional) return void 0;
77
+ }
78
+ if (isVar(def)) {
79
+ if (value === void 0) return void 0;
80
+ const schema = def.schema;
81
+ return schema === void 0 ? value : validate(schema, value, path);
82
+ }
83
+ if (def.name === "any") return def.transform ? def.transform(value) : value;
84
+ if (def.name === "date") {
85
+ if (!(value instanceof Date)) throw new ValidationError(path, `expected date, received ${typeOf(value)}`);
86
+ return def.transform ? def.transform(value) : value;
87
+ }
88
+ if (def.name === "function") {
89
+ if (typeof value !== "function") throw new ValidationError(path, `expected function, received ${typeOf(value)}`);
90
+ const inner = def.fnInput;
91
+ if (inner === void 0 || value.$fn === true) return value;
92
+ const innerType = asType(inner);
93
+ return (input, parent) => value(validate(innerType, input, `${path}()`), parent);
94
+ }
95
+ if (def.name === "array") {
96
+ if (!Array.isArray(value)) throw new ValidationError(path, `expected array, received ${typeOf(value)}`);
97
+ applyRules(def, value, path);
98
+ if (def.shape === void 0) return def.transform ? def.transform(value) : value;
99
+ const elementType = asType(def.shape);
100
+ const items = [];
101
+ const problems = [];
102
+ for (let index = 0; index < value.length; index++) try {
103
+ items.push(validate(elementType, value[index], `${path}[${index}]`));
104
+ } catch (thrown) {
105
+ if (!(thrown instanceof ValidationError)) throw thrown;
106
+ problems.push(...thrown.issues);
107
+ }
108
+ const firstProblem = problems[0];
109
+ if (firstProblem) throw new ValidationError(firstProblem.path, firstProblem.message, problems);
110
+ return def.transform ? def.transform(items) : items;
111
+ }
112
+ if (def.name === "object") {
113
+ if (typeOf(value) !== "object") throw new ValidationError(path, `expected object, received ${typeOf(value)}`);
114
+ if (def.shape === void 0) return def.transform ? def.transform(value) : value;
115
+ const parsed = {};
116
+ const issues = [];
117
+ for (const [field, child] of Object.entries(def.shape)) try {
118
+ const parsedField = validate(asType(child), value[field], `${path}.${field}`);
119
+ if (parsedField !== void 0) parsed[field] = parsedField;
120
+ } catch (thrown) {
121
+ if (!(thrown instanceof ValidationError)) throw thrown;
122
+ issues.push(...thrown.issues);
123
+ }
124
+ const firstIssue = issues[0];
125
+ if (firstIssue) throw new ValidationError(firstIssue.path, firstIssue.message, issues);
126
+ return def.transform ? def.transform(parsed) : parsed;
127
+ }
128
+ if (typeOf(value) !== def.name) throw new ValidationError(path, `expected ${def.name}, received ${typeOf(value)}`);
129
+ applyRules(def, value, path);
130
+ return def.transform ? def.transform(value) : value;
131
+ };
132
+ /** Builds the runtime object; the declared return type is the contract. */
133
+ const build = (name, options, extra) => ({
134
+ name,
135
+ ...extra,
136
+ ...options
137
+ });
138
+ const vTypes = {
139
+ /** An `enum` narrows both sides to the literal union: `v.string({
140
+ * enum: ["a", "b"] })` types as `"a" | "b"`, not `string`. */
141
+ string: (options) => build("string", options),
142
+ number: (options) => build("number", options),
143
+ boolean: (options) => build("boolean", options),
144
+ /** A Date INSTANCE - checked with `instanceof`, never parsed. */
145
+ date: (options) => build("date", options),
146
+ /** Passthrough - validated as-is, never coerced or stripped. */
147
+ any: (options) => build("any", options),
148
+ /** With a SHAPE every field validates; with NO shape (`v.object()`)
149
+ * any object passes, as-is. */
150
+ object: (shape, options) => build("object", options, shape === void 0 ? {} : { shape }),
151
+ /** With an ELEMENT every item validates - all failures report
152
+ * together, like object fields; with NO element (`v.array()`) any
153
+ * array passes, as-is. `min`/`max`/`length` count items. */
154
+ array: (element, options) => build("array", options, element === void 0 ? {} : { shape: element })
155
+ };
156
+ //#endregion
157
+ export { asType, isFnSchema, isType, isVar, outputContract, typeOf, vTypes, validate };
158
+
159
+ //# sourceMappingURL=schema.mjs.map