import { hasShape, predicates, Shape, ShapeToType } from "@sealcode/ts-predicates"; import { BaseContext } from "koa"; import merge from "merge"; import { BasePageProps } from "./list"; export type EncodedProps = Record; // the intention here is to sometime in the future be able to store multiple frames on one document, so props for each frame will be in a different namespace, and parsers are going to help with that function parseStringValues( shape: TheShape, values: Record ): ShapeToType { const result: Record = {}; for (const [key, value] of Object.entries(values)) { if (!(key in shape)) { continue; } const predicate = shape[key]; if (predicate == predicates.number) { result[key] = parseFloat(value); } else { result[key] = value; } } // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return result as ShapeToType; } export abstract class PagePropsParser { abstract decode(ctx: BaseContext): PropsType; abstract encode(props: PropsType): EncodedProps; abstract getHTMLInputName(prop_name: string): string; constructor(public propsShape: Shape, public defaultValues: Partial) {} overwriteProp(ctx: BaseContext, new_props: Partial): EncodedProps { const result = {}; merge.recursive(result, this.decode(ctx), new_props); return result; } makeHiddenInputs(values: PropsType, fields_to_skip: string[]): string { return Object.entries(values) .filter(([key]) => !fields_to_skip.includes(key)) .map( ([key, value]: [string, string | number]) => /* HTML */ `` ) .join(" "); } getDefaultValue(key: Key): PropsType[Key] | undefined { return this.defaultValues[key]; } } export class AllQueryParams< PropsType extends BasePageProps > extends PagePropsParser { decode(ctx: BaseContext): PropsType { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const query = parseStringValues(this.propsShape, { ...this.defaultValues, ...ctx.query, } as unknown as Record); if (!hasShape(this.propsShape, query)) { throw new Error("Wrong props shape"); } // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return query as PropsType; } encode(props: PropsType): Record { return props; } getHTMLInputName(prop_name: string): string { return prop_name; } }