|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- import { IncomingMessage } from 'http';
- import { getFormValues } from '@theoryofnekomata/formxtra';
- import {
- ENCTYPE_APPLICATION_JSON, ENCTYPE_APPLICATION_OCTET_STREAM,
- ENCTYPE_MULTIPART_FORM_DATA,
- ENCTYPE_X_WWW_FORM_URLENCODED,
- } from '../common/enctypes';
- import { getBody, parseMultipartFormData } from './request';
- import {
- METHOD_FORM_KEY,
- PREVENT_REDIRECT_FORM_KEY,
- CONTENT_ENCODING_HEADER_KEY,
- CONTENT_TYPE_HEADER_KEY,
- DEFAULT_ENCODING,
- } from '../common/constants';
-
- export type EncTypeSerializer = (data: unknown) => string;
-
- export type EncTypeSerializerMap = Record<string, EncTypeSerializer>;
-
- export type SerializerOptions = Parameters<typeof getFormValues>[1];
-
- export interface SerializeBodyParams {
- form: HTMLFormElement,
- encType: string,
- serializers?: EncTypeSerializerMap,
- serializerOptions?: SerializerOptions,
- methodFormKey?: string,
- preventRedirectFormKey?: string,
- }
-
- export const DEFAULT_ENCTYPE_SERIALIZERS: EncTypeSerializerMap = {
- [ENCTYPE_APPLICATION_JSON]: (data: unknown) => JSON.stringify(data),
- };
-
- export const serializeBody = (params: SerializeBodyParams) => {
- const {
- form,
- encType,
- serializers = DEFAULT_ENCTYPE_SERIALIZERS,
- serializerOptions,
- methodFormKey = METHOD_FORM_KEY,
- preventRedirectFormKey = PREVENT_REDIRECT_FORM_KEY,
- } = params;
-
- if (encType === ENCTYPE_X_WWW_FORM_URLENCODED) {
- const searchParams = new URLSearchParams(form);
- searchParams.delete(methodFormKey);
- searchParams.delete(preventRedirectFormKey);
- return searchParams;
- }
-
- if (encType === ENCTYPE_MULTIPART_FORM_DATA) {
- // type error when provided a submitter element for some reason...
- const FormDataUnknown = FormData as unknown as {
- new(formElement?: HTMLElement, submitter?: HTMLElement): FormData & {
- entries: () => IterableIterator<[string, unknown]>;
- };
- };
- const formData = new FormDataUnknown(form, serializerOptions?.submitter);
- const emptyFiles = Array.from(formData.entries())
- .filter(([, value]) => (
- value instanceof File && value.size === 0 && value.name === ''
- ));
- emptyFiles.forEach(([key]) => formData.delete(key));
- formData.delete(methodFormKey);
- formData.delete(preventRedirectFormKey);
- return formData;
- }
-
- if (typeof serializers[encType] === 'function') {
- const {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- [methodFormKey]: _method,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- [preventRedirectFormKey]: _preventRedirect,
- ...formValues
- } = getFormValues(form, serializerOptions);
-
- return serializers[encType](formValues);
- }
-
- throw new Error(`Unsupported encType: ${encType}`);
- };
-
- export type EncTypeDeserializer = (data: string) => unknown;
-
- export type EncTypeDeserializerMap = Record<string, EncTypeDeserializer>;
-
- export interface DeserializeBodyParams {
- req: IncomingMessage,
- deserializers?: EncTypeDeserializerMap,
- }
-
- export const DEFAULT_ENCTYPE_DESERIALIZERS: EncTypeDeserializerMap = {
- [ENCTYPE_APPLICATION_JSON]: (data: string) => JSON.parse(data) as Record<string, unknown>,
- [ENCTYPE_X_WWW_FORM_URLENCODED]: (data: string) => Object.fromEntries(
- new URLSearchParams(data).entries(),
- ),
- };
-
- export const deserializeFormObjectBody = async (params: DeserializeBodyParams) => {
- const { req, deserializers = DEFAULT_ENCTYPE_DESERIALIZERS } = params;
- const contentType = req.headers[CONTENT_TYPE_HEADER_KEY] ?? ENCTYPE_APPLICATION_OCTET_STREAM;
-
- if (contentType?.startsWith(`${ENCTYPE_MULTIPART_FORM_DATA};`)) {
- return parseMultipartFormData(req);
- }
-
- const bodyRaw = await getBody(req);
- const encoding = (
- req.headers[CONTENT_ENCODING_HEADER_KEY] ?? DEFAULT_ENCODING
- ) as BufferEncoding;
-
- const { [contentType]: theDeserializer } = deserializers;
-
- if (typeof theDeserializer !== 'function') {
- throw new Error(`Could not deserialize body with content type: ${contentType}`);
- }
-
- return theDeserializer(bodyRaw.toString(encoding)) as Record<string, unknown>;
- };
|