release: WRNexusJS 0.5.0
This commit is contained in:
@@ -20,7 +20,7 @@ export type RuleDescriptor =
|
||||
| { kind: "integer"; message?: string };
|
||||
|
||||
export interface FieldDescriptor {
|
||||
type: "string" | "number" | "boolean";
|
||||
type: "string" | "number" | "boolean" | "unknown";
|
||||
optional?: boolean;
|
||||
/** Message used when a required field is empty. Defaults to "Required". */
|
||||
requiredMessage?: string;
|
||||
@@ -103,6 +103,14 @@ export function checkField(
|
||||
desc: FieldDescriptor,
|
||||
raw: unknown,
|
||||
): { value: unknown; error: string | null } {
|
||||
if (desc.type === "unknown") {
|
||||
const empty = raw === undefined || raw === null || raw === "";
|
||||
return {
|
||||
value: raw,
|
||||
error: empty && !desc.optional ? desc.requiredMessage || "Required" : null,
|
||||
};
|
||||
}
|
||||
|
||||
if (desc.type === "boolean") {
|
||||
const value = raw === true || raw === "true" || raw === "on";
|
||||
if (!desc.optional && !value) {
|
||||
@@ -139,8 +147,8 @@ export function checkField(
|
||||
/** A server-only refinement (a predicate that can't be serialized to the client). */
|
||||
type Refinement = { fn: (value: unknown) => boolean | string; message?: string };
|
||||
|
||||
abstract class FieldSchema {
|
||||
abstract readonly type: "string" | "number" | "boolean";
|
||||
export abstract class FieldSchema {
|
||||
abstract readonly type: "string" | "number" | "boolean" | "unknown";
|
||||
protected _optional = false;
|
||||
protected _requiredMessage?: string;
|
||||
protected _label?: string;
|
||||
@@ -206,7 +214,7 @@ abstract class FieldSchema {
|
||||
}
|
||||
}
|
||||
|
||||
class StringSchema extends FieldSchema {
|
||||
export class StringSchema extends FieldSchema {
|
||||
readonly type = "string" as const;
|
||||
private _trim = false;
|
||||
email(message?: string): this {
|
||||
@@ -246,7 +254,7 @@ class StringSchema extends FieldSchema {
|
||||
}
|
||||
}
|
||||
|
||||
class NumberSchema extends FieldSchema {
|
||||
export class NumberSchema extends FieldSchema {
|
||||
readonly type = "number" as const;
|
||||
integer(message?: string): this {
|
||||
this.rules.push({ kind: "integer", message });
|
||||
@@ -262,13 +270,34 @@ class NumberSchema extends FieldSchema {
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanSchema extends FieldSchema {
|
||||
export class BooleanSchema extends FieldSchema {
|
||||
readonly type = "boolean" as const;
|
||||
}
|
||||
|
||||
export class UnknownSchema extends FieldSchema {
|
||||
readonly type = "unknown" as const;
|
||||
}
|
||||
|
||||
export type AnyFieldSchema = StringSchema | NumberSchema | BooleanSchema | UnknownSchema;
|
||||
|
||||
export class ObjectSchema {
|
||||
constructor(private readonly fields: Record<string, FieldSchema>) {}
|
||||
|
||||
/** Return a defensive copy of the schema fields. */
|
||||
getFields(): Readonly<Record<string, FieldSchema>> {
|
||||
return { ...this.fields };
|
||||
}
|
||||
|
||||
/** Create a new schema with fields added or replaced. The original is unchanged. */
|
||||
extend(fields: Record<string, FieldSchema>): ObjectSchema {
|
||||
return new ObjectSchema({ ...this.fields, ...fields });
|
||||
}
|
||||
|
||||
/** Create a new schema containing fields from both schemas. */
|
||||
merge(schema: ObjectSchema): ObjectSchema {
|
||||
return new ObjectSchema({ ...this.fields, ...schema.getFields() });
|
||||
}
|
||||
|
||||
/** Validate an input object; returns coerced values + per-field errors. */
|
||||
parse(input: unknown): ParseResult {
|
||||
const source = (input ?? {}) as Record<string, unknown>;
|
||||
@@ -309,6 +338,7 @@ export const v = {
|
||||
string: () => new StringSchema(),
|
||||
number: () => new NumberSchema(),
|
||||
boolean: () => new BooleanSchema(),
|
||||
unknown: () => new UnknownSchema(),
|
||||
object: (fields: Record<string, FieldSchema>) => new ObjectSchema(fields),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user