Page MenuHomeSealhub

field.ts
No OneTemporary

field.ts

import { Predicate, predicates } from "@sealcode/ts-predicates";
import { Context } from "koa";
import { t } from "../../utils/translate.js";
import { FormDataValue } from "../form-types.js";
export type FormFieldValidationResponse = { valid: boolean; message: string };
export type FormFieldValidationFn<ValueType> = (
ctx: Context,
value: ValueType,
field: FormField<boolean, ValueType>
) => Promise<FormFieldValidationResponse>;
export type FieldParsedValue<T> = T extends FormField<infer R> ? R : never;
export type GetPredicate<F extends FormField> = F extends FormField<true>
? F["predicate"]
: F["predicate"] | typeof predicates.undefined;
// for quickly testing GetPredicate conditonally applying the undefined predicate:
// const f = new SimpleFormField(true);
// type p = GetPredicate<typeof f>;
// const f2 = new SimpleFormField(false);
// type p2 = GetPredicate<typeof f2>;
export type FieldsToShape<T extends Record<string, FormField>> = {
[property in keyof T]: GetPredicate<T[property]>;
};
export function fieldsToShape<Fields extends Record<string, FormField>>(
obj: Fields
): FieldsToShape<Fields> {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, value.predicate])
) as FieldsToShape<Fields>;
}
export type FieldParseResult<T> =
| {
parsable: true;
error: null;
parsed_value: T;
}
| {
parsable: false;
error: string;
parsed_value: null;
};
export abstract class FormField<
Required extends boolean = boolean,
ParsedValue = unknown
> {
name: string;
constructor(public readonly required: Required) {}
predicate: Predicate = predicates.unknown;
mapToFilter: (parsed: ParsedValue) => unknown = (value) => value;
async init(fieldname: string): Promise<void> {
this.name = fieldname;
}
public async _validate(
ctx: Context,
value: ParsedValue
): Promise<FormFieldValidationResponse> {
if (value === "" || value === null || value === undefined) {
if (this.required) {
return {
valid: false,
message: t(
ctx,
"field_is_required",
[],
"This field is required"
),
};
} else {
return { valid: true, message: "Empty, but not required" };
}
}
return this.isValueValid(ctx, value);
}
public abstract getEmptyValue(): ParsedValue;
public async isValueValid(
_ctx: Context,
_value: ParsedValue
): Promise<FormFieldValidationResponse> {
return { valid: true, message: "" };
}
abstract parse(
ctx: Context,
raw_value: FormDataValue
): Promise<FieldParseResult<ParsedValue>>;
async getValue(
ctx: Context,
all_form_values: Record<string, FormDataValue>,
validate: true
): Promise<{
valid: boolean;
message: string;
parsed: ParsedValue | null;
raw: FormDataValue;
}>;
async getValue(
ctx: Context,
all_form_values: Record<string, FormDataValue>,
validate?: false
): Promise<{
message: string;
parsed: ParsedValue | null;
raw: FormDataValue;
}>;
async getValue(
ctx: Context,
all_form_values: Record<string, FormDataValue>,
validate = false
): Promise<{
valid?: boolean;
message: string;
parsed: ParsedValue | null;
raw: FormDataValue;
}> {
const raw = all_form_values[this.name];
if (raw == undefined || raw == null || raw == "") {
return {
parsed: null,
message: this.required
? "empty value, but field is required"
: "",
raw,
...(validate ? { valid: !this.required } : {}),
};
}
const {
parsable,
parsed_value,
error: parse_error,
} = await this.parse(ctx, raw);
if (!parsable) {
return {
...(validate ? { valid: false } : {}),
parsed: this.getEmptyValue(),
raw,
message: parse_error || "Error",
};
}
if (validate) {
const { valid, message } = await this._validate(ctx, parsed_value);
return {
parsed: parsed_value,
...(validate ? { valid } : {}),
message,
raw: all_form_values[this.name],
};
} else {
return { parsed: parsed_value, raw, message: "parsed" };
}
}
setMapToFilter(fn: (parsed: ParsedValue) => unknown) {
this.mapToFilter = fn;
return this;
}
//return undefined here to delete a value
async getDatabaseValue(
ctx: Context,
data: Record<string, FormDataValue>
): Promise<unknown> {
const { parsed } = await this.getValue(ctx, data);
return parsed;
}
}

File Metadata

Mime Type
text/x-java
Expires
Wed, May 7, 19:37 (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
634780
Default Alt Text
field.ts (4 KB)

Event Timeline