diff --git a/README.md b/README.md
index d5bb44d..214bcd0 100644
--- a/README.md
+++ b/README.md
@@ -127,9 +127,10 @@ In theory, any API route may have a corresponding action route.
 - [ ] `<form method="dialog">` (on hold, see https://github.com/whatwg/html/issues/9625)
 - [ ] Tests
   - [X] Form with redirects
-  - [ ] Form with files
+  - [X] Form with files
 - [ ] Documentation
 - [ ] Remix support
 - [ ] `accept-charset=""` attribute support 
 - [X] Method override support
-- Integration with Next router (iceform-next)
+- [ ] Integration with Next router (iceform-next)
+- [ ] Investigate bug of not cleaning response body cache and flash messages via cookies
diff --git a/packages/iceform-next-sandbox/next.config.js b/packages/iceform-next-sandbox/next.config.js
index 500f490..96572f2 100644
--- a/packages/iceform-next-sandbox/next.config.js
+++ b/packages/iceform-next-sandbox/next.config.js
@@ -4,6 +4,11 @@ const nextConfig = {
 	experimental: {
 		optimizeCss: true,
 	},
+	webpack: (config) => {
+		config.resolve.fallback = { fs: false };
+
+		return config;
+	},
 };
 
 module.exports = nextConfig;
diff --git a/packages/iceform-next-sandbox/src/handlers/note.ts b/packages/iceform-next-sandbox/src/handlers/note.ts
index 55ed12f..5d6e18f 100644
--- a/packages/iceform-next-sandbox/src/handlers/note.ts
+++ b/packages/iceform-next-sandbox/src/handlers/note.ts
@@ -51,7 +51,7 @@ const patchNote: NextApiHandler = async (req, res) => {
 		return;
 	}
 
-	const { title, content } = req.body;
+	const { title, content, image } = req.body;
 
 	const dataRaw = await fs.readFile('.db/notes.jsonl', {
 		encoding: 'utf-8',
@@ -75,6 +75,7 @@ const patchNote: NextApiHandler = async (req, res) => {
 		...note,
 		title: title ?? note.title,
 		content: content ?? note.content,
+		image: image ?? note.image,
 	};
 
 	data[noteIndex] = updatedNote;
@@ -185,7 +186,7 @@ export interface NoteCollectionParams {
 }
 
 const createNote = (params: NoteCollectionParams): NextApiHandler => async (req, res) => {
-	const { title, content } = req.body;
+	const { title, content, image } = req.body;
 
 	if (typeof title !== 'string' || typeof content !== 'string') {
 		res.status(400).send('Bad Request');
@@ -202,6 +203,7 @@ const createNote = (params: NoteCollectionParams): NextApiHandler => async (req,
 				id: newId,
 				title,
 				content,
+				image: `data:${image.type};base64,${image.toString('base64')}`,
 			})}\n`,
 			{
 				flag: 'a',
@@ -223,6 +225,7 @@ const createNote = (params: NoteCollectionParams): NextApiHandler => async (req,
 		id: newId,
 		title,
 		content,
+		image,
 	});
 };
 
diff --git a/packages/iceform-next-sandbox/src/pages/notes/[noteId].tsx b/packages/iceform-next-sandbox/src/pages/notes/[noteId].tsx
index 23d2fbd..3608d16 100644
--- a/packages/iceform-next-sandbox/src/pages/notes/[noteId].tsx
+++ b/packages/iceform-next-sandbox/src/pages/notes/[noteId].tsx
@@ -72,6 +72,10 @@ const NotesItemPage: Iceform.NextPage<NotesItemPageProps> = ({
 								/>
 							</div>
 							<div>
+								<img
+									src={body.image as string}
+									alt={body.title as string}
+								/>
 								<label>
 									<span className="after:block">Image</span>
 									<input type="file" name="image" />
diff --git a/packages/iceform-next-sandbox/src/pages/notes/index.tsx b/packages/iceform-next-sandbox/src/pages/notes/index.tsx
index 372b3d0..f7788e4 100644
--- a/packages/iceform-next-sandbox/src/pages/notes/index.tsx
+++ b/packages/iceform-next-sandbox/src/pages/notes/index.tsx
@@ -108,13 +108,24 @@ const NotesPage: NextPage<NotesPageProps> = ({
 									</div>
 								</Iceform.Form>
 							</div>
-							<div
-								className="font-bold"
-							>
-								{note.title}
-							</div>
-							<div>
-								{note.content}
+							<div className="grid grid-cols-3 gap-4">
+								<div>
+									<img
+										className="w-full"
+										src={note.image}
+										alt={note.title}
+									/>
+								</div>
+								<div className="col-span-2">
+									<div
+										className="font-bold"
+									>
+										{note.title}
+									</div>
+									<div>
+										{note.content}
+									</div>
+								</div>
 							</div>
 						</div>
 					))}
diff --git a/packages/iceform-next/.eslintrc b/packages/iceform-next/.eslintrc
index 722c768..c028872 100644
--- a/packages/iceform-next/.eslintrc
+++ b/packages/iceform-next/.eslintrc
@@ -10,7 +10,9 @@
 		"react/jsx-indent": ["error", "tab"],
 		"react/jsx-props-no-spreading": "off",
 		"@typescript-eslint/no-misused-promises": "off",
-		"@typescript-eslint/no-namespace": "off"
+		"@typescript-eslint/no-namespace": "off",
+		"max-classes-per-file": "off",
+		"import/prefer-default-export": "off"
 	},
   "parserOptions": {
     "project": "./tsconfig.eslint.json"
diff --git a/packages/iceform-next/package.json b/packages/iceform-next/package.json
index a4c3557..89a75fc 100644
--- a/packages/iceform-next/package.json
+++ b/packages/iceform-next/package.json
@@ -68,13 +68,11 @@
   },
   "dependencies": {
     "@theoryofnekomata/formxtra": "^1.0.3",
+    "@web-std/file": "^3.0.3",
     "busboy": "^1.6.0",
     "nookies": "^2.5.2",
-    "seroval": "^0.9.0"
+    "seroval": "^0.10.2"
   },
-  "types": "./dist/types/index.d.ts",
-  "main": "./dist/cjs/production/index.js",
-  "module": "./dist/esm/production/index.js",
   "exports": {
     ".": {
       "development": {
@@ -88,5 +86,8 @@
   },
   "typesVersions": {
     "*": {}
-  }
+  },
+  "types": "./dist/types/index.d.ts",
+  "main": "./dist/cjs/production/index.js",
+  "module": "./dist/esm/production/index.js"
 }
diff --git a/packages/iceform-next/pridepack.json b/packages/iceform-next/pridepack.json
index 841fb58..0bc7a8f 100644
--- a/packages/iceform-next/pridepack.json
+++ b/packages/iceform-next/pridepack.json
@@ -1,3 +1,3 @@
 {
   "target": "es2018"
-}
\ No newline at end of file
+}
diff --git a/packages/iceform-next/src/server/action.ts b/packages/iceform-next/src/server/action.ts
deleted file mode 100644
index 75d1265..0000000
--- a/packages/iceform-next/src/server/action.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import {
-	GetServerSideProps,
-	NextApiHandler,
-	PageConfig,
-	NextApiRequest as DefaultNextApiRequest, PreviewData, GetServerSidePropsContext,
-} from 'next';
-import { ParsedUrlQuery } from 'querystring';
-import { deserializeBody, EncTypeDeserializerMap } from '../utils/serialization';
-import { IceformNextServerResponse } from './response';
-import {
-	BODY_COOKIE_KEY, CONTENT_TYPE_COOKIE_KEY,
-	CookieManager,
-	STATUS_CODE_COOKIE_KEY,
-	STATUS_MESSAGE_COOKIE_KEY,
-} from '../utils/cookies';
-import {
-	METHOD_FORM_KEY, METHODS_WITH_BODY,
-	PREVENT_REDIRECT_FORM_KEY,
-} from '../common/constants';
-import {
-	ACTION_STATUS_CODE,
-	DEFAULT_METHOD,
-} from './constants';
-
-export const getApiConfig = (customConfig = {} as PageConfig) => ({
-	api: {
-		...(customConfig.api ?? {}),
-		bodyParser: false,
-	},
-});
-
-export type OnActionFunction<
-	Props extends Record<string, unknown> = Record<string, unknown>,
-	Params extends ParsedUrlQuery = ParsedUrlQuery,
-	Preview extends PreviewData = PreviewData,
-> = (
-	actionReq: DefaultNextApiRequest,
-	actionRes: IceformNextServerResponse,
-	context: GetServerSidePropsContext<Params, Preview>,
-) => Promise<Awaited<ReturnType<GetServerSideProps<Props, Params, Preview>>> | undefined>;
-
-export interface ActionWrapperOptions {
-	fn: NextApiHandler,
-	onAction?: OnActionFunction,
-	deserializers?: EncTypeDeserializerMap,
-	/**
-	 * Maps the Location header from the handler response to an accessible URL.
-	 * @param url
-	 */
-	mapLocationToRedirectDestination?: (referer: string, url: string) => string,
-}
-
-export const wrapApiHandler = (
-	options: ActionWrapperOptions,
-): NextApiHandler => async (req, res) => {
-	const reqMut = req as unknown as Record<string, unknown>;
-	if (METHODS_WITH_BODY.includes(req.method?.toUpperCase() as typeof METHODS_WITH_BODY[number])) {
-		reqMut.body = await deserializeBody({
-			req,
-			deserializers: options.deserializers,
-		});
-	}
-	return options.fn(reqMut as unknown as DefaultNextApiRequest, res);
-};
-
-export const getServerSideProps = (
-	options: ActionWrapperOptions,
-): GetServerSideProps => async (ctx) => {
-	const { referer = '/' } = ctx.req.headers;
-	const deserialized = await deserializeBody({
-		req: ctx.req,
-		deserializers: options.deserializers,
-	});
-
-	const defaultMethod = ctx.req.method ?? DEFAULT_METHOD;
-	let effectiveMethod = defaultMethod;
-	let mockReqBody: unknown = deserialized;
-	if (typeof deserialized === 'object' && deserialized !== null) {
-		const {
-			[METHOD_FORM_KEY]: theMethod,
-			...theMockReqBody
-		} = deserialized as {
-			[METHOD_FORM_KEY]?: string,
-			[key: string]: unknown,
-		};
-		effectiveMethod = theMethod ?? defaultMethod;
-		mockReqBody = theMockReqBody;
-	}
-
-	const mockReq = {
-		...ctx.req,
-		body: mockReqBody,
-		query: {
-			...ctx.query,
-			...(ctx.params ?? {}),
-		},
-		// ?: how to prevent malicious method spoofing?
-		method: effectiveMethod,
-	} as DefaultNextApiRequest;
-
-	const mockRes = new IceformNextServerResponse(ctx.req);
-	await options.fn(mockReq, mockRes);
-
-	const cookieManager = new CookieManager(ctx);
-	cookieManager.setCookie(STATUS_CODE_COOKIE_KEY, mockRes.statusCode.toString());
-	cookieManager.setCookie(STATUS_MESSAGE_COOKIE_KEY, mockRes.statusMessage);
-	if (mockRes.data) {
-		cookieManager.setCookie(BODY_COOKIE_KEY, mockRes.data as string);
-		if (mockRes.contentType) {
-			cookieManager.setCookie(CONTENT_TYPE_COOKIE_KEY, mockRes.contentType);
-		}
-	}
-
-	if (typeof options.onAction === 'function') {
-		const onActionResult = await options.onAction(
-			mockReq,
-			mockRes,
-			ctx,
-		);
-		if (onActionResult) {
-			return onActionResult;
-		}
-	}
-
-	const preventRedirect = (
-		typeof mockReq.body === 'object'
-		&& mockReq.body !== null
-		&& PREVENT_REDIRECT_FORM_KEY in mockReq.body
-	);
-	const redirectDestination = (
-		mockRes.location
-		&& typeof options.mapLocationToRedirectDestination === 'function'
-		&& !preventRedirect
-	)
-		? options.mapLocationToRedirectDestination(referer, mockRes.location)
-		: referer;
-
-	return {
-		redirect: {
-			destination: redirectDestination,
-			statusCode: ACTION_STATUS_CODE,
-		},
-		props: {
-			query: ctx.query,
-			body: mockRes.data,
-		},
-	};
-};
diff --git a/packages/iceform-next/src/server/action/api.ts b/packages/iceform-next/src/server/action/api.ts
new file mode 100644
index 0000000..6114cb7
--- /dev/null
+++ b/packages/iceform-next/src/server/action/api.ts
@@ -0,0 +1,34 @@
+import { NextApiHandler, PageConfig } from 'next';
+import { NextApiRequest as DefaultNextApiRequest } from 'next/dist/shared/lib/utils';
+import { ActionWrapperOptions } from './common';
+import { METHODS_WITH_BODY } from '../../common/constants';
+import { deserializeFormObjectBody } from '../../utils/serialization';
+
+/**
+ * Wraps the API handler's `config` export to support all content types.
+ * @param customConfig
+ */
+export const getApiConfig = (customConfig = {} as PageConfig) => ({
+	api: {
+		...(customConfig.api ?? {}),
+		bodyParser: false,
+	},
+});
+
+/**
+ * Wraps the API handler to support script.
+ * @param options
+ */
+export const wrapApiHandler = (options: ActionWrapperOptions): NextApiHandler => async (
+	req,
+	res,
+) => {
+	const reqMut = req as unknown as Record<string, unknown>;
+	if (METHODS_WITH_BODY.includes(req.method?.toUpperCase() as typeof METHODS_WITH_BODY[number])) {
+		reqMut.body = await deserializeFormObjectBody({
+			req,
+			deserializers: options.deserializers,
+		});
+	}
+	return options.fn(reqMut as unknown as DefaultNextApiRequest, res);
+};
diff --git a/packages/iceform-next/src/server/action/common.ts b/packages/iceform-next/src/server/action/common.ts
new file mode 100644
index 0000000..45bbc9f
--- /dev/null
+++ b/packages/iceform-next/src/server/action/common.ts
@@ -0,0 +1,31 @@
+import {
+	GetServerSideProps,
+	GetServerSidePropsContext,
+	NextApiHandler,
+	PreviewData,
+} from 'next';
+import { ParsedUrlQuery } from 'querystring';
+import { NextApiRequest as DefaultNextApiRequest } from 'next/dist/shared/lib/utils';
+import { EncTypeDeserializerMap } from '../../utils/serialization';
+import { IceformNextServerResponse } from '../response';
+
+export type OnActionFunction<
+	Props extends Record<string, unknown> = Record<string, unknown>,
+	Params extends ParsedUrlQuery = ParsedUrlQuery,
+	Preview extends PreviewData = PreviewData,
+> = (
+	actionReq: DefaultNextApiRequest,
+	actionRes: IceformNextServerResponse,
+	context: GetServerSidePropsContext<Params, Preview>,
+) => Promise<Awaited<ReturnType<GetServerSideProps<Props, Params, Preview>>> | undefined>;
+
+export interface ActionWrapperOptions {
+	fn: NextApiHandler,
+	onAction?: OnActionFunction,
+	deserializers?: EncTypeDeserializerMap,
+	/**
+	 * Maps the Location header from the handler response to an accessible URL.
+	 * @param url
+	 */
+	mapLocationToRedirectDestination?: (referer: string, url: string) => string,
+}
diff --git a/packages/iceform-next/src/server/action/gssp.ts b/packages/iceform-next/src/server/action/gssp.ts
new file mode 100644
index 0000000..68f53c1
--- /dev/null
+++ b/packages/iceform-next/src/server/action/gssp.ts
@@ -0,0 +1,135 @@
+import { IncomingMessage } from 'http';
+import { GetServerSideProps } from 'next';
+import { NextApiRequest as DefaultNextApiRequest } from 'next/dist/shared/lib/utils';
+import crypto from 'crypto';
+import {
+	DEFAULT_ENCTYPE_DESERIALIZERS,
+	deserializeFormObjectBody,
+	EncTypeDeserializerMap,
+} from '../../utils/serialization';
+import { METHOD_FORM_KEY, PREVENT_REDIRECT_FORM_KEY } from '../../common/constants';
+import { ACTION_STATUS_CODE, DEFAULT_METHOD } from '../constants';
+import { getBody } from '../../utils/request';
+import {
+	ENCTYPE_APPLICATION_OCTET_STREAM,
+	ENCTYPE_MULTIPART_FORM_DATA,
+} from '../../common/enctypes';
+import { ActionWrapperOptions } from './common';
+import { IceformNextServerResponse } from '../response';
+import {
+	REQUEST_ID_COOKIE_KEY,
+	CookieManager,
+} from '../../utils/cookies';
+import { cacheResponse } from '../cache';
+
+const getFormObjectMethodAndBody = async (
+	req: IncomingMessage,
+	deserializers: EncTypeDeserializerMap,
+) => {
+	const deserialized = await deserializeFormObjectBody({
+		req,
+		deserializers,
+	});
+	const {
+		[METHOD_FORM_KEY]: method = req.method ?? DEFAULT_METHOD,
+		...body
+	} = deserialized as {
+		[METHOD_FORM_KEY]?: string,
+		[key: string]: unknown,
+	};
+
+	return {
+		body,
+		method,
+	};
+};
+
+const getBinaryMethodAndBody = async (
+	req: IncomingMessage,
+) => {
+	const body = await getBody(req);
+
+	return {
+		body,
+		method: req.method ?? DEFAULT_METHOD,
+	};
+};
+
+const isContentTypeFormObject = (
+	contentType: string,
+	deserializers: EncTypeDeserializerMap,
+) => (
+	contentType?.startsWith(`${ENCTYPE_MULTIPART_FORM_DATA};`)
+	|| Object.keys(deserializers).includes(contentType)
+);
+
+/**
+ * Wraps the `getServerSideProps` function to support no-script.
+ * @param options
+ */
+export const getServerSideProps = (options: ActionWrapperOptions): GetServerSideProps => async (
+	ctx,
+) => {
+	const {
+		referer = '/',
+		'content-type': contentType = ENCTYPE_APPLICATION_OCTET_STREAM,
+	} = ctx.req.headers;
+	const { deserializers = DEFAULT_ENCTYPE_DESERIALIZERS } = options;
+	const methodAndBodyFn = (
+		isContentTypeFormObject(contentType, deserializers)
+			? getFormObjectMethodAndBody
+			: getBinaryMethodAndBody
+	);
+	const { body, method } = await methodAndBodyFn(ctx.req, deserializers);
+	const req = {
+		...ctx.req,
+		body,
+		query: {
+			...ctx.query,
+			...(ctx.params ?? {}),
+		},
+		// ?: how to prevent malicious method spoofing?
+		method,
+	} as DefaultNextApiRequest;
+	const res = new IceformNextServerResponse(ctx.req);
+	await options.fn(req, res);
+	const requestId = crypto.randomUUID();
+	const cookieManager = new CookieManager(ctx);
+	cookieManager.setCookie(REQUEST_ID_COOKIE_KEY, requestId);
+	await cacheResponse(requestId, res);
+
+	if (typeof options.onAction === 'function') {
+		const onActionResult = await options.onAction(
+			req,
+			res,
+			ctx,
+		);
+		if (onActionResult) {
+			return onActionResult;
+		}
+	}
+
+	const preventRedirect = (
+		typeof req.body === 'object'
+		&& req.body !== null
+		&& PREVENT_REDIRECT_FORM_KEY in req.body
+	);
+	const redirectDestination = (
+		res.location
+		&& typeof options.mapLocationToRedirectDestination === 'function'
+		&& !preventRedirect
+	)
+		? options.mapLocationToRedirectDestination(referer, res.location)
+		: referer;
+
+	return {
+		redirect: {
+			destination: redirectDestination,
+			statusCode: ACTION_STATUS_CODE,
+		},
+		props: {
+			query: ctx.query,
+			body: res.data,
+		},
+	};
+};
diff --git a/packages/iceform-next/src/server/action/index.ts b/packages/iceform-next/src/server/action/index.ts
new file mode 100644
index 0000000..ab1cf5c
--- /dev/null
+++ b/packages/iceform-next/src/server/action/index.ts
@@ -0,0 +1,3 @@
+export * from './api';
+export * from './common';
+export * from './gssp';
diff --git a/packages/iceform-next/src/server/cache.ts b/packages/iceform-next/src/server/cache.ts
new file mode 100644
index 0000000..be4dca3
--- /dev/null
+++ b/packages/iceform-next/src/server/cache.ts
@@ -0,0 +1,48 @@
+import { createWriteStream } from 'fs';
+import { readFile, unlink } from 'fs/promises';
+
+interface CacheableResponse {
+	statusCode: number;
+	statusMessage?: string;
+	contentType?: string;
+	data?: unknown;
+}
+
+const getFilePathFromRequestId = (requestId: string) => `${requestId}`;
+
+export const cacheResponse = async (requestId: string, res: CacheableResponse) => {
+	const filePath = getFilePathFromRequestId(requestId);
+	const cacheStream = createWriteStream(filePath, { encoding: 'utf-8' });
+	cacheStream.write(`${res.statusCode.toString()} ${res.statusMessage || ''}\n`);
+	if (res.contentType) {
+		cacheStream.write(`Content-Type: ${res.contentType}\n`);
+	}
+	if (res.data) {
+		cacheStream.write('\n');
+		cacheStream.write(res.data as string);
+	}
+
+	return new Promise((resolve) => {
+		cacheStream.close(resolve);
+	});
+};
+
+export const retrieveCache = async (requestId: string) => {
+	const filePath = getFilePathFromRequestId(requestId);
+	const requestBuffer = await readFile(filePath, 'utf-8');
+	await unlink(filePath);
+	const [statusLine, ...headersAndBody] = requestBuffer.split('\n');
+	const [statusCode, ...statusMessageWords] = statusLine.split(' ');
+	const statusMessage = statusMessageWords.join(' ');
+	const bodyStart = headersAndBody.findIndex((line) => line === '');
+	const headers = headersAndBody.slice(0, bodyStart);
+	const body = headersAndBody.slice(bodyStart + 1).join('\n');
+	const contentTypeHeader = headers.find((header) => header.toLowerCase().startsWith('content-type:'));
+	const contentType = contentTypeHeader?.split(':')[1].trim();
+	return {
+		statusCode: parseInt(statusCode, 10),
+		statusMessage,
+		contentType,
+		body,
+	};
+};
diff --git a/packages/iceform-next/src/server/destination.ts b/packages/iceform-next/src/server/destination.ts
index ec48946..5d79cc5 100644
--- a/packages/iceform-next/src/server/destination.ts
+++ b/packages/iceform-next/src/server/destination.ts
@@ -2,20 +2,12 @@ import { ParsedUrlQuery } from 'querystring';
 import { GetServerSideProps, GetServerSidePropsContext, PreviewData } from 'next';
 import { deserialize } from 'seroval';
 import { NextApiRequest, NextApiResponse } from '../common/types';
-import {
-	DEFAULT_ENCODING,
-	DEFAULT_METHOD,
-	DEFAULT_RESPONSE_STATUS_CODE,
-} from './constants';
+import { DEFAULT_ENCODING, DEFAULT_METHOD } from './constants';
 import { getBody } from '../utils/request';
-import {
-	BODY_COOKIE_KEY, CONTENT_TYPE_COOKIE_KEY,
-	CookieManager,
-	STATUS_CODE_COOKIE_KEY,
-	STATUS_MESSAGE_COOKIE_KEY,
-} from '../utils/cookies';
+import { REQUEST_ID_COOKIE_KEY, CookieManager } from '../utils/cookies';
 import { ENCTYPE_APPLICATION_JSON, ENCTYPE_APPLICATION_OCTET_STREAM } from '../common/enctypes';
 import { METHODS_WITH_BODY } from '../common/constants';
+import { retrieveCache } from './cache';
 
 export type DestinationGetServerSideProps<
 	Props extends Record<string, unknown> = Record<string, unknown>,
@@ -48,24 +40,20 @@ export const getServerSideProps = (
 		req.body = body.toString(DEFAULT_ENCODING);
 	}
 
-	const cookieManager = new CookieManager(ctx);
-	// TODO how to properly remove cookies without leftovers?
-	if (cookieManager.hasCookie(STATUS_CODE_COOKIE_KEY)) {
-		ctx.res.statusCode = Number(
-			cookieManager.getCookie(STATUS_CODE_COOKIE_KEY) || DEFAULT_RESPONSE_STATUS_CODE,
-		);
-		cookieManager.unsetCookie(STATUS_CODE_COOKIE_KEY);
-	}
-
-	if (cookieManager.hasCookie(STATUS_MESSAGE_COOKIE_KEY)) {
-		ctx.res.statusMessage = cookieManager.getCookie(STATUS_MESSAGE_COOKIE_KEY) || '';
-		cookieManager.unsetCookie(STATUS_MESSAGE_COOKIE_KEY);
-	}
-
 	const res: NextApiResponse = {};
-	if (cookieManager.hasCookie(BODY_COOKIE_KEY)) {
-		const resBody = cookieManager.getCookie(BODY_COOKIE_KEY);
-		const contentType = cookieManager.getCookie(CONTENT_TYPE_COOKIE_KEY);
+
+	const cookieManager = new CookieManager(ctx);
+	const resRequestId = cookieManager.getCookie(REQUEST_ID_COOKIE_KEY);
+	cookieManager.unsetCookie(REQUEST_ID_COOKIE_KEY);
+	if (resRequestId) {
+		const {
+			statusCode,
+			statusMessage,
+			contentType,
+			body: resBody,
+		} = await retrieveCache(resRequestId);
+		ctx.res.statusCode = statusCode;
+		ctx.res.statusMessage = statusMessage;
 		if (contentType === ENCTYPE_APPLICATION_JSON) {
 			res.body = deserialize(resBody);
 		} else if (contentType === ENCTYPE_APPLICATION_OCTET_STREAM) {
@@ -75,8 +63,6 @@ export const getServerSideProps = (
 			c.warn('Could not parse response body, returning nothing');
 			res.body = null;
 		}
-		cookieManager.unsetCookie(BODY_COOKIE_KEY);
-		cookieManager.unsetCookie(CONTENT_TYPE_COOKIE_KEY);
 	}
 
 	const gspResult = (
diff --git a/packages/iceform-next/src/server/response.ts b/packages/iceform-next/src/server/response.ts
index 9d9bc6f..47f77b4 100644
--- a/packages/iceform-next/src/server/response.ts
+++ b/packages/iceform-next/src/server/response.ts
@@ -1,9 +1,13 @@
 import { ServerResponse } from 'http';
 import { NextApiResponse as DefaultNextApiResponse } from 'next/dist/shared/lib/utils';
 import { serialize } from 'seroval';
+
 import { ENCTYPE_APPLICATION_JSON, ENCTYPE_APPLICATION_OCTET_STREAM } from '../common/enctypes';
-import { ACTION_STATUS_CODE, CONTENT_TYPE_HEADER_KEY, LOCATION_HEADER_KEY } from './constants';
+import {
+	ACTION_STATUS_CODE, CONTENT_TYPE_HEADER_KEY, LOCATION_HEADER_KEY, DEFAULT_RESPONSE_STATUS_CODE,
+} from './constants';
 
+// for client-side
 class DummyServerResponse {}
 
 const EffectiveServerResponse = ServerResponse ?? DummyServerResponse;
@@ -19,6 +23,11 @@ export class IceformNextServerResponse
 
 	private readonly revalidateResponse = Promise.resolve(undefined);
 
+	constructor(...args: ConstructorParameters<typeof EffectiveServerResponse>) {
+		super(...args);
+		this.statusCode = DEFAULT_RESPONSE_STATUS_CODE;
+	}
+
 	setHeader(name: string, value: number | string | readonly string[]): this {
 		super.setHeader(name, value);
 
@@ -38,9 +47,13 @@ export class IceformNextServerResponse
 		return this as unknown as DefaultNextApiResponse;
 	}
 
+	private static serialize(body: unknown) {
+		return serialize(body);
+	}
+
 	json(body: unknown): void {
 		this.contentType = ENCTYPE_APPLICATION_JSON;
-		this.data = serialize(body);
+		this.data = IceformNextServerResponse.serialize(body);
 	}
 
 	revalidate(): Promise<void> {
@@ -59,7 +72,7 @@ export class IceformNextServerResponse
 			this.contentType = ENCTYPE_APPLICATION_OCTET_STREAM;
 		}
 
-		this.data = serialize(body);
+		this.data = IceformNextServerResponse.serialize(body);
 	}
 
 	setDraftMode(): DefaultNextApiResponse {
diff --git a/packages/iceform-next/src/utils/cookies.ts b/packages/iceform-next/src/utils/cookies.ts
index 4616db9..59ba349 100644
--- a/packages/iceform-next/src/utils/cookies.ts
+++ b/packages/iceform-next/src/utils/cookies.ts
@@ -1,5 +1,6 @@
 import { IncomingMessage, ServerResponse } from 'http';
 import * as nookies from 'nookies';
+import * as crypto from 'crypto';
 
 const COMMON_COOKIE_CONFIG = {
 	path: '/',
@@ -13,11 +14,7 @@ const COMMON_SET_COOKIE_CONFIG = {
 
 const cookieKeys: Record<string, string> = {};
 
-export const BODY_COOKIE_KEY = 'b' as const;
-export const STATUS_CODE_COOKIE_KEY = 'sc' as const;
-export const STATUS_MESSAGE_COOKIE_KEY = 'sm' as const;
-export const CONTENT_TYPE_COOKIE_KEY = 'ct' as const;
-
+export const REQUEST_ID_COOKIE_KEY = 'b' as const;
 export class CookieManager {
 	private readonly ctx: { req: IncomingMessage, res: ServerResponse<IncomingMessage> };
 
@@ -27,13 +24,17 @@ export class CookieManager {
 	}
 
 	private static generateCookieKey(key: string) {
-		return `if${key}${Date.now()}`;
+		const random = crypto.randomBytes(16).toString('hex');
+		return `if${key}${random}`;
 	}
 
 	setCookie(key: string, value: string) {
+		// cleanup previous cookie
+		this.unsetCookie(key);
+		cookieKeys[key] = CookieManager.generateCookieKey(key);
 		nookies.setCookie(
 			this.ctx,
-			cookieKeys[key] = CookieManager.generateCookieKey(key),
+			cookieKeys[key],
 			value,
 			COMMON_SET_COOKIE_CONFIG,
 		);
diff --git a/packages/iceform-next/src/utils/helpers.ts b/packages/iceform-next/src/utils/helpers.ts
new file mode 100644
index 0000000..b7d957e
--- /dev/null
+++ b/packages/iceform-next/src/utils/helpers.ts
@@ -0,0 +1,44 @@
+interface AbstractFormDataItem<T> {
+	value: T | null;
+}
+
+export interface FileItem extends AbstractFormDataItem<Buffer> {
+	kind: 'file';
+	mimeType: string;
+	filename: string;
+}
+
+export interface FieldItem extends AbstractFormDataItem<string> {
+	kind: 'field';
+}
+
+export type FormDataItem = FileItem | FieldItem;
+
+export type FormDataMap = Record<string, FormDataItem>;
+
+export const addFormObjectBodyHelpers = (deserialized: Record<string, unknown>): FormDataMap => (
+	Object.fromEntries(
+		Object
+			.entries(deserialized)
+			.filter(([, value]) => typeof value !== 'undefined')
+			.map(([key, value]) => [
+				key,
+				{
+					kind: 'field',
+					value: value?.toString() ?? null,
+				},
+			] satisfies [string, FieldItem]),
+	)
+);
+
+export const removeFormObjectBodyHelpers = (formObject: FormDataMap): Record<string, unknown> => (
+	Object.fromEntries(
+		Object
+			.entries(formObject)
+			.filter(([, value]) => typeof value !== 'undefined')
+			.map(([key, valueWithHelper]) => [
+				key,
+				valueWithHelper.value,
+			]),
+	)
+);
diff --git a/packages/iceform-next/src/utils/request.ts b/packages/iceform-next/src/utils/request.ts
index f147a80..c233715 100644
--- a/packages/iceform-next/src/utils/request.ts
+++ b/packages/iceform-next/src/utils/request.ts
@@ -28,24 +28,26 @@ export const parseMultipartFormData = async (
 	bb.on('file', (name, file, info) => {
 		const {
 			filename,
-			mimeType: mimetype,
+			mimeType,
 		} = info;
-
-		let fileData = Buffer.from('');
+		let buffer = Buffer.from('');
 
 		file.on('data', (data) => {
-			fileData = Buffer.concat([fileData, data]);
+			buffer = Buffer.concat([buffer, data]);
 		});
 
 		file.on('close', () => {
-			const newFile = fileData.buffer as unknown as Record<string, unknown>;
-			newFile.name = filename;
-			newFile.type = mimetype;
-			body[name] = newFile;
+			const bufferMut = buffer as unknown as Record<string, unknown>;
+			bufferMut.name = filename;
+			bufferMut.type = mimeType;
+			bufferMut.size = buffer.length;
+
+			body[name] = bufferMut;
 		});
 	});
 
 	bb.on('field', (name, value) => {
+		// TODO max length for long values, convert to reference instead
 		body[name] = value;
 	});
 
diff --git a/packages/iceform-next/src/utils/serialization.ts b/packages/iceform-next/src/utils/serialization.ts
index 007c034..73e89a2 100644
--- a/packages/iceform-next/src/utils/serialization.ts
+++ b/packages/iceform-next/src/utils/serialization.ts
@@ -7,6 +7,7 @@ import {
 } from '../common/enctypes';
 import { getBody, parseMultipartFormData } from './request';
 import { METHOD_FORM_KEY, PREVENT_REDIRECT_FORM_KEY } from '../common/constants';
+import { DEFAULT_ENCODING } from '../server/constants';
 
 export type EncTypeSerializer = (data: unknown) => string;
 
@@ -43,9 +44,16 @@ export const serializeBody = (params: SerializeBodyParams) => {
 	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;
+			new(formElement?: HTMLElement, submitter?: HTMLElement): FormData & {
+				entries: () => IterableIterator<[string, unknown]>;
+			};
 		};
 		const formData = new FormDataUnknown(form, options?.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(METHOD_FORM_KEY);
 		formData.delete(PREVENT_REDIRECT_FORM_KEY);
 		return formData;
@@ -75,9 +83,12 @@ export interface DeserializeBodyParams {
 
 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 deserializeBody = async (params: DeserializeBodyParams): Promise<unknown> => {
+export const deserializeFormObjectBody = async (params: DeserializeBodyParams) => {
 	const { req, deserializers = DEFAULT_ENCTYPE_DESERIALIZERS } = params;
 	const contentType = req.headers['content-type'] ?? ENCTYPE_APPLICATION_OCTET_STREAM;
 
@@ -85,18 +96,16 @@ export const deserializeBody = async (params: DeserializeBodyParams): Promise<un
 		return parseMultipartFormData(req);
 	}
 
-	const encoding = (req.headers['content-encoding'] ?? 'utf-8') as BufferEncoding;
 	const bodyRaw = await getBody(req);
+	const encoding = (
+		req.headers['content-encoding'] ?? DEFAULT_ENCODING
+	) as BufferEncoding;
 
-	if (contentType === ENCTYPE_X_WWW_FORM_URLENCODED) {
-		return Object.fromEntries(
-			new URLSearchParams(bodyRaw.toString(encoding)).entries(),
-		);
-	}
+	const { [contentType]: theDeserializer } = deserializers;
 
-	if (typeof deserializers[contentType] === 'function') {
-		return deserializers[contentType](bodyRaw.toString(encoding));
+	if (typeof theDeserializer !== 'function') {
+		throw new Error(`Could not deserialize body with content type: ${contentType}`);
 	}
 
-	return bodyRaw.toString('binary');
+	return theDeserializer(bodyRaw.toString(encoding)) as Record<string, unknown>;
 };
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9502088..4c1efe2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,6 +11,9 @@ importers:
       '@theoryofnekomata/formxtra':
         specifier: ^1.0.3
         version: 1.0.3
+      '@web-std/file':
+        specifier: ^3.0.3
+        version: 3.0.3
       busboy:
         specifier: ^1.6.0
         version: 1.6.0
@@ -18,8 +21,8 @@ importers:
         specifier: ^2.5.2
         version: 2.5.2
       seroval:
-        specifier: ^0.9.0
-        version: 0.9.0
+        specifier: ^0.10.2
+        version: 0.10.2
     devDependencies:
       '@testing-library/jest-dom':
         specifier: ^5.16.5
@@ -1306,6 +1309,7 @@ packages:
       typescript: 4.9.5
     transitivePeerDependencies:
       - supports-color
+    dev: true
 
   /@typescript-eslint/parser@6.7.0(eslint@8.49.0)(typescript@5.2.2):
     resolution: {integrity: sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==}
@@ -1334,6 +1338,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 5.62.0
       '@typescript-eslint/visitor-keys': 5.62.0
+    dev: true
 
   /@typescript-eslint/scope-manager@6.7.0:
     resolution: {integrity: sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==}
@@ -1366,6 +1371,7 @@ packages:
   /@typescript-eslint/types@5.62.0:
     resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
 
   /@typescript-eslint/types@6.7.0:
     resolution: {integrity: sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==}
@@ -1391,6 +1397,7 @@ packages:
       typescript: 4.9.5
     transitivePeerDependencies:
       - supports-color
+    dev: true
 
   /@typescript-eslint/typescript-estree@6.7.0(typescript@5.2.2):
     resolution: {integrity: sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==}
@@ -1439,6 +1446,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 5.62.0
       eslint-visitor-keys: 3.4.3
+    dev: true
 
   /@typescript-eslint/visitor-keys@6.7.0:
     resolution: {integrity: sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==}
@@ -1507,6 +1515,31 @@ packages:
       pretty-format: 29.7.0
     dev: true
 
+  /@web-std/blob@3.0.5:
+    resolution: {integrity: sha512-Lm03qr0eT3PoLBuhkvFBLf0EFkAsNz/G/AYCzpOdi483aFaVX86b4iQs0OHhzHJfN5C15q17UtDbyABjlzM96A==}
+    dependencies:
+      '@web-std/stream': 1.0.0
+      web-encoding: 1.1.5
+    dev: false
+
+  /@web-std/file@3.0.3:
+    resolution: {integrity: sha512-X7YYyvEERBbaDfJeC9lBKC5Q5lIEWYCP1SNftJNwNH/VbFhdHm+3neKOQP+kWEYJmosbDFq+NEUG7+XIvet/Jw==}
+    dependencies:
+      '@web-std/blob': 3.0.5
+    dev: false
+
+  /@web-std/stream@1.0.0:
+    resolution: {integrity: sha512-jyIbdVl+0ZJyKGTV0Ohb9E6UnxP+t7ZzX4Do3AHjZKxUXKMs9EmqnBDQgHF7bEw0EzbQygOjtt/7gvtmi//iCQ==}
+    dependencies:
+      web-streams-polyfill: 3.2.1
+    dev: false
+
+  /@zxing/text-encoding@0.9.0:
+    resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==}
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /abab@2.0.6:
     resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
     dev: true
@@ -2677,6 +2710,7 @@ packages:
       - eslint-import-resolver-node
       - eslint-import-resolver-webpack
       - supports-color
+    dev: true
 
   /eslint-import-resolver-typescript@3.6.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.49.0):
     resolution: {integrity: sha512-QTHR9ddNnn35RTxlaEnx2gCxqFlF2SEN0SE2d17SqwyM7YOSI2GHWRYp5BiRkObTUNYPupC/3Fq2a0PpT+EKpg==}
@@ -2729,6 +2763,7 @@ packages:
       eslint-import-resolver-typescript: 3.6.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.28.1)(eslint@8.49.0)
     transitivePeerDependencies:
       - supports-color
+    dev: true
 
   /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.0)(eslint@8.49.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
@@ -2809,6 +2844,7 @@ packages:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
+    dev: true
 
   /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-typescript@3.6.0)(eslint@8.49.0):
     resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==}
@@ -2829,7 +2865,7 @@ packages:
       doctrine: 2.1.0
       eslint: 8.49.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.0)(eslint@8.49.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.0)(eslint@8.49.0)
       has: 1.0.3
       is-core-module: 2.13.0
       is-glob: 4.0.3
@@ -3542,7 +3578,6 @@ packages:
     dependencies:
       call-bind: 1.0.2
       has-tostringtag: 1.0.0
-    dev: true
 
   /is-array-buffer@3.0.2:
     resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
@@ -4892,8 +4927,8 @@ packages:
       - supports-color
     dev: true
 
-  /seroval@0.9.0:
-    resolution: {integrity: sha512-Ttr96/8czi3SXjbFFzpRc2Xpp1wvBufmaNuTviUL8eGQhUr1mdeiQ6YYSaLnMwMc4YWSeBggq72bKEBVu6/IFA==}
+  /seroval@0.10.2:
+    resolution: {integrity: sha512-aa9Tmthjs1wdSdtwr+USjeKHtML55ZJw2wPR6qjzsW9o+Og3eDNL+vJhjkIBH/pQT4bm4TUJ8+DcLlZq8RwzCg==}
     engines: {node: '>=10'}
     dev: false
 
@@ -5278,6 +5313,7 @@ packages:
 
   /tslib@1.14.1:
     resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+    dev: true
 
   /tslib@2.5.0:
     resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
@@ -5290,6 +5326,7 @@ packages:
     dependencies:
       tslib: 1.14.1
       typescript: 4.9.5
+    dev: true
 
   /type-check@0.4.0:
     resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
@@ -5358,6 +5395,7 @@ packages:
     resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
     engines: {node: '>=4.2.0'}
     hasBin: true
+    dev: true
 
   /typescript@5.2.2:
     resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
@@ -5419,6 +5457,16 @@ packages:
   /util-deprecate@1.0.2:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
 
+  /util@0.12.5:
+    resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
+    dependencies:
+      inherits: 2.0.4
+      is-arguments: 1.1.1
+      is-generator-function: 1.0.10
+      is-typed-array: 1.1.12
+      which-typed-array: 1.1.11
+    dev: false
+
   /utils-merge@1.0.1:
     resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
     engines: {node: '>= 0.4.0'}
@@ -5600,6 +5648,19 @@ packages:
       defaults: 1.0.4
     dev: true
 
+  /web-encoding@1.1.5:
+    resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==}
+    dependencies:
+      util: 0.12.5
+    optionalDependencies:
+      '@zxing/text-encoding': 0.9.0
+    dev: false
+
+  /web-streams-polyfill@3.2.1:
+    resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
+    engines: {node: '>= 8'}
+    dev: false
+
   /webidl-conversions@7.0.0:
     resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
     engines: {node: '>=12'}