diff --git a/packages/iceform-next-sandbox/next.config.js b/packages/iceform-next-sandbox/next.config.js index 96572f2..500f490 100644 --- a/packages/iceform-next-sandbox/next.config.js +++ b/packages/iceform-next-sandbox/next.config.js @@ -4,11 +4,6 @@ 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 5d6e18f..bde7c10 100644 --- a/packages/iceform-next-sandbox/src/handlers/note.ts +++ b/packages/iceform-next-sandbox/src/handlers/note.ts @@ -73,9 +73,9 @@ const patchNote: NextApiHandler = async (req, res) => { const updatedNote = { ...note, - title: title ?? note.title, - content: content ?? note.content, - image: image ?? note.image, + title: title || note.title, + content: content || note.content, + image: image ? `data:${image.type};base64,${image.toString('base64')}` : note.image, }; data[noteIndex] = updatedNote; @@ -225,7 +225,7 @@ const createNote = (params: NoteCollectionParams): NextApiHandler => async (req, id: newId, title, content, - image, + image: `data:${image.type};base64,${image.toString('base64')}`, }); }; diff --git a/packages/iceform-next/package.json b/packages/iceform-next/package.json index 89a75fc..9e0a8ae 100644 --- a/packages/iceform-next/package.json +++ b/packages/iceform-next/package.json @@ -68,9 +68,8 @@ }, "dependencies": { "@theoryofnekomata/formxtra": "^1.0.3", - "@web-std/file": "^3.0.3", "busboy": "^1.6.0", - "nookies": "^2.5.2", + "node-cache": "^5.1.2", "seroval": "^0.10.2" }, "exports": { diff --git a/packages/iceform-next/src/client/components/Form.tsx b/packages/iceform-next/src/client/components/Form.tsx index 79423ac..05d6fc2 100644 --- a/packages/iceform-next/src/client/components/Form.tsx +++ b/packages/iceform-next/src/client/components/Form.tsx @@ -29,6 +29,7 @@ export interface FormProps extends Omit, 'ac responseEncType?: string; serializerOptions?: SerializerOptions; disableFetch?: boolean; + methodFormKey?: string; } export const Form = React.forwardRef(({ @@ -46,6 +47,7 @@ export const Form = React.forwardRef(({ responseEncType = ENCTYPE_APPLICATION_JSON, serializerOptions, disableFetch = false, + methodFormKey = METHOD_FORM_KEY, ...etcProps }, forwardedRef) => { const { handleSubmit } = useFormFetch({ @@ -83,7 +85,7 @@ export const Form = React.forwardRef(({ encType={ENCTYPE_MULTIPART_FORM_DATA} > {serverMethodOverride && ( - + )} {children} @@ -104,4 +106,5 @@ Form.defaultProps = { refresh: undefined, responseEncType: ENCTYPE_APPLICATION_JSON, serializerOptions: undefined, + methodFormKey: METHOD_FORM_KEY, }; diff --git a/packages/iceform-next/src/client/hooks/useFormFetch.ts b/packages/iceform-next/src/client/hooks/useFormFetch.ts index e8f62f8..2e399af 100644 --- a/packages/iceform-next/src/client/hooks/useFormFetch.ts +++ b/packages/iceform-next/src/client/hooks/useFormFetch.ts @@ -58,7 +58,7 @@ export const useFormFetch = ({ form: event.currentTarget, encType, serializers: encTypeSerializers, - options: { + serializerOptions: { ...serializerOptions, submitter, }, diff --git a/packages/iceform-next/src/common/constants.ts b/packages/iceform-next/src/common/constants.ts index ef98770..7da5bd4 100644 --- a/packages/iceform-next/src/common/constants.ts +++ b/packages/iceform-next/src/common/constants.ts @@ -1,3 +1,8 @@ export const PREVENT_REDIRECT_FORM_KEY = '__iceform_prevent_redirect' as const; export const METHOD_FORM_KEY = '__iceform_method' as const; export const METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH', 'DELETE'] as const; +export const REQUEST_ID_FORM_KEY = '__iceform_request_id' as const; +export const CONTENT_TYPE_HEADER_KEY = 'content-type' as const; +export const LOCATION_HEADER_KEY = 'location' as const; +export const CONTENT_ENCODING_HEADER_KEY = 'content-encoding' as const; +export const DEFAULT_ENCODING = 'utf-8' as const; diff --git a/packages/iceform-next/src/server/action/common.ts b/packages/iceform-next/src/server/action/common.ts index 45bbc9f..c122204 100644 --- a/packages/iceform-next/src/server/action/common.ts +++ b/packages/iceform-next/src/server/action/common.ts @@ -23,6 +23,9 @@ export interface ActionWrapperOptions { fn: NextApiHandler, onAction?: OnActionFunction, deserializers?: EncTypeDeserializerMap, + methodFormKey?: string, + requestIdFormKey?: string, + preventRedirectFormKey?: string, /** * Maps the Location header from the handler response to an accessible URL. * @param url diff --git a/packages/iceform-next/src/server/action/gssp.ts b/packages/iceform-next/src/server/action/gssp.ts index 68f53c1..7e28129 100644 --- a/packages/iceform-next/src/server/action/gssp.ts +++ b/packages/iceform-next/src/server/action/gssp.ts @@ -7,7 +7,11 @@ import { deserializeFormObjectBody, EncTypeDeserializerMap, } from '../../utils/serialization'; -import { METHOD_FORM_KEY, PREVENT_REDIRECT_FORM_KEY } from '../../common/constants'; +import { + METHOD_FORM_KEY, + PREVENT_REDIRECT_FORM_KEY, + REQUEST_ID_FORM_KEY, +} from '../../common/constants'; import { ACTION_STATUS_CODE, DEFAULT_METHOD } from '../constants'; import { getBody } from '../../utils/request'; import { @@ -16,25 +20,21 @@ import { } 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, + methodFormKey = METHOD_FORM_KEY as string, ) => { const deserialized = await deserializeFormObjectBody({ req, deserializers, }); const { - [METHOD_FORM_KEY]: method = req.method ?? DEFAULT_METHOD, + [methodFormKey]: method = req.method ?? DEFAULT_METHOD, ...body } = deserialized as { - [METHOD_FORM_KEY]?: string, [key: string]: unknown, }; @@ -80,7 +80,11 @@ export const getServerSideProps = (options: ActionWrapperOptions): GetServerSide ? getFormObjectMethodAndBody : getBinaryMethodAndBody ); - const { body, method } = await methodAndBodyFn(ctx.req, deserializers); + const { body, method } = await methodAndBodyFn( + ctx.req, + deserializers, + options.methodFormKey ?? METHOD_FORM_KEY, + ); const req = { ...ctx.req, body, @@ -93,9 +97,7 @@ export const getServerSideProps = (options: ActionWrapperOptions): GetServerSide } 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); + const requestId = crypto.randomBytes(8).toString('base64url'); await cacheResponse(requestId, res); if (typeof options.onAction === 'function') { @@ -112,9 +114,9 @@ export const getServerSideProps = (options: ActionWrapperOptions): GetServerSide const preventRedirect = ( typeof req.body === 'object' && req.body !== null - && PREVENT_REDIRECT_FORM_KEY in req.body + && (options.preventRedirectFormKey ?? PREVENT_REDIRECT_FORM_KEY) in req.body ); - const redirectDestination = ( + const redirectDestinationRaw = ( res.location && typeof options.mapLocationToRedirectDestination === 'function' && !preventRedirect @@ -122,9 +124,12 @@ export const getServerSideProps = (options: ActionWrapperOptions): GetServerSide ? options.mapLocationToRedirectDestination(referer, res.location) : referer; + const url = new URL(redirectDestinationRaw, 'http://example.com'); + url.searchParams.set(options.requestIdFormKey ?? REQUEST_ID_FORM_KEY, requestId); + return { redirect: { - destination: redirectDestination, + destination: `/${url.pathname}${url.search}`, statusCode: ACTION_STATUS_CODE, }, props: { diff --git a/packages/iceform-next/src/server/cache.ts b/packages/iceform-next/src/server/cache.ts index be4dca3..1679282 100644 --- a/packages/iceform-next/src/server/cache.ts +++ b/packages/iceform-next/src/server/cache.ts @@ -1,5 +1,6 @@ -import { createWriteStream } from 'fs'; -import { readFile, unlink } from 'fs/promises'; +import NodeCache from 'node-cache'; +import { STATUS_CODES } from 'http'; +import { DEFAULT_RESPONSE_STATUS_CODE } from './constants'; interface CacheableResponse { statusCode: number; @@ -8,41 +9,60 @@ interface CacheableResponse { data?: unknown; } -const getFilePathFromRequestId = (requestId: string) => `${requestId}`; +const cache = new NodeCache({ + deleteOnExpire: true, + stdTTL: 60, +}); 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`); + let cacheValue = ''; + cacheValue += `${res.statusCode.toString()} ${res.statusMessage || ''}\n`; if (res.contentType) { - cacheStream.write(`Content-Type: ${res.contentType}\n`); + cacheValue += `Content-Type: ${res.contentType}\n`; } if (res.data) { - cacheStream.write('\n'); - cacheStream.write(res.data as string); + cacheValue += `\n${res.data as string}`; } - return new Promise((resolve) => { - cacheStream.close(resolve); + return new Promise((resolve) => { + cache.set(requestId, cacheValue); + 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 parseCacheValue = (cacheValue: string) => { + const [statusLine, ...headersAndBody] = cacheValue.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 data = 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, + statusMessage: statusMessage || STATUS_CODES[statusCode], contentType, - body, + data, }; }; + +export const retrieveCache = async (requestId: string) => new Promise< + CacheableResponse +>((resolve) => { + const requestBuffer = cache.get(requestId); + + if (!requestBuffer) { + resolve({ + statusCode: DEFAULT_RESPONSE_STATUS_CODE, + statusMessage: STATUS_CODES[DEFAULT_RESPONSE_STATUS_CODE], + contentType: undefined, + data: '', + }); + return; + } + + cache.del(requestId); + const value = parseCacheValue(requestBuffer); + resolve(value); +}); diff --git a/packages/iceform-next/src/server/constants.ts b/packages/iceform-next/src/server/constants.ts index 6b5e431..265d1de 100644 --- a/packages/iceform-next/src/server/constants.ts +++ b/packages/iceform-next/src/server/constants.ts @@ -1,6 +1,3 @@ export const DEFAULT_METHOD = 'GET' as const; -export const DEFAULT_ENCODING = 'utf-8' as const; export const ACTION_STATUS_CODE = 307 as const; // temporary redirect export const DEFAULT_RESPONSE_STATUS_CODE = 200 as const; // ok -export const CONTENT_TYPE_HEADER_KEY = 'content-type' as const; -export const LOCATION_HEADER_KEY = 'location' as const; diff --git a/packages/iceform-next/src/server/destination.ts b/packages/iceform-next/src/server/destination.ts index 5d79cc5..140c54d 100644 --- a/packages/iceform-next/src/server/destination.ts +++ b/packages/iceform-next/src/server/destination.ts @@ -2,11 +2,10 @@ 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 } from './constants'; +import { DEFAULT_METHOD } from './constants'; import { getBody } from '../utils/request'; -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 { METHODS_WITH_BODY, REQUEST_ID_FORM_KEY, DEFAULT_ENCODING } from '../common/constants'; import { retrieveCache } from './cache'; export type DestinationGetServerSideProps< @@ -21,6 +20,7 @@ export type DestinationGetServerSideProps< export interface DestinationWrapperOptions { fn?: DestinationGetServerSideProps; + requestIdKey?: string; } export const getServerSideProps = ( @@ -41,26 +41,31 @@ export const getServerSideProps = ( } const res: NextApiResponse = {}; - - const cookieManager = new CookieManager(ctx); - const resRequestId = cookieManager.getCookie(REQUEST_ID_COOKIE_KEY); - cookieManager.unsetCookie(REQUEST_ID_COOKIE_KEY); + const resRequestId = ctx.query[ + options.requestIdKey ?? REQUEST_ID_FORM_KEY + ] as string | undefined; if (resRequestId) { const { statusCode, statusMessage, contentType, - body: resBody, + data: 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) { - res.body = deserialize(resBody); + if (typeof statusMessage !== 'undefined') { + ctx.res.statusMessage = statusMessage; + } + if (resBody) { + if (contentType === ENCTYPE_APPLICATION_JSON) { + res.body = deserialize(resBody as string); + } else if (contentType === ENCTYPE_APPLICATION_OCTET_STREAM) { + res.body = deserialize(resBody as string); + } else { + const c = console; + c.warn('Could not parse response body, returning nothing'); + res.body = null; + } } else { - const c = console; - c.warn('Could not parse response body, returning nothing'); res.body = null; } } diff --git a/packages/iceform-next/src/server/response.ts b/packages/iceform-next/src/server/response.ts index 47f77b4..f1fc09f 100644 --- a/packages/iceform-next/src/server/response.ts +++ b/packages/iceform-next/src/server/response.ts @@ -1,11 +1,12 @@ 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, DEFAULT_RESPONSE_STATUS_CODE, + ACTION_STATUS_CODE, + DEFAULT_RESPONSE_STATUS_CODE, } from './constants'; +import { CONTENT_TYPE_HEADER_KEY, LOCATION_HEADER_KEY } from '../common/constants'; // for client-side class DummyServerResponse {} diff --git a/packages/iceform-next/src/utils/cookies.ts b/packages/iceform-next/src/utils/cookies.ts deleted file mode 100644 index 59ba349..0000000 --- a/packages/iceform-next/src/utils/cookies.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { IncomingMessage, ServerResponse } from 'http'; -import * as nookies from 'nookies'; -import * as crypto from 'crypto'; - -const COMMON_COOKIE_CONFIG = { - path: '/', - httpOnly: true, -}; - -const COMMON_SET_COOKIE_CONFIG = { - ...COMMON_COOKIE_CONFIG, - maxAge: 30 * 24 * 60 * 60, -}; - -const cookieKeys: Record = {}; - -export const REQUEST_ID_COOKIE_KEY = 'b' as const; -export class CookieManager { - private readonly ctx: { req: IncomingMessage, res: ServerResponse }; - - constructor(ctx: { req: IncomingMessage, res: ServerResponse }) { - // noop - this.ctx = ctx; - } - - private static generateCookieKey(key: string) { - 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], - value, - COMMON_SET_COOKIE_CONFIG, - ); - } - - unsetCookie(key: string) { - nookies.destroyCookie(this.ctx, cookieKeys[key], COMMON_COOKIE_CONFIG); - } - - hasCookie(key: string) { - const cookies = nookies.parseCookies(this.ctx); - return cookieKeys[key] in cookies; - } - - getCookie(key: string) { - const cookies = nookies.parseCookies(this.ctx); - return cookies[cookieKeys[key]]; - } -} diff --git a/packages/iceform-next/src/utils/request.ts b/packages/iceform-next/src/utils/request.ts index c233715..ca640e3 100644 --- a/packages/iceform-next/src/utils/request.ts +++ b/packages/iceform-next/src/utils/request.ts @@ -37,6 +37,11 @@ export const parseMultipartFormData = async ( }); file.on('close', () => { + // filename can be undefined somehow... + if (!filename && buffer.length <= 0) { + return; + } + const bufferMut = buffer as unknown as Record; bufferMut.name = filename; bufferMut.type = mimeType; diff --git a/packages/iceform-next/src/utils/serialization.ts b/packages/iceform-next/src/utils/serialization.ts index 73e89a2..7551864 100644 --- a/packages/iceform-next/src/utils/serialization.ts +++ b/packages/iceform-next/src/utils/serialization.ts @@ -6,8 +6,13 @@ import { ENCTYPE_X_WWW_FORM_URLENCODED, } 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'; +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; @@ -19,7 +24,9 @@ export interface SerializeBodyParams { form: HTMLFormElement, encType: string, serializers?: EncTypeSerializerMap, - options?: SerializerOptions, + serializerOptions?: SerializerOptions, + methodFormKey?: string, + preventRedirectFormKey?: string, } export const DEFAULT_ENCTYPE_SERIALIZERS: EncTypeSerializerMap = { @@ -31,13 +38,15 @@ export const serializeBody = (params: SerializeBodyParams) => { form, encType, serializers = DEFAULT_ENCTYPE_SERIALIZERS, - options, + 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(METHOD_FORM_KEY); - searchParams.delete(PREVENT_REDIRECT_FORM_KEY); + searchParams.delete(methodFormKey); + searchParams.delete(preventRedirectFormKey); return searchParams; } @@ -48,23 +57,25 @@ export const serializeBody = (params: SerializeBodyParams) => { entries: () => IterableIterator<[string, unknown]>; }; }; - const formData = new FormDataUnknown(form, options?.submitter); + 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(METHOD_FORM_KEY); - formData.delete(PREVENT_REDIRECT_FORM_KEY); + formData.delete(methodFormKey); + formData.delete(preventRedirectFormKey); return formData; } if (typeof serializers[encType] === 'function') { const { - [METHOD_FORM_KEY]: _method, - [PREVENT_REDIRECT_FORM_KEY]: _preventRedirect, + // 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, options); + } = getFormValues(form, serializerOptions); return serializers[encType](formValues); } @@ -90,7 +101,7 @@ export const DEFAULT_ENCTYPE_DESERIALIZERS: EncTypeDeserializerMap = { export const deserializeFormObjectBody = async (params: DeserializeBodyParams) => { const { req, deserializers = DEFAULT_ENCTYPE_DESERIALIZERS } = params; - const contentType = req.headers['content-type'] ?? ENCTYPE_APPLICATION_OCTET_STREAM; + const contentType = req.headers[CONTENT_TYPE_HEADER_KEY] ?? ENCTYPE_APPLICATION_OCTET_STREAM; if (contentType?.startsWith(`${ENCTYPE_MULTIPART_FORM_DATA};`)) { return parseMultipartFormData(req); @@ -98,7 +109,7 @@ export const deserializeFormObjectBody = async (params: DeserializeBodyParams) = const bodyRaw = await getBody(req); const encoding = ( - req.headers['content-encoding'] ?? DEFAULT_ENCODING + req.headers[CONTENT_ENCODING_HEADER_KEY] ?? DEFAULT_ENCODING ) as BufferEncoding; const { [contentType]: theDeserializer } = deserializers; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c1efe2..73fddbc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,15 +11,12 @@ 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 - nookies: - specifier: ^2.5.2 - version: 2.5.2 + node-cache: + specifier: ^5.1.2 + version: 5.1.2 seroval: specifier: ^0.10.2 version: 0.10.2 @@ -1515,31 +1512,6 @@ 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 @@ -1987,6 +1959,11 @@ packages: engines: {node: '>=0.8'} dev: true + /clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + dev: false + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -3578,6 +3555,7 @@ 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==} @@ -4224,6 +4202,13 @@ packages: - '@babel/core' - babel-plugin-macros + /node-cache@5.1.2: + resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==} + engines: {node: '>= 8.0.0'} + dependencies: + clone: 2.1.2 + dev: false + /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} @@ -5457,16 +5442,6 @@ 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'} @@ -5648,19 +5623,6 @@ 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'}