@c9up/rune 0.1.7 → 0.1.9
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/MessagesProvider.d.ts +5 -0
- package/dist/MessagesProvider.d.ts.map +1 -1
- package/dist/MessagesProvider.js +1 -1
- package/dist/MessagesProvider.js.map +1 -1
- package/dist/Schema.d.ts +831 -35
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +2342 -140
- package/dist/Schema.js.map +1 -1
- package/dist/date.d.ts +36 -0
- package/dist/date.d.ts.map +1 -0
- package/dist/date.js +275 -0
- package/dist/date.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -1
- package/dist/formats.d.ts +148 -0
- package/dist/formats.d.ts.map +1 -0
- package/dist/formats.js +671 -0
- package/dist/formats.js.map +1 -0
- package/dist/index.d.ts +150 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +180 -1
- package/dist/index.js.map +1 -1
- package/dist/magic.d.ts +30 -0
- package/dist/magic.d.ts.map +1 -0
- package/dist/magic.js +154 -0
- package/dist/magic.js.map +1 -0
- package/dist/native.d.ts +18 -6
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +34 -19
- package/dist/native.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +9 -1
- package/src/MessagesProvider.ts +1 -1
- package/src/Schema.ts +3389 -177
- package/src/date.ts +320 -0
- package/src/errors.ts +11 -0
- package/src/formats.ts +776 -0
- package/src/index.ts +269 -0
- package/src/magic.ts +181 -0
- package/src/native.ts +36 -21
- package/src/types.ts +55 -0
package/dist/Schema.d.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @implements FR38, FR39, FR40, FR41
|
|
5
5
|
*/
|
|
6
|
+
import { type CompareUnit, type DateFormat } from "./date.js";
|
|
7
|
+
import { RuneValidationError } from "./errors.js";
|
|
8
|
+
import { type AlphaOptions, type EmailOptions, type NormalizeEmailOptions, type NormalizeUrlOptions, type UrlOptions, type VatOptions } from "./formats.js";
|
|
6
9
|
import type { MessagesProviderContract } from "./MessagesProvider.js";
|
|
7
10
|
export type ValidationMessageParams = Record<string, string | number | boolean>;
|
|
8
11
|
export type ValidationTranslator = (key: string, params?: ValidationMessageParams) => string | undefined;
|
|
@@ -34,8 +37,26 @@ export interface FieldContext {
|
|
|
34
37
|
meta: Record<string, unknown>;
|
|
35
38
|
/** `true` while no error has been reported for this field yet. */
|
|
36
39
|
isValid: boolean;
|
|
37
|
-
/**
|
|
38
|
-
|
|
40
|
+
/** Last path segment — `city` for `address.city` (VineJS `name`). */
|
|
41
|
+
name: string;
|
|
42
|
+
/** Dotted path with numeric segments replaced by `*` (`tags.*.name`). */
|
|
43
|
+
wildCardPath: string;
|
|
44
|
+
/** `true` when this value sits inside an array. */
|
|
45
|
+
isArrayMember: boolean;
|
|
46
|
+
/** `true` when the value is neither `undefined` nor `null`. */
|
|
47
|
+
isDefined: boolean;
|
|
48
|
+
/** `true` when the value passed its type rule. */
|
|
49
|
+
isValidDataType: boolean;
|
|
50
|
+
/** The full dotted path — same value as {@link field}, VineJS spelling. */
|
|
51
|
+
getFieldPath(): string;
|
|
52
|
+
/** Replace the value under validation (VineJS `mutate`). */
|
|
53
|
+
mutate(newValue: unknown): void;
|
|
54
|
+
/**
|
|
55
|
+
* Report a validation failure. `field` and `args` are optional (VineJS
|
|
56
|
+
* passes four arguments); omitting them reports against this field with no
|
|
57
|
+
* interpolation data.
|
|
58
|
+
*/
|
|
59
|
+
report(message: string, rule: string, field?: string | FieldContext, args?: Record<string, unknown>): void;
|
|
39
60
|
}
|
|
40
61
|
/**
|
|
41
62
|
* A `.use()` rule validator — VineJS shape `(value, options, field)`. Report
|
|
@@ -45,6 +66,13 @@ export type RuleValidator<Options = undefined> = (value: unknown, options: Optio
|
|
|
45
66
|
/** A compiled `.use()` rule produced by {@link createRule}. */
|
|
46
67
|
export interface CompiledRule {
|
|
47
68
|
readonly __rune: "rule";
|
|
69
|
+
/** Run even on `undefined`/`null` (VineJS implicit rules). */
|
|
70
|
+
readonly implicit?: boolean;
|
|
71
|
+
readonly name?: string;
|
|
72
|
+
/** Modifier applied to this field's JSON Schema node. */
|
|
73
|
+
readonly toJSONSchema?: JsonSchemaModifier;
|
|
74
|
+
/** The options the rule was built with, handed to {@link toJSONSchema}. */
|
|
75
|
+
readonly ruleOptions?: unknown;
|
|
48
76
|
run(value: unknown, field: FieldContext): void;
|
|
49
77
|
}
|
|
50
78
|
/**
|
|
@@ -62,8 +90,61 @@ export interface CompiledRule {
|
|
|
62
90
|
* passwordConfirmation: rules.string().use(sameAs('password')),
|
|
63
91
|
* })
|
|
64
92
|
*/
|
|
65
|
-
|
|
66
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Options accepted by {@link createRule} / {@link createAsyncRule} — VineJS
|
|
95
|
+
* `vine.createRule(fn, { implicit, isAsync })`.
|
|
96
|
+
*/
|
|
97
|
+
export interface CreateRuleOptions {
|
|
98
|
+
/**
|
|
99
|
+
* Run the rule even when the value is `undefined` or `null`. Non-implicit
|
|
100
|
+
* rules are skipped on an absent value, which is why a `required`-style
|
|
101
|
+
* custom rule could not be written before.
|
|
102
|
+
*/
|
|
103
|
+
implicit?: boolean;
|
|
104
|
+
/** Rule name reported in errors when the validator does not pass one. */
|
|
105
|
+
name?: string;
|
|
106
|
+
/**
|
|
107
|
+
* VineJS `toJSONSchema?: JsonSchemaModifier` — a FUNCTION receiving the node
|
|
108
|
+
* built so far (plus the rule's options) and returning the modified node.
|
|
109
|
+
* A static fragment could only ever add keys; a modifier can also narrow or
|
|
110
|
+
* replace what the base rules produced.
|
|
111
|
+
*/
|
|
112
|
+
toJSONSchema?: JsonSchemaModifier;
|
|
113
|
+
/**
|
|
114
|
+
* Declare the rule asynchronous (VineJS `{ isAsync: true }`).
|
|
115
|
+
* {@link createAsyncRule} sets it; passing it to {@link createRule} routes
|
|
116
|
+
* the rule to the async builder instead of silently producing a sync rule
|
|
117
|
+
* whose Promise nobody awaits.
|
|
118
|
+
*/
|
|
119
|
+
isAsync?: boolean;
|
|
120
|
+
}
|
|
121
|
+
export declare function createRule(validator: RuleValidator<undefined>, options?: CreateRuleOptions): () => CompiledRule;
|
|
122
|
+
export declare function createRule<Options>(validator: RuleValidator<Options>, options?: CreateRuleOptions): (options: Options) => CompiledRule;
|
|
123
|
+
/**
|
|
124
|
+
* An async `.useAsync()` rule validator — same shape as {@link RuleValidator}
|
|
125
|
+
* but may return a Promise. Runs only under {@link ValidationSchema.validateResultAsync}.
|
|
126
|
+
*/
|
|
127
|
+
export type AsyncRuleValidator<Options = undefined> = (value: unknown, options: Options, field: FieldContext) => void | Promise<void>;
|
|
128
|
+
/** A compiled async rule produced by {@link createAsyncRule}. */
|
|
129
|
+
export interface AsyncCompiledRule {
|
|
130
|
+
readonly __rune: "asyncRule";
|
|
131
|
+
/** Run even on `undefined`/`null` (VineJS implicit rules). */
|
|
132
|
+
readonly implicit?: boolean;
|
|
133
|
+
readonly name?: string;
|
|
134
|
+
/** Modifier applied to this field's JSON Schema node. */
|
|
135
|
+
readonly toJSONSchema?: JsonSchemaModifier;
|
|
136
|
+
/** The options the rule was built with, handed to {@link toJSONSchema}. */
|
|
137
|
+
readonly ruleOptions?: unknown;
|
|
138
|
+
run(value: unknown, field: FieldContext): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Async counterpart of {@link createRule} — for rules that must await (DB lookups
|
|
142
|
+
* etc.). Attach with `chain.useAsync(rule(options))`; the schema must then be run
|
|
143
|
+
* with `validateResultAsync`. This is how DB-backed `unique`/`exists` rules are built
|
|
144
|
+
* (the validator does the query), keeping rune framework-agnostic.
|
|
145
|
+
*/
|
|
146
|
+
export declare function createAsyncRule(validator: AsyncRuleValidator<undefined>, options?: CreateRuleOptions): () => AsyncCompiledRule;
|
|
147
|
+
export declare function createAsyncRule<Options>(validator: AsyncRuleValidator<Options>, options?: CreateRuleOptions): (options: Options) => AsyncCompiledRule;
|
|
67
148
|
/**
|
|
68
149
|
* Validation result — discriminated union that narrows `data` to the schema's
|
|
69
150
|
* `T` when `valid` is `true`, removing the need for callers to cast or guard
|
|
@@ -88,17 +169,105 @@ export interface ValidateOptions {
|
|
|
88
169
|
* provider takes precedence over a globally bound translator).
|
|
89
170
|
*/
|
|
90
171
|
messagesProvider?: MessagesProviderContract;
|
|
172
|
+
/**
|
|
173
|
+
* VineJS `errorReporter: () => ErrorReporterContract` — a FACTORY returning a
|
|
174
|
+
* reporter, so a transcribed Adonis reporter works as-is. A plain
|
|
175
|
+
* `(error) => void` observer is also accepted.
|
|
176
|
+
*
|
|
177
|
+
* The two are told apart by ARITY (a factory takes no argument), never by
|
|
178
|
+
* calling one speculatively to see what comes back.
|
|
179
|
+
*
|
|
180
|
+
* Either way the reporter OBSERVES: the validation result is never changed by
|
|
181
|
+
* it, so a reporter cannot mask a failure.
|
|
182
|
+
*/
|
|
183
|
+
errorReporter?: ErrorReporterFactory | ((error: ValidationError) => void);
|
|
91
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* VineJS `JsonSchemaModifier`: receives the JSON Schema node assembled from the
|
|
187
|
+
* declarative rules and returns the node to use instead.
|
|
188
|
+
*/
|
|
189
|
+
export type JsonSchemaModifier = (node: Record<string, unknown>, options?: unknown) => Record<string, unknown>;
|
|
190
|
+
/** VineJS `ErrorReporterContract`. */
|
|
191
|
+
export interface ErrorReporterContract {
|
|
192
|
+
/** `true` once at least one error has been reported. */
|
|
193
|
+
hasErrors: boolean;
|
|
194
|
+
/** Build the exception a caller may throw. */
|
|
195
|
+
createError(): Error;
|
|
196
|
+
/** Report one failure. */
|
|
197
|
+
report(message: string, rule: string, field: FieldContext | string, args?: Record<string, unknown>): unknown;
|
|
198
|
+
}
|
|
199
|
+
/** A zero-argument factory producing a fresh {@link ErrorReporterContract}. */
|
|
200
|
+
export type ErrorReporterFactory = () => ErrorReporterContract;
|
|
92
201
|
export interface ValidationSchema<T = Record<string, unknown>> {
|
|
93
202
|
fields: Record<string, RuleChain>;
|
|
94
|
-
/**
|
|
95
|
-
|
|
203
|
+
/**
|
|
204
|
+
* The object schema the validator was built from — always a chain, so
|
|
205
|
+
* `validator.schema.partial()` / `.pick()` / `.omit()` work as in VineJS
|
|
206
|
+
* whichever form `create()` received. The raw field map stays on
|
|
207
|
+
* {@link fields}.
|
|
208
|
+
*/
|
|
209
|
+
schema: RuleChain;
|
|
210
|
+
/**
|
|
211
|
+
* Standard Schema v1 contract, so a consumer can validate through the
|
|
212
|
+
* vendor-neutral protocol instead of rune's own API.
|
|
213
|
+
*/
|
|
214
|
+
"~standard": {
|
|
215
|
+
version: 1;
|
|
216
|
+
vendor: string;
|
|
217
|
+
/** Standard JSON Schema v1 props (VineJS 4.3+). */
|
|
218
|
+
jsonSchema: {
|
|
219
|
+
input(): Record<string, unknown>;
|
|
220
|
+
output(): Record<string, unknown>;
|
|
221
|
+
};
|
|
222
|
+
validate(value: unknown): Promise<{
|
|
223
|
+
value: T;
|
|
224
|
+
} | {
|
|
225
|
+
issues: ReadonlyArray<{
|
|
226
|
+
message: string;
|
|
227
|
+
path: string[];
|
|
228
|
+
}>;
|
|
229
|
+
}>;
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* Error reporter for this validator (VineJS `validator.errorReporter`). A
|
|
233
|
+
* per-call option still wins; this wins over the process-wide one.
|
|
234
|
+
*/
|
|
235
|
+
errorReporter: ErrorReporterFactory | ((error: ValidationError) => void) | null;
|
|
236
|
+
/** Introspection of the compiled schema — VineJS `{ schema, refs }` shape. */
|
|
237
|
+
toJSON(): {
|
|
238
|
+
schema: SchemaIntrospection;
|
|
239
|
+
refs: string[];
|
|
240
|
+
};
|
|
241
|
+
/** JSON Schema for the compiled validator (VineJS `toJSONSchema`). */
|
|
242
|
+
toJSONSchema(): Record<string, unknown>;
|
|
243
|
+
/**
|
|
244
|
+
* Validate and return the payload, throwing {@link RuneValidationError} on
|
|
245
|
+
* failure — the VineJS/Adonis contract (`validator.validate(data)`), async
|
|
246
|
+
* so a schema carrying `unique`/`exists` behaves like any other.
|
|
247
|
+
*
|
|
248
|
+
* The never-throwing, synchronous form rune also offers is
|
|
249
|
+
* {@link validateResult}.
|
|
250
|
+
*/
|
|
251
|
+
validate(data: unknown, options?: ValidateOptions): Promise<T>;
|
|
252
|
+
/** Result-based validation (rune superset) — synchronous, never throws. */
|
|
253
|
+
validateResult(data: unknown, options?: ValidateOptions): ValidationResult<T>;
|
|
254
|
+
/** Result-based validation awaiting async rules — never throws. */
|
|
255
|
+
validateResultAsync(data: unknown, options?: ValidateOptions): Promise<ValidationResult<T>>;
|
|
96
256
|
/**
|
|
97
257
|
* Throwing validation (VineJS/Adonis parity). Returns the validated data on
|
|
98
258
|
* success; throws {@link RuneValidationError} (`E_VALIDATION_ERROR`, HTTP 422)
|
|
99
259
|
* with a structured `.messages` array on failure.
|
|
100
260
|
*/
|
|
101
261
|
validateOrThrow(data: unknown, options?: ValidateOptions): T;
|
|
262
|
+
/**
|
|
263
|
+
* Non-throwing validation returning `[error, null] | [null, data]`
|
|
264
|
+
* (VineJS `tryValidate`).
|
|
265
|
+
*/
|
|
266
|
+
tryValidate(data: unknown, options?: ValidateOptions): Promise<[RuneValidationError, null] | [null, T]>;
|
|
267
|
+
/** Synchronous counterpart of {@link tryValidate} (rune superset). */
|
|
268
|
+
tryValidateSync(data: unknown, options?: ValidateOptions): [RuneValidationError, null] | [null, T];
|
|
269
|
+
/** Throwing async validation (see {@link validateResultAsync} + {@link validateOrThrow}). */
|
|
270
|
+
validateOrThrowAsync(data: unknown, options?: ValidateOptions): Promise<T>;
|
|
102
271
|
}
|
|
103
272
|
/** Context threaded through validation so field rules can reach root/parent/meta. */
|
|
104
273
|
interface RunContext {
|
|
@@ -106,7 +275,171 @@ interface RunContext {
|
|
|
106
275
|
parent: Record<string, unknown> | unknown[];
|
|
107
276
|
meta: Record<string, unknown>;
|
|
108
277
|
messagesProvider?: MessagesProviderContract;
|
|
278
|
+
errorReporter?: (error: ValidationError) => void;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* An async rule run deferred by the (synchronous) traversal and awaited by
|
|
282
|
+
* `validateResultAsync`. Collected at EVERY depth — top-level fields, nested object
|
|
283
|
+
* fields and array items alike.
|
|
284
|
+
*/
|
|
285
|
+
interface PendingAsync {
|
|
286
|
+
chain: RuleChain;
|
|
287
|
+
field: string;
|
|
288
|
+
value: unknown;
|
|
289
|
+
ctx: RunContext;
|
|
290
|
+
}
|
|
291
|
+
/** Bind (or clear) the process-wide error reporter. */
|
|
292
|
+
export declare function setGlobalErrorReporter(reporter: ErrorReporterFactory | ((error: ValidationError) => void) | null): void;
|
|
293
|
+
/** Read the process-wide error reporter. */
|
|
294
|
+
export declare function getGlobalErrorReporter(): ErrorReporterFactory | ((error: ValidationError) => void) | null;
|
|
295
|
+
/** Host lookup seam backing `activeUrl()` — see that rule's note on why. */
|
|
296
|
+
export interface HostResolver {
|
|
297
|
+
/** Resolve `true` when the hostname resolves (DNS, or whatever you decide). */
|
|
298
|
+
resolves(hostname: string): Promise<boolean>;
|
|
299
|
+
}
|
|
300
|
+
/** Toggle the global `"" -> null` conversion (VineJS `convertEmptyStringsToNull`). */
|
|
301
|
+
export declare function setConvertEmptyStringsToNull(enabled: boolean): void;
|
|
302
|
+
/** Read the global `"" -> null` conversion flag. */
|
|
303
|
+
export declare function getConvertEmptyStringsToNull(): boolean;
|
|
304
|
+
/** Bind (or clear, with `null`) the resolver backing `activeUrl()`. */
|
|
305
|
+
export declare function bindHostResolver(resolver: HostResolver | null): void;
|
|
306
|
+
/** Bind (or clear) the process-wide messages provider. */
|
|
307
|
+
export declare function setGlobalMessagesProvider(provider: MessagesProviderContract | null): void;
|
|
308
|
+
/** Read the process-wide messages provider. */
|
|
309
|
+
export declare function getGlobalMessagesProvider(): MessagesProviderContract | null;
|
|
310
|
+
/** Bind (or clear, with `null`) the global `rules.date()` output mapper. */
|
|
311
|
+
export declare function setDateTransform(fn: ((value: Date) => unknown) | null): void;
|
|
312
|
+
/**
|
|
313
|
+
* A database lookup seam for the Lucid-style `unique` / `exists` rules.
|
|
314
|
+
*
|
|
315
|
+
* rune stays framework-agnostic, so it never imports a driver: the host binds
|
|
316
|
+
* one resolver at boot (as `bindRosetta` does for translations) and the rules
|
|
317
|
+
* then take Lucid's `{ table, column, where }` options instead of a hand-written
|
|
318
|
+
* callback. The callback form is kept — it is what the resolver is built from.
|
|
319
|
+
*/
|
|
320
|
+
export interface DatabaseResolver {
|
|
321
|
+
/** Resolve `true` when at least one row matches. */
|
|
322
|
+
exists(query: DatabaseLookup): Promise<boolean>;
|
|
323
|
+
}
|
|
324
|
+
/** The lookup handed to a {@link DatabaseResolver} (Lucid `unique`/`exists`). */
|
|
325
|
+
export interface DatabaseLookup {
|
|
326
|
+
table: string;
|
|
327
|
+
column: string;
|
|
328
|
+
value: unknown;
|
|
329
|
+
/** Extra equality filters, e.g. `{ tenant_id: 3 }` (Lucid `where`). */
|
|
330
|
+
where?: Record<string, unknown>;
|
|
331
|
+
/** Rows to ignore, e.g. `{ id: 7 }` when updating (Lucid `whereNot`). */
|
|
332
|
+
whereNot?: Record<string, unknown>;
|
|
333
|
+
}
|
|
334
|
+
/** Bind (or clear, with `null`) the resolver backing `unique()` / `exists()`. */
|
|
335
|
+
export declare function bindDatabase(resolver: DatabaseResolver | null): void;
|
|
336
|
+
/** Options form of `unique()` / `exists()` — Lucid's shape. */
|
|
337
|
+
export interface DatabaseRuleOptions {
|
|
338
|
+
table: string;
|
|
339
|
+
column?: string;
|
|
340
|
+
where?: Record<string, unknown>;
|
|
341
|
+
whereNot?: Record<string, unknown>;
|
|
109
342
|
}
|
|
343
|
+
/** Options accepted by every date comparison (VineJS `{ compare, format }`). */
|
|
344
|
+
export interface DateCompareOptions {
|
|
345
|
+
/** Granularity of the comparison. Defaults to `"day"`, like VineJS. */
|
|
346
|
+
compare?: CompareUnit;
|
|
347
|
+
/** Format used to parse the operand / sibling, when it is a string. */
|
|
348
|
+
format?: string;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* The structural shape `file()` accepts. An Adonis bodyparser `MultipartFile`
|
|
352
|
+
* satisfies it without rune having to know the type.
|
|
353
|
+
*/
|
|
354
|
+
export interface FileLike {
|
|
355
|
+
size: number;
|
|
356
|
+
/**
|
|
357
|
+
* MIME type as REPORTED by the upload. Trust it only with
|
|
358
|
+
* `verifyContent()`, which checks it against the real bytes.
|
|
359
|
+
*/
|
|
360
|
+
type?: string;
|
|
361
|
+
/** Adonis bodyparser's temp path — a byte source for `verifyContent()`. */
|
|
362
|
+
tmpPath?: string;
|
|
363
|
+
/** Alternative byte-source paths. */
|
|
364
|
+
filePath?: string;
|
|
365
|
+
path?: string;
|
|
366
|
+
/** In-memory bytes, when the upload was buffered. */
|
|
367
|
+
buffer?: Uint8Array;
|
|
368
|
+
extname?: string | null;
|
|
369
|
+
clientName?: string;
|
|
370
|
+
name?: string;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Parse a size limit — a byte count, or Adonis's `"2mb"` / `"512kb"` spelling.
|
|
374
|
+
* Throws on an unreadable unit rather than falling back to "unlimited": a cap
|
|
375
|
+
* that silently stops capping is worse than no cap at all.
|
|
376
|
+
*/
|
|
377
|
+
export declare function parseByteSize(size: number | string): number;
|
|
378
|
+
/**
|
|
379
|
+
* What a `parse()` callback receives besides the value — VineJS's
|
|
380
|
+
* `ParseFn = (value, ctx: Pick<FieldContext, 'data' | 'parent' | 'meta'>)`.
|
|
381
|
+
*/
|
|
382
|
+
export type ParseContext = Pick<FieldContext, "data" | "parent" | "meta">;
|
|
383
|
+
/**
|
|
384
|
+
* A conditional set of properties merged into an object (VineJS `vine.group`).
|
|
385
|
+
* The first branch whose predicate matches contributes its shape; `otherwise`
|
|
386
|
+
* is the unconditional fallback.
|
|
387
|
+
*/
|
|
388
|
+
export interface ConditionalGroup {
|
|
389
|
+
readonly __rune: "group";
|
|
390
|
+
branches: ReadonlyArray<{
|
|
391
|
+
predicate: ((data: Record<string, unknown>) => boolean) | null;
|
|
392
|
+
shape: Record<string, RuleChain>;
|
|
393
|
+
}>;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Called when no union branch matched (VineJS `UnionNoMatchCallback`). Report
|
|
397
|
+
* through the field context; reporting nothing suppresses the generic error.
|
|
398
|
+
*/
|
|
399
|
+
export type UnionNoMatchCallback = (value: unknown, field: FieldContext) => void;
|
|
400
|
+
/**
|
|
401
|
+
* Called with a record's keys (VineJS `RecordKeysCallback`). Report through the
|
|
402
|
+
* field context; reporting nothing accepts the key set.
|
|
403
|
+
*/
|
|
404
|
+
export type RecordKeysCallback = (keys: string[], field: FieldContext) => void;
|
|
405
|
+
/** Per-field introspection returned inside `toJSON().schema`. */
|
|
406
|
+
export type SchemaIntrospection = Record<string, {
|
|
407
|
+
rules: string[];
|
|
408
|
+
optional: boolean;
|
|
409
|
+
nullable: boolean;
|
|
410
|
+
}>;
|
|
411
|
+
/** Build a conditional group (VineJS `vine.group([...])`). */
|
|
412
|
+
export declare function group(branches: ReadonlyArray<{
|
|
413
|
+
predicate: ((data: Record<string, unknown>) => boolean) | null;
|
|
414
|
+
shape: Record<string, RuleChain>;
|
|
415
|
+
}>): ConditionalGroup;
|
|
416
|
+
/** A predicate-guarded group branch (`vine.group.if`). */
|
|
417
|
+
export declare function groupIf(predicate: (data: Record<string, unknown>) => boolean, shape: Record<string, RuleChain>): {
|
|
418
|
+
predicate: (data: Record<string, unknown>) => boolean;
|
|
419
|
+
shape: Record<string, RuleChain>;
|
|
420
|
+
};
|
|
421
|
+
/** The unconditional fallback branch (`vine.group.else` / `.otherwise`). */
|
|
422
|
+
export declare function groupElse(shape: Record<string, RuleChain>): {
|
|
423
|
+
predicate: null;
|
|
424
|
+
shape: Record<string, RuleChain>;
|
|
425
|
+
};
|
|
426
|
+
/** A union branch guarded by a predicate — `vine.union.if(...)`. */
|
|
427
|
+
export interface ConditionalBranch {
|
|
428
|
+
/** `null` for an unconditional branch (`union.else`). */
|
|
429
|
+
predicate: ((value: unknown, field: FieldContext) => boolean) | null;
|
|
430
|
+
chain: RuleChain;
|
|
431
|
+
}
|
|
432
|
+
/** What `union()` accepts: a bare chain, or a guarded branch. */
|
|
433
|
+
export type UnionBranch = RuleChain | ConditionalBranch;
|
|
434
|
+
/**
|
|
435
|
+
* Guarded union branch (VineJS `vine.union.if`). The predicate picks the branch;
|
|
436
|
+
* the chosen branch's OWN errors are reported, which is what makes a union
|
|
437
|
+
* diagnosable — "matches nothing" tells the caller nothing about which shape it
|
|
438
|
+
* nearly matched.
|
|
439
|
+
*/
|
|
440
|
+
export declare function unionIf(predicate: (value: unknown, field: FieldContext) => boolean, chain: RuleChain): ConditionalBranch;
|
|
441
|
+
/** Fallback union branch (VineJS `vine.union.else`). */
|
|
442
|
+
export declare function unionElse(chain: RuleChain): ConditionalBranch;
|
|
110
443
|
/** Extract the phantom output type of a chain. */
|
|
111
444
|
type OutputOf<C> = C extends RuleChain<infer O> ? O : never;
|
|
112
445
|
/** Keys whose output includes `undefined` become optional in the inferred shape. */
|
|
@@ -144,8 +477,19 @@ export type Infer<S> = Prettify<{
|
|
|
144
477
|
* An explicit generic is still accepted for back-compat
|
|
145
478
|
* (`schema<MyType>({ ... })`), overriding inference.
|
|
146
479
|
*/
|
|
147
|
-
export declare function schema<S extends Record<string, RuleChain>>(fields: S): ValidationSchema<Infer<S>>;
|
|
148
|
-
export declare function schema<T = Record<string, unknown>>(fields: Record<string, RuleChain
|
|
480
|
+
export declare function schema<S extends Record<string, RuleChain>>(fields: S, objectChain?: RuleChain): ValidationSchema<Infer<S>>;
|
|
481
|
+
export declare function schema<T = Record<string, unknown>>(fields: Record<string, RuleChain>, objectChain?: RuleChain): ValidationSchema<T>;
|
|
482
|
+
/**
|
|
483
|
+
* VineJS's `vine.create(...)`. Same thing as {@link schema} — the Adonis
|
|
484
|
+
* spelling is provided so a validator reads the same in both frameworks.
|
|
485
|
+
*/
|
|
486
|
+
/**
|
|
487
|
+
* VineJS's `vine.create(...)`. Accepts either a map of fields (rune's native
|
|
488
|
+
* spelling) or the `RuleChain` produced by `rune.object({...})`, because
|
|
489
|
+
* `vine.create(vine.object({...}))` is the form Adonis documents.
|
|
490
|
+
*/
|
|
491
|
+
export declare function create<S extends Record<string, RuleChain>>(fields: S): ValidationSchema<Infer<S>>;
|
|
492
|
+
export declare function create(chain: RuleChain): ValidationSchema;
|
|
149
493
|
export declare function setValidationTranslator(translator?: ValidationTranslator): void;
|
|
150
494
|
export declare function bindRosetta(rosetta: {
|
|
151
495
|
t(key: string, params?: ValidationMessageParams): string;
|
|
@@ -156,10 +500,24 @@ export interface RuleDef {
|
|
|
156
500
|
param?: number;
|
|
157
501
|
/** Interpolation args exposed to i18n/messages providers (e.g. `{ min: 3 }`). */
|
|
158
502
|
args?: Record<string, unknown>;
|
|
159
|
-
|
|
503
|
+
/**
|
|
504
|
+
* `field` is the context VineJS hands a rule — a callback-valued rule
|
|
505
|
+
* (`in`, `notIn`, `enum`) reads `meta`/`parent` off it to compute its list
|
|
506
|
+
* per request. Rules that do not need it simply declare one parameter.
|
|
507
|
+
*/
|
|
508
|
+
validate: (value: unknown, field: FieldContext) => boolean;
|
|
160
509
|
message: string;
|
|
161
510
|
/** Set when `.message()` overrode this rule's default text. */
|
|
162
511
|
hasCustomMessage?: boolean;
|
|
512
|
+
/**
|
|
513
|
+
* Keep this rule off the Rust path even though its NAME is in
|
|
514
|
+
* {@link NATIVE_RULES}. Set by options the native engine does not know about
|
|
515
|
+
* (`uuid({ version })`, a callback list for `in` / `notIn`): the engine would
|
|
516
|
+
* run the rule without them and silently answer a different question.
|
|
517
|
+
*/
|
|
518
|
+
tsOnly?: boolean;
|
|
519
|
+
/** Modifier this rule applies to its field's JSON Schema node. */
|
|
520
|
+
toJSONSchema?: JsonSchemaModifier;
|
|
163
521
|
}
|
|
164
522
|
/** A conditional-required condition (VineJS `requiredWhen` family). */
|
|
165
523
|
interface RequiredCondition {
|
|
@@ -171,6 +529,11 @@ interface RequiredCondition {
|
|
|
171
529
|
/** Phantom brand carrying the inferred output type (never assigned at runtime). */
|
|
172
530
|
declare const OUTPUT: unique symbol;
|
|
173
531
|
/** Rule chain — fluent, phantom-typed validation builder. */
|
|
532
|
+
/**
|
|
533
|
+
* The value list accepted by `in` / `notIn` / `enum` — static, or computed at
|
|
534
|
+
* validation time (VineJS parity).
|
|
535
|
+
*/
|
|
536
|
+
export type AllowedValues = ReadonlyArray<string | number | boolean> | ((field: FieldContext) => ReadonlyArray<string | number | boolean>);
|
|
174
537
|
export declare class RuleChain<Output = unknown> {
|
|
175
538
|
#private;
|
|
176
539
|
/** Phantom output type — drives {@link Infer}; never read at runtime. */
|
|
@@ -186,8 +549,30 @@ export declare class RuleChain<Output = unknown> {
|
|
|
186
549
|
}>;
|
|
187
550
|
/** Public read access to `.use()` rules (used to keep such schemas off the native path). */
|
|
188
551
|
get useRules(): readonly CompiledRule[];
|
|
552
|
+
/** Public read access to async rules (`unique`/`exists`/`useAsync`) — run by `validateResultAsync`. */
|
|
553
|
+
get asyncRules(): readonly AsyncCompiledRule[];
|
|
554
|
+
/**
|
|
555
|
+
* Does this chain — or anything nested under it (object fields, array items) —
|
|
556
|
+
* carry async rules? The schema-level detection used to inspect only the
|
|
557
|
+
* top-level chains, so a nested `unique`/`exists` was invisible: `validate()`
|
|
558
|
+
* did not throw and the async pass never ran the rule, silently accepting
|
|
559
|
+
* an unchecked value.
|
|
560
|
+
*/
|
|
561
|
+
get hasAsyncRulesDeep(): boolean;
|
|
562
|
+
/** Does this object keep keys its shape does not declare? */
|
|
563
|
+
get allowsUnknown(): boolean;
|
|
564
|
+
/** Free-form JSON Schema metadata attached with `meta()`. */
|
|
565
|
+
get metadata(): Record<string, unknown> | null;
|
|
566
|
+
/** The item chain of an `array()`, if declared. */
|
|
567
|
+
get arrayItem(): RuleChain | null;
|
|
568
|
+
/** The positional chains of a `tuple()`, if declared. */
|
|
569
|
+
get tupleItems(): RuleChain[] | null;
|
|
570
|
+
/** The value chain of a `record()`, if declared. */
|
|
571
|
+
get recordValue(): RuleChain | null;
|
|
572
|
+
/** Whether this chain stops at its first failing rule (VineJS `bail`). */
|
|
573
|
+
get bails(): boolean;
|
|
189
574
|
/** Public read access to `.parse()` pre-transforms (kept off the native path). */
|
|
190
|
-
get preTransforms(): ReadonlyArray<(value: unknown) => unknown>;
|
|
575
|
+
get preTransforms(): ReadonlyArray<(value: unknown, ctx: ParseContext) => unknown>;
|
|
191
576
|
/** Whether this chain carries a `requiredWhen`-family condition. */
|
|
192
577
|
get hasConditionalRequired(): boolean;
|
|
193
578
|
/** Mark field as optional (absent / `undefined` allowed). */
|
|
@@ -204,12 +589,210 @@ export declare class RuleChain<Output = unknown> {
|
|
|
204
589
|
array<Item extends RuleChain>(itemChain?: Item): RuleChain<OutputOf<Item>[]>;
|
|
205
590
|
/** Must be a string. */
|
|
206
591
|
string(): RuleChain<string>;
|
|
207
|
-
/**
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
592
|
+
/**
|
|
593
|
+
* Must be a number. Like VineJS, a numeric STRING is coerced (`"32"` → `32`)
|
|
594
|
+
* — HTML form bodies and query strings carry numbers as text, so requiring
|
|
595
|
+
* `typeof v === "number"` rejected the values Adonis accepts. Pass
|
|
596
|
+
* `{ strict: true }` to refuse anything that is not already a number.
|
|
597
|
+
*/
|
|
598
|
+
number(options?: {
|
|
599
|
+
strict?: boolean;
|
|
600
|
+
}): RuleChain<number>;
|
|
601
|
+
/**
|
|
602
|
+
* Must be a boolean. Like VineJS, `"true"`, `"false"`, `"on"`, `"off"`,
|
|
603
|
+
* `"1"`, `"0"`, `1` and `0` are coerced; `{ strict: true }` refuses them.
|
|
604
|
+
*/
|
|
605
|
+
boolean(options?: {
|
|
606
|
+
strict?: boolean;
|
|
607
|
+
}): RuleChain<boolean>;
|
|
608
|
+
/**
|
|
609
|
+
* Must be a date (VineJS `vine.date()`). ISO 8601 by default; pass `formats`
|
|
610
|
+
* for unix timestamps (`x` = ms, `X` = seconds) or a token format such as
|
|
611
|
+
* `DD/MM/YYYY`. Parsing is calendar-strict — `2026-02-31` is rejected.
|
|
612
|
+
*
|
|
613
|
+
* The validated output is a `Date`; bind {@link setDateTransform} to map it
|
|
614
|
+
* to your own type once at boot.
|
|
615
|
+
*/
|
|
616
|
+
date(options?: {
|
|
617
|
+
formats?: DateFormat[];
|
|
618
|
+
}): RuleChain<Date>;
|
|
619
|
+
/** Must be strictly after `operand` (`'today'`, an ISO string, or a `Date`). */
|
|
620
|
+
after(operand: unknown, options?: DateCompareOptions): this;
|
|
621
|
+
/** Must be strictly before `operand`. */
|
|
622
|
+
before(operand: unknown, options?: DateCompareOptions): this;
|
|
623
|
+
/** Must be after `operand`, or equal to it. */
|
|
624
|
+
afterOrEqual(operand: unknown, options?: DateCompareOptions): this;
|
|
625
|
+
/** Must be before `operand`, or equal to it. */
|
|
626
|
+
beforeOrEqual(operand: unknown, options?: DateCompareOptions): this;
|
|
627
|
+
/** Must be after the date held by a sibling field (VineJS `afterField`). */
|
|
628
|
+
afterField(otherField: string, options?: DateCompareOptions): this;
|
|
629
|
+
/** Must be before the date held by a sibling field. */
|
|
630
|
+
beforeField(otherField: string, options?: DateCompareOptions): this;
|
|
631
|
+
/** Must be the same instant as `operand` (VineJS `equals`). */
|
|
632
|
+
equals(operand: unknown, options?: DateCompareOptions): this;
|
|
633
|
+
/** Must be after the sibling's date, or the same instant (VineJS `afterOrSameAs`). */
|
|
634
|
+
afterOrSameAs(otherField: string, options?: DateCompareOptions): this;
|
|
635
|
+
/** Must be before the sibling's date, or the same instant. */
|
|
636
|
+
beforeOrSameAs(otherField: string, options?: DateCompareOptions): this;
|
|
637
|
+
/** Must fall on a Saturday or Sunday (VineJS `weekend`). */
|
|
638
|
+
weekend(): this;
|
|
639
|
+
/** Must fall on a Monday-to-Friday day (VineJS `weekday`). */
|
|
640
|
+
weekday(): this;
|
|
641
|
+
/**
|
|
642
|
+
* Keep keys the object shape does not declare (VineJS
|
|
643
|
+
* `allowUnknownProperties`). Off by default: dropping undeclared keys is what
|
|
644
|
+
* makes a validated payload safe to hand to a mass assignment.
|
|
645
|
+
*/
|
|
646
|
+
allowUnknownProperties(): this;
|
|
647
|
+
/**
|
|
648
|
+
* Convert the object's KEYS to camelCase in the output (VineJS
|
|
649
|
+
* `object.toCamelCase()`), so a snake_case payload hydrates camelCase
|
|
650
|
+
* properties. Distinct from the string `toCamelCase()`, which rewrites a
|
|
651
|
+
* VALUE — that one was never a substitute for this.
|
|
652
|
+
*/
|
|
653
|
+
toCamelCaseKeys(): this;
|
|
654
|
+
/**
|
|
655
|
+
* Merge extra properties into this object's shape (VineJS `merge`). Accepts a
|
|
656
|
+
* plain shape or a {@link ConditionalGroup} whose branch is chosen per
|
|
657
|
+
* payload — `vine.group` in VineJS.
|
|
658
|
+
*/
|
|
659
|
+
merge(extra: Record<string, RuleChain> | ConditionalGroup): this;
|
|
660
|
+
/** The nested shape declared by `object()`, if any (VineJS `getProperties`). */
|
|
661
|
+
getProperties(): Record<string, RuleChain> | null;
|
|
662
|
+
/** Independent copy of this chain (VineJS `clone`). */
|
|
663
|
+
clone(): RuleChain<Output>;
|
|
664
|
+
/**
|
|
665
|
+
* A CLONED subset of the object's properties (VineJS `pick`).
|
|
666
|
+
*
|
|
667
|
+
* Returns a properties record, not a schema — VineJS types it
|
|
668
|
+
* `Pick<Properties, Keys>` precisely so it composes by spread:
|
|
669
|
+
* `rules.any().object({ ...userShape.pick(["id"]) })`. Returning a chain here
|
|
670
|
+
* broke that idiom.
|
|
671
|
+
*/
|
|
672
|
+
pick<K extends string>(keys: readonly K[]): Record<string, RuleChain>;
|
|
673
|
+
/** A cloned copy of the properties EXCLUDING `keys` (VineJS `omit`). */
|
|
674
|
+
omit<K extends string>(keys: readonly K[]): Record<string, RuleChain>;
|
|
675
|
+
/** Make every property of an object shape optional (VineJS `partial`). */
|
|
676
|
+
partial(keys?: readonly string[]): RuleChain<Output>;
|
|
677
|
+
/**
|
|
678
|
+
* Must be an "accepted" value — `true`, `1`, `"1"`, `"on"`, `"yes"`,
|
|
679
|
+
* `"true"` (VineJS `accepted`, for checkbox-style consent fields).
|
|
680
|
+
*/
|
|
681
|
+
accepted(): RuleChain<true>;
|
|
682
|
+
/**
|
|
683
|
+
* Object with arbitrary keys, every value validated by `valueChain`
|
|
684
|
+
* (VineJS `record`).
|
|
685
|
+
*/
|
|
686
|
+
record<Item extends RuleChain>(valueChain: Item): RuleChain<Record<string, OutputOf<Item>>>;
|
|
687
|
+
/**
|
|
688
|
+
* Check the record's KEYS, not its values (VineJS `record().validateKeys()`).
|
|
689
|
+
*
|
|
690
|
+
* The callback receives every key at once and reports through the field
|
|
691
|
+
* context — the set is what matters when keys must be exclusive, exhaustive,
|
|
692
|
+
* or drawn from a list only known at runtime.
|
|
693
|
+
*/
|
|
694
|
+
validateKeys(callback: RecordKeysCallback): this;
|
|
695
|
+
/**
|
|
696
|
+
* Fixed-length array with a schema per position (VineJS `tuple`). Extra
|
|
697
|
+
* items are rejected — a tuple that silently ignores a trailing element is
|
|
698
|
+
* how unvalidated data slips through.
|
|
699
|
+
*/
|
|
700
|
+
tuple<const Items extends readonly RuleChain[]>(items: Items): RuleChain<{
|
|
701
|
+
[K in keyof Items]: OutputOf<Items[K]>;
|
|
702
|
+
}>;
|
|
703
|
+
/**
|
|
704
|
+
* Value must satisfy at least one of `chains`.
|
|
705
|
+
*
|
|
706
|
+
* Two forms, both supported:
|
|
707
|
+
*
|
|
708
|
+
* - guarded (VineJS parity): `union([rules.union.if(pred, chain), …,
|
|
709
|
+
* rules.union.else(fallback)])` — the predicate SELECTS the branch and
|
|
710
|
+
* that branch's own errors are reported, so a failure says which shape was
|
|
711
|
+
* meant and why it did not fit.
|
|
712
|
+
* - bare chains: tried in order, first match wins, and a total miss reports a
|
|
713
|
+
* single `union` error rather than every losing branch's noise.
|
|
714
|
+
*/
|
|
715
|
+
/**
|
|
716
|
+
* What to do when NO union branch matched (VineJS `union().otherwise()`).
|
|
717
|
+
*
|
|
718
|
+
* The callback receives the value and the field, and reports the error it
|
|
719
|
+
* wants — the point being that "matches nothing" is a useless message when
|
|
720
|
+
* the caller knows which shapes were on offer. Reporting nothing from the
|
|
721
|
+
* callback suppresses the generic error entirely, which is how a union
|
|
722
|
+
* folded into a larger check stays quiet.
|
|
723
|
+
*/
|
|
724
|
+
otherwise(callback: UnionNoMatchCallback): this;
|
|
725
|
+
union(chains: readonly UnionBranch[]): this;
|
|
726
|
+
/**
|
|
727
|
+
* Must be an uploaded file (VineJS/Adonis `vine.file()`).
|
|
728
|
+
*
|
|
729
|
+
* Named deviation: Adonis validates a bodyparser `MultipartFile`, which rune
|
|
730
|
+
* cannot import and stay agnostic. It checks the STRUCTURE instead — any
|
|
731
|
+
* object exposing `size` and a name/extension — so an Adonis MultipartFile
|
|
732
|
+
* satisfies it, and so does any other upload representation.
|
|
733
|
+
*
|
|
734
|
+
* `size` is a byte count; `extnames` are compared lowercase, without the dot.
|
|
735
|
+
*/
|
|
736
|
+
file(options?: {
|
|
737
|
+
size?: number | string;
|
|
738
|
+
extnames?: readonly string[];
|
|
739
|
+
/**
|
|
740
|
+
* Skip the magic-number check. Default `true` — Adonis derives `extname`
|
|
741
|
+
* from the real bytes before validation, so trusting the declaration is
|
|
742
|
+
* NOT the safe default: a `.exe` renamed `.png` satisfies every
|
|
743
|
+
* declarative check, since all of them come from the uploader.
|
|
744
|
+
*/
|
|
745
|
+
verifyContent?: boolean;
|
|
746
|
+
}): RuleChain<FileLike>;
|
|
747
|
+
/**
|
|
748
|
+
* Uploaded file with VineJS `nativeFile` options — `minSize`, `maxSize`,
|
|
749
|
+
* `mimeTypes`. Same structural contract as {@link file}: rune never reads
|
|
750
|
+
* bytes, so the MIME type is the one the upload REPORTS.
|
|
751
|
+
*/
|
|
752
|
+
nativeFile(options?: {
|
|
753
|
+
minSize?: number | string;
|
|
754
|
+
maxSize?: number | string;
|
|
755
|
+
mimeTypes?: readonly string[];
|
|
756
|
+
}): RuleChain<FileLike>;
|
|
757
|
+
/** Minimum upload size (VineJS `nativeFile().minSize()`). */
|
|
758
|
+
minSize(size: number | string): this;
|
|
759
|
+
/** Maximum upload size (VineJS `nativeFile().maxSize()`). */
|
|
760
|
+
maxSize(size: number | string): this;
|
|
761
|
+
/**
|
|
762
|
+
* Allowed MIME types (VineJS `nativeFile().mimeTypes()`). The type is the one
|
|
763
|
+
* the upload REPORTS — rune never reads bytes, see {@link file}.
|
|
764
|
+
*/
|
|
765
|
+
mimeTypes(types: readonly string[]): this;
|
|
766
|
+
/**
|
|
767
|
+
* Verify the file's REAL type against its magic number (Adonis parity).
|
|
768
|
+
*
|
|
769
|
+
* A `.exe` renamed `.jpg` passes every declarative check — size, extension,
|
|
770
|
+
* reported MIME — because all three come from the uploader. This reads the
|
|
771
|
+
* leading bytes and refuses a mismatch.
|
|
772
|
+
*
|
|
773
|
+
* Async by nature (it touches the filesystem), so the schema must run with
|
|
774
|
+
* `validateResultAsync` / `validate`. Needs a byte source on the file object
|
|
775
|
+
* (`buffer`, `tmpPath`, `filePath` or `path`) — an Adonis `MultipartFile`
|
|
776
|
+
* carries `tmpPath`. With NO source it FAILS: a content check that cannot
|
|
777
|
+
* run must never look like one that passed.
|
|
778
|
+
*/
|
|
779
|
+
verifyContent(): this;
|
|
780
|
+
/**
|
|
781
|
+
* Must equal one of `values` (enum). Narrows the output to the union.
|
|
782
|
+
*
|
|
783
|
+
* `values` may be a callback receiving the field, which is how a list that
|
|
784
|
+
* depends on the request — the roles this tenant allows, the statuses this
|
|
785
|
+
* user may set — is computed per validation instead of frozen at import.
|
|
786
|
+
*/
|
|
787
|
+
enum<const V extends readonly (string | number | boolean)[]>(values: V | ((field: FieldContext) => V)): RuleChain<V[number]>;
|
|
788
|
+
/**
|
|
789
|
+
* The choices this enum was declared with (VineJS `getChoices()`) — the list
|
|
790
|
+
* itself, or the callback when it is computed per request.
|
|
791
|
+
*
|
|
792
|
+
* Reading them back is what lets a form render the same options the
|
|
793
|
+
* validator will accept, from one declaration instead of two.
|
|
794
|
+
*/
|
|
795
|
+
getChoices(): ReadonlyArray<string | number | boolean> | ((field: FieldContext) => ReadonlyArray<string | number | boolean>) | undefined;
|
|
213
796
|
/** Must equal a literal value. */
|
|
214
797
|
literal<V extends string | number | boolean>(value: V): RuleChain<V>;
|
|
215
798
|
/** Minimum length (string) or minimum value (number). Alias of min/minLength. */
|
|
@@ -223,25 +806,133 @@ export declare class RuleChain<Output = unknown> {
|
|
|
223
806
|
/** Exact length for a string or array (VineJS `fixedLength`). */
|
|
224
807
|
fixedLength(n: number): this;
|
|
225
808
|
/** Must be a valid email. */
|
|
226
|
-
email(): this;
|
|
809
|
+
email(options?: EmailOptions): this;
|
|
227
810
|
/** Must match a regular expression (TS-only — never dispatched to Rust). */
|
|
228
811
|
regex(pattern: RegExp): this;
|
|
229
812
|
/** Must be a valid URL (TS-only — uses the WHATWG URL parser). */
|
|
230
|
-
url(): this;
|
|
231
|
-
/**
|
|
232
|
-
|
|
813
|
+
url(options?: UrlOptions): this;
|
|
814
|
+
/**
|
|
815
|
+
* The host must actually resolve (VineJS `activeUrl`).
|
|
816
|
+
*
|
|
817
|
+
* The only rule needing the network, which rune cannot do and stay agnostic
|
|
818
|
+
* and zero-dependency — so it runs through a resolver bound once at boot,
|
|
819
|
+
* exactly like `unique()`. Async by nature: run the schema with
|
|
820
|
+
* `validateResultAsync`. Unbound it THROWS, rather than passing a host nobody
|
|
821
|
+
* checked.
|
|
822
|
+
*/
|
|
823
|
+
activeUrl(): this;
|
|
824
|
+
/**
|
|
825
|
+
* Must be a valid UUID, optionally restricted to given versions
|
|
826
|
+
* (VineJS `uuid({ version: [4] })`, versions 1 through 8).
|
|
827
|
+
*/
|
|
828
|
+
uuid(options?: {
|
|
829
|
+
version?: number | number[];
|
|
830
|
+
}): this;
|
|
831
|
+
/** Must be a ULID (VineJS `ulid`). */
|
|
832
|
+
ulid(): this;
|
|
833
|
+
/** Must be a JSON Web Token — three dot-separated base64url segments. */
|
|
834
|
+
jwt(): this;
|
|
835
|
+
/** Must contain only ASCII characters (VineJS `ascii`). */
|
|
836
|
+
ascii(): this;
|
|
837
|
+
/** Must be a CSS hex colour code, with or without the leading `#`. */
|
|
838
|
+
hexCode(): this;
|
|
839
|
+
/** Must be an IP address. Pass `version` to require v4 or v6 specifically. */
|
|
840
|
+
ipAddress(options?: {
|
|
841
|
+
version?: 4 | 6;
|
|
842
|
+
}): this;
|
|
843
|
+
/** Must pass the Luhn checksum (VineJS `creditCard`). */
|
|
844
|
+
creditCard(): this;
|
|
845
|
+
/** Must be an IBAN passing the ISO 13616 mod-97 check. */
|
|
846
|
+
iban(): this;
|
|
847
|
+
/** Must be a `"lat,lng"` pair within the valid ranges. */
|
|
848
|
+
coordinates(): this;
|
|
849
|
+
/**
|
|
850
|
+
* Must be a mobile number (VineJS/Adonis `mobile()`).
|
|
851
|
+
*
|
|
852
|
+
* With no `locale`, the number must be in E.164 form. With one or more,
|
|
853
|
+
* it must match one of their numbering plans, as in VineJS. rune carries
|
|
854
|
+
* its own plans rather than validator.js', so it knows fewer locales — an
|
|
855
|
+
* unknown one raises `UNSUPPORTED_LOCALE` at schema build, naming the ones
|
|
856
|
+
* it does know, rather than silently accepting anything at request time.
|
|
857
|
+
*/
|
|
858
|
+
mobile(options?: {
|
|
859
|
+
locale?: string | string[];
|
|
860
|
+
strictMode?: boolean;
|
|
861
|
+
}): this;
|
|
862
|
+
/**
|
|
863
|
+
* Must be a postal code for `countryCode`. Throws for a country rune has no
|
|
864
|
+
* pattern for, rather than accepting the value unchecked.
|
|
865
|
+
*/
|
|
866
|
+
postalCode(options: {
|
|
867
|
+
countryCode: string | string[];
|
|
868
|
+
} | ((field: FieldContext) => {
|
|
869
|
+
countryCode: string | string[];
|
|
870
|
+
})): this;
|
|
871
|
+
/**
|
|
872
|
+
* Must be a valid VAT number (VineJS 4.2 `vat`). Accepts a country list or a
|
|
873
|
+
* callback resolving it per payload.
|
|
874
|
+
*
|
|
875
|
+
* Checksums are run where the country defines a short, well-defined one
|
|
876
|
+
* (BE, DE, NL, IT, PT, LU, CH); the others are FORMAT-only, which is stated
|
|
877
|
+
* rather than implied. An unknown country LEVES rather than accepting the
|
|
878
|
+
* value unchecked.
|
|
879
|
+
*/
|
|
880
|
+
vat(options: VatOptions | ((field: FieldContext) => VatOptions)): this;
|
|
881
|
+
/** Must differ from a sibling field (VineJS `notSameAs`). */
|
|
882
|
+
notSameAs(otherField: string): this;
|
|
883
|
+
/** Array items must be unique — optionally compared on `field` (VineJS `distinct`). */
|
|
884
|
+
distinct(field?: string | string[]): this;
|
|
885
|
+
/** Must be less than or equal to zero (VineJS `nonPositive`). */
|
|
886
|
+
nonPositive(): this;
|
|
887
|
+
/** Array must hold at least one item (VineJS `notEmpty`). */
|
|
888
|
+
notEmpty(): this;
|
|
889
|
+
/** Drop `null`, `undefined` and `""` items before the item rules run. */
|
|
890
|
+
compact(): this;
|
|
891
|
+
/** Number must have no fractional part (VineJS `withoutDecimals`). */
|
|
892
|
+
withoutDecimals(): this;
|
|
893
|
+
/** Must be a passport number for `countryCode`. Throws for an uncovered country. */
|
|
894
|
+
passport(options: {
|
|
895
|
+
countryCode: string | string[];
|
|
896
|
+
}): this;
|
|
897
|
+
/** Lowercase the value (VineJS `toLowerCase`). */
|
|
898
|
+
toLowerCase(): this;
|
|
899
|
+
/** Uppercase the value (VineJS `toUpperCase`). */
|
|
900
|
+
toUpperCase(): this;
|
|
901
|
+
/**
|
|
902
|
+
* VineJS `toCamelCase()`, on both shapes it exists for:
|
|
903
|
+
*
|
|
904
|
+
* - on an `object()` chain it camelCases the object's KEYS
|
|
905
|
+
* (`VineObject.toCamelCase`);
|
|
906
|
+
* - on any other chain it camelCases the string VALUE (`VineString`).
|
|
907
|
+
*
|
|
908
|
+
* One name, because Vine has one name. Dispatching on whether a nested shape
|
|
909
|
+
* was declared is what keeps a transcribed validator behaving the same.
|
|
910
|
+
*/
|
|
911
|
+
toCamelCase(): this;
|
|
912
|
+
/** HTML-escape `& < > " '` (VineJS `escape`). */
|
|
913
|
+
escape(): this;
|
|
914
|
+
/** Normalise an email address (VineJS `normalizeEmail`). */
|
|
915
|
+
normalizeEmail(options?: NormalizeEmailOptions): this;
|
|
916
|
+
/** Normalise a URL (VineJS `normalizeUrl`). */
|
|
917
|
+
normalizeUrl(options?: NormalizeUrlOptions): this;
|
|
233
918
|
/** Must contain only ASCII letters. */
|
|
234
|
-
alpha(): this;
|
|
919
|
+
alpha(options?: AlphaOptions): this;
|
|
235
920
|
/** Must contain only ASCII letters and digits. */
|
|
236
|
-
alphaNumeric(): this;
|
|
921
|
+
alphaNumeric(options?: AlphaOptions): this;
|
|
237
922
|
/** String must start with `substring`. */
|
|
238
923
|
startsWith(substring: string): this;
|
|
239
924
|
/** String must end with `substring`. */
|
|
240
925
|
endsWith(substring: string): this;
|
|
241
|
-
/**
|
|
242
|
-
|
|
926
|
+
/**
|
|
927
|
+
* Value must be one of `values`.
|
|
928
|
+
*
|
|
929
|
+
* VineJS also accepts a callback so the list can be computed at validation
|
|
930
|
+
* time (tenant-scoped roles, values read from config…). A static array is
|
|
931
|
+
* snapshotted; a callback is invoked on every check.
|
|
932
|
+
*/
|
|
933
|
+
in(values: AllowedValues): this;
|
|
243
934
|
/** Value must NOT be one of `values`. */
|
|
244
|
-
notIn(values:
|
|
935
|
+
notIn(values: AllowedValues): this;
|
|
245
936
|
/** Number must be positive (> 0) and finite. */
|
|
246
937
|
positive(): this;
|
|
247
938
|
/** Number must be negative (< 0) and finite. */
|
|
@@ -249,13 +940,14 @@ export declare class RuleChain<Output = unknown> {
|
|
|
249
940
|
/** Number must be >= 0 and finite. */
|
|
250
941
|
nonNegative(): this;
|
|
251
942
|
/** Number must fall within `[min, max]` (inclusive). */
|
|
252
|
-
range(min: number, max: number): this;
|
|
943
|
+
range(bounds: [min: number, max: number]): this;
|
|
253
944
|
/** Number must have at most `digits` decimal places (TS-only). */
|
|
254
|
-
decimal(digits: number): this;
|
|
945
|
+
decimal(digits: number | [number, number]): this;
|
|
255
946
|
/** Must equal a sibling field (VineJS `sameAs`). Cross-field → TS-only. */
|
|
256
947
|
sameAs(otherField: string): this;
|
|
257
948
|
/** Must equal its `<field>_confirmation` sibling (VineJS `confirmed`). */
|
|
258
949
|
confirmed(options?: {
|
|
950
|
+
as?: string;
|
|
259
951
|
confirmationField?: string;
|
|
260
952
|
}): this;
|
|
261
953
|
/** Required only when `otherField` is present (non-null) — else optional. */
|
|
@@ -273,7 +965,7 @@ export declare class RuleChain<Output = unknown> {
|
|
|
273
965
|
*/
|
|
274
966
|
transform<U>(fn: (value: unknown, field: FieldContext) => U): RuleChain<U>;
|
|
275
967
|
/** Pre-validation transform of the raw input (VineJS `parse`). */
|
|
276
|
-
parse(fn: (value: unknown) => unknown): this;
|
|
968
|
+
parse(fn: (value: unknown, ctx: ParseContext) => unknown): this;
|
|
277
969
|
/** Custom validation rule. */
|
|
278
970
|
custom(name: string, validate: (value: unknown) => boolean, message?: string): this;
|
|
279
971
|
/**
|
|
@@ -281,14 +973,72 @@ export declare class RuleChain<Output = unknown> {
|
|
|
281
973
|
* receives a {@link FieldContext} with the root `data` and `parent`, so it can
|
|
282
974
|
* validate across fields. Runs after this field's type/value rules.
|
|
283
975
|
*/
|
|
284
|
-
use(rule: CompiledRule): this;
|
|
285
|
-
/**
|
|
976
|
+
use(rule: CompiledRule | AsyncCompiledRule): this;
|
|
977
|
+
/**
|
|
978
|
+
* Attach an async rule (from {@link createAsyncRule}). The schema must then be
|
|
979
|
+
* run with `validateResultAsync` — sync `validate()` throws for such a schema.
|
|
980
|
+
*/
|
|
981
|
+
useAsync(rule: AsyncCompiledRule): this;
|
|
982
|
+
/**
|
|
983
|
+
* DB-backed uniqueness rule (Adonis Lucid `unique`). `check(value, field)`
|
|
984
|
+
* resolves `true` when the value is unique (valid). rune stays agnostic — the
|
|
985
|
+
* check does the query (e.g. against atlas). Requires the async path (`validateResultAsync` / `validate`).
|
|
986
|
+
*
|
|
987
|
+
* rules.string().email().unique(async (value) => {
|
|
988
|
+
* const row = await db.from('users').where('email', value).first()
|
|
989
|
+
* return !row
|
|
990
|
+
* })
|
|
991
|
+
*/
|
|
992
|
+
unique(check: (value: unknown, field: FieldContext) => boolean | Promise<boolean>, message?: string): this;
|
|
993
|
+
unique(options: DatabaseRuleOptions, message?: string): this;
|
|
994
|
+
/**
|
|
995
|
+
* DB-backed existence rule (Adonis Lucid `exists`). `check(value, field)`
|
|
996
|
+
* resolves `true` when a matching row exists (valid). Requires the async path (`validateResultAsync` / `validate`).
|
|
997
|
+
*/
|
|
998
|
+
exists(check: (value: unknown, field: FieldContext) => boolean | Promise<boolean>, message?: string): this;
|
|
999
|
+
exists(options: DatabaseRuleOptions, message?: string): this;
|
|
1000
|
+
/**
|
|
1001
|
+
* Attach free-form JSON Schema metadata (VineJS `meta()`) — `title`,
|
|
1002
|
+
* `description`, `examples`, `deprecated`… Merged verbatim into the field's
|
|
1003
|
+
* node by `toJSONSchema()`.
|
|
1004
|
+
*/
|
|
1005
|
+
meta(metadata: Record<string, unknown>): this;
|
|
1006
|
+
/**
|
|
1007
|
+
* Set a custom error message for the rule that was just added.
|
|
1008
|
+
*
|
|
1009
|
+
* "The last rule" spans all three registers: value rules (`#rules`),
|
|
1010
|
+
* cross-field `.use()` rules (`sameAs`, `confirmed`, `afterField`,
|
|
1011
|
+
* `notSameAs`) and async rules (`unique`, `exists`, `useAsync`). Targeting
|
|
1012
|
+
* `#rules` alone silently retargeted the PREVIOUS value rule — or threw
|
|
1013
|
+
* `NO_RULE` — whenever the preceding call was a cross-field or async rule.
|
|
1014
|
+
*/
|
|
286
1015
|
message(msg: string): this;
|
|
287
|
-
/**
|
|
288
|
-
|
|
1016
|
+
/**
|
|
1017
|
+
* Register a TYPE rule from outside the chain — used by the `optional()` and
|
|
1018
|
+
* `null()` factories, which are types in their own right.
|
|
1019
|
+
* @internal
|
|
1020
|
+
*/
|
|
1021
|
+
pushTypeRule(rule: RuleDef): void;
|
|
1022
|
+
/** Re-type this chain in place, without cloning. @internal */
|
|
1023
|
+
retypeTo<U>(): RuleChain<U>;
|
|
1024
|
+
/**
|
|
1025
|
+
* Internal: validate a field value and return errors + transformed value.
|
|
1026
|
+
*
|
|
1027
|
+
* `pending` is the async-rule collector. The traversal itself stays sync (it
|
|
1028
|
+
* is shared with `validate()`); when a collector is supplied, every chain in
|
|
1029
|
+
* the tree that carries async rules and passed its sync rules records itself
|
|
1030
|
+
* for the async path to await. Without it, nested async rules never ran.
|
|
1031
|
+
*/
|
|
1032
|
+
_validateWithTransform(field: string, rawValue: unknown, ctx?: RunContext, pending?: PendingAsync[]): {
|
|
289
1033
|
errors: ValidationError[];
|
|
290
1034
|
transformed: unknown;
|
|
291
1035
|
};
|
|
1036
|
+
/**
|
|
1037
|
+
* Run this chain's async rules on the (already sync-validated) value, awaiting
|
|
1038
|
+
* each in order. Returns the errors they reported. Used by `validateResultAsync`.
|
|
1039
|
+
* @internal
|
|
1040
|
+
*/
|
|
1041
|
+
_runAsyncRules(field: string, transformed: unknown, ctx: RunContext): Promise<ValidationError[]>;
|
|
292
1042
|
/** Internal: validate a field value against all rules. */
|
|
293
1043
|
_validate(field: string, value: unknown): ValidationError[];
|
|
294
1044
|
/** Internal: apply transforms. */
|
|
@@ -296,15 +1046,61 @@ export declare class RuleChain<Output = unknown> {
|
|
|
296
1046
|
}
|
|
297
1047
|
/** No-op alias of {@link schema} — VineJS `vine.compile()` API parity. */
|
|
298
1048
|
export declare function compile<T extends ValidationSchema>(s: T): T;
|
|
1049
|
+
export declare function compile(chain: RuleChain): ValidationSchema;
|
|
299
1050
|
/** Entry point for building rules. */
|
|
300
1051
|
export declare const rules: {
|
|
301
1052
|
string: () => RuleChain<string>;
|
|
302
|
-
number: (
|
|
303
|
-
|
|
1053
|
+
number: (options?: {
|
|
1054
|
+
strict?: boolean;
|
|
1055
|
+
}) => RuleChain<number>;
|
|
1056
|
+
boolean: (options?: {
|
|
1057
|
+
strict?: boolean;
|
|
1058
|
+
}) => RuleChain<boolean>;
|
|
304
1059
|
any: () => RuleChain<unknown>;
|
|
1060
|
+
date: (options?: {
|
|
1061
|
+
formats?: DateFormat[];
|
|
1062
|
+
}) => RuleChain<Date>;
|
|
1063
|
+
accepted: () => RuleChain<true>;
|
|
1064
|
+
file: (options?: {
|
|
1065
|
+
size?: number | string;
|
|
1066
|
+
extnames?: readonly string[];
|
|
1067
|
+
verifyContent?: boolean;
|
|
1068
|
+
}) => RuleChain<FileLike>;
|
|
1069
|
+
nativeFile: (options?: {
|
|
1070
|
+
minSize?: number | string;
|
|
1071
|
+
maxSize?: number | string;
|
|
1072
|
+
mimeTypes?: readonly string[];
|
|
1073
|
+
}) => RuleChain<FileLike>;
|
|
1074
|
+
record: <Item extends RuleChain>(valueChain: Item) => RuleChain<Record<string, OutputOf<Item>>>;
|
|
1075
|
+
tuple: <const Items extends readonly RuleChain[]>(items: Items) => RuleChain<{ [K in keyof Items]: OutputOf<Items[K]>; }>;
|
|
1076
|
+
union: ((chains: readonly UnionBranch[]) => RuleChain) & {
|
|
1077
|
+
if: typeof unionIf;
|
|
1078
|
+
else: typeof unionElse;
|
|
1079
|
+
otherwise: typeof unionElse;
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* Union discriminated by the value's TYPE (VineJS `unionOfTypes`): the first
|
|
1083
|
+
* branch whose own type rule accepts the value wins.
|
|
1084
|
+
*/
|
|
1085
|
+
/**
|
|
1086
|
+
* Make every property of a shape optional (VineJS `vine.helpers.optional`).
|
|
1087
|
+
* A properties TRANSFORMER, like `pick`/`omit` — it returns a record to
|
|
1088
|
+
* spread, not a schema.
|
|
1089
|
+
*/
|
|
1090
|
+
/**
|
|
1091
|
+
* A field that must be ABSENT (VineJS `vine.optional()` → `VineOptional`,
|
|
1092
|
+
* `builder.d.ts:135`). Mostly a `unionOfTypes` branch. Distinct from
|
|
1093
|
+
* `.optional()` on a chain, which relaxes an existing type — this one IS the
|
|
1094
|
+
* type. The properties transformer that used to squat this name moved to
|
|
1095
|
+
* `helpers.optional`, where VineJS keeps it.
|
|
1096
|
+
*/
|
|
1097
|
+
optional: () => RuleChain<undefined>;
|
|
1098
|
+
/** A field that must be `null` (VineJS `vine.null()` → `VineNull`). */
|
|
1099
|
+
null: () => RuleChain<null>;
|
|
1100
|
+
unionOfTypes: (chains: readonly RuleChain[]) => RuleChain;
|
|
305
1101
|
object: <Sh extends Record<string, RuleChain>>(shape: Sh) => RuleChain<Infer<Sh>>;
|
|
306
1102
|
array: <Item extends RuleChain>(item?: Item) => RuleChain<OutputOf<Item>[]>;
|
|
307
|
-
enum: <const V extends readonly (string | number | boolean)[]>(values: V) => RuleChain<V[number]>;
|
|
1103
|
+
enum: <const V extends readonly (string | number | boolean)[]>(values: V | ((field: FieldContext) => V)) => RuleChain<V[number]>;
|
|
308
1104
|
literal: <V extends string | number | boolean>(value: V) => RuleChain<V>;
|
|
309
1105
|
};
|
|
310
1106
|
export {};
|