diff --git a/.idea/prettier.xml b/.idea/prettier.xml index b0ab31a..0c83ac4 100644 --- a/.idea/prettier.xml +++ b/.idea/prettier.xml @@ -1,6 +1,7 @@ + \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js index 8d5469f..c3e0bf1 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,9 +1,7 @@ "use strict"; -const tailwindConfig = require("prettier-plugin-tailwindcss"); - module.exports = { - plugins: [tailwindConfig], - printWidth: 80, + plugins: [require("prettier-plugin-tailwindcss")], + printWidth: 60, tabWidth: 2, useTabs: false, semi: true, diff --git a/package.json b/package.json index 2fee51f..711a552 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,12 @@ "googleapis": "^118.0.0", "jsonwebtoken": "^9.0.0", "mime-types": "^2.1.35", - "next": "13.3.0", + "next": "^13.4.3", "next-seo": "^6.0.0", "nextjs-progressbar": "^0.0.16", "postcss": "8.4.22", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-h5-audio-player": "^3.8.6", "react-icons": "^4.8.0", "react-loading": "^2.0.3", @@ -55,8 +55,9 @@ "@types/node": "18.15.11", "@types/three": "^0.150.2", "cypress": "^12.10.0", + "encoding": "^0.1.13", "eslint": "8.38.0", - "eslint-config-next": "13.3.0", + "eslint-config-next": "^13.4.3", "prettier": "^2.8.7", "prettier-plugin-tailwindcss": "^0.2.7", "typescript": "5.0.4" diff --git a/src/app/(api)/api/banner/route.ts b/src/app/(api)/api/banner/route.ts new file mode 100644 index 0000000..8e70b86 --- /dev/null +++ b/src/app/(api)/api/banner/route.ts @@ -0,0 +1,89 @@ +import { NextRequest, NextResponse } from "next/server"; +import { ErrorResponse } from "types/googleapis"; +import getSearchParams from "utils/getSearchParams"; +import { ExtendedError } from "utils/driveHelper"; +import driveClient from "utils/driveClient"; +import { shortDecrypt } from "utils/encryptionHelper"; + +export async function GET(request: NextRequest) { + const _start = Date.now(); + try { + const { id } = getSearchParams(request.url, ["id"]); + if (!id) { + throw new ExtendedError( + "No id provided", + 400, + "badRequest", + ); + } + + const getMetadata = driveClient.files.get({ + fileId: shortDecrypt(id), + fields: "id, name, mimeType", + }); + const getStream = driveClient.files.get( + { + fileId: shortDecrypt(id), + alt: "media", + }, + { responseType: "stream" }, + ); + + const [metadata, stream] = await Promise.all([ + getMetadata, + getStream, + ]); + + const arrayBuffer = await new Promise( + (resolve, reject) => { + const chunks: Buffer[] = []; + stream.data.on("data", (chunk) => + chunks.push(chunk), + ); + stream.data.on("end", () => { + const buffer = Buffer.concat(chunks); + resolve( + buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength, + ), + ); + }); + stream.data.on("error", reject); + }, + ); + + return new NextResponse(arrayBuffer, { + status: 200, + headers: { + "Content-Type": + metadata.data.mimeType || + "application/octet-stream", + "Cache-Control": + "public, max-age=31536000, immutable", + "Content-Disposition": `inline; filename="${metadata.data.name}"`, + }, + }); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return NextResponse.json(payload, { + status: payload.code || 500, + }); + } +} diff --git a/src/app/(api)/api/file/[...path]/route.ts b/src/app/(api)/api/file/[...path]/route.ts new file mode 100644 index 0000000..31f7359 --- /dev/null +++ b/src/app/(api)/api/file/[...path]/route.ts @@ -0,0 +1,211 @@ +import { NextRequest, NextResponse } from "next/server"; +import { ErrorResponse } from "types/googleapis"; +import { ExtendedError } from "utils/driveHelper"; +import apiConfig from "config/api.config"; +import driveClient from "utils/driveClient"; +import { + shortDecrypt, + shortEncrypt, +} from "utils/encryptionHelper"; + +export async function GET( + request: NextRequest, + { params }: { params: { path: string[] } }, +) { + const _start = Date.now(); + try { + const pathArray = params.path; + + const getRootId = + apiConfig.files.rootFolder !== "root" + ? apiConfig.files.rootFolder + : driveClient.files + .get({ + fileId: "root", + fields: "id", + }) + .then((res) => res.data.id); + const getPathId = pathArray.map(async (path, index) => { + const query = [ + "trashed = false", + "'me' in owners", + `name = '${path}'`, + ]; + const fetchFolderContents = + await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: "files(id, name, mimeType, parents)", + }); + + if (!fetchFolderContents.data.files?.length) { + throw new ExtendedError( + `Path ${path} is not found`, + 404, + "notFound", + ); + } + + return { + path, + data: fetchFolderContents.data.files.map( + (file) => ({ + id: file.id, + parents: file.parents?.[0], + mimeType: file.mimeType, + }), + ), + }; + }); + + const [rootId, pathId] = await Promise.all([ + getRootId, + Promise.all(getPathId), + ]); + + const selectedPath: string[] = []; + const mapIds: Record[] = []; + + // Check path validity + pathId.forEach((path, index) => { + if (index === 0) { + // Check if root folder inside data id + const checkRoot = path.data.find( + (item) => item.parents === rootId, + ); + if (!checkRoot) { + throw new ExtendedError( + `Path "${path.path}" is either not found or not valid`, + 400, + "badRequest", + ); + } + + selectedPath.push(checkRoot.id as string); + mapIds.push({ + name: path.path, + id: shortEncrypt(checkRoot.id as string), + mimeType: checkRoot.mimeType as string, + }); + } else { + const checkPath = path.data.find( + (item) => + item.parents === selectedPath[index - 1], + ); + if (!checkPath) { + throw new ExtendedError( + `Path "${path.path}" is either not found or not valid`, + 400, + "badRequest", + ); + } + + selectedPath.push(checkPath.id as string); + mapIds.push({ + name: path.path, + id: shortEncrypt(checkPath.id as string), + mimeType: checkPath.mimeType as string, + }); + } + }); + + // Check for protected folder + const getPassword = mapIds.map(async (path) => { + if ( + path.mimeType !== + "application/vnd.google-apps.folder" + ) + return; + + const query = [ + "trashed = false", + "'me' in owners", + `'${shortDecrypt(path.id)}' in parents`, + `name = '${apiConfig.files.specialFile.password}'`, + ]; + + const folderContents = await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: "files(id, name, mimeType, parents)", + }); + + if (!folderContents.data.files?.length) return; + + return { + path: path.name, + password: folderContents.data.files?.[0].id, + }; + }); + + let password = await Promise.all(getPassword); + password = password.filter( + (item) => item !== undefined, + ); + const lastProtectedFolder = + password[password.length - 1]; + + // Check password + if (lastProtectedFolder) { + const pass = request.headers.get( + "next-gdrive-password", + ); + if (!pass) { + throw new ExtendedError( + `You are trying to access a protected folder, please provide a password`, + 401, + "unauthorized", + ); + } + const passwordFile = await driveClient.files.get( + { + fileId: lastProtectedFolder.password as string, + alt: "media", + }, + { responseType: "text" }, + ); + + if (passwordFile.data !== pass) { + throw new ExtendedError( + `The password you entered is incorrect`, + 401, + "unauthorized", + ); + } + } + + const payload = { + success: true, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: 200, + data: mapIds, + }; + + return NextResponse.json(payload, { + status: 200, + headers: { + "Cache-Control": apiConfig.cache, + }, + }); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return NextResponse.json(payload, { + status: payload.code || 500, + }); + } +} diff --git a/src/app/(api)/api/files/[encryptedId]/route.ts b/src/app/(api)/api/files/[encryptedId]/route.ts new file mode 100644 index 0000000..1ffc0c1 --- /dev/null +++ b/src/app/(api)/api/files/[encryptedId]/route.ts @@ -0,0 +1,206 @@ +import { + ErrorResponse, + FileResponse, + FilesResponse, +} from "types/googleapis"; +import { NextRequest, NextResponse } from "next/server"; +import getSearchParams from "utils/getSearchParams"; +import { + shortDecrypt, + shortEncrypt, +} from "utils/encryptionHelper"; +import driveClient from "utils/driveClient"; +import apiConfig from "config/api.config"; +import { + ExtendedError, + hiddenFiles, +} from "utils/driveHelper"; + +export async function GET( + request: NextRequest, + { params }: { params: { encryptedId: string } }, +) { + const _start = Date.now(); + + try { + const { pageToken, banner, thumbnail } = + getSearchParams(request.url, ["pageToken", "banner"]); + const id = shortDecrypt(params.encryptedId); + if (id === apiConfig.files.rootFolder) { + return NextResponse.redirect( + `${apiConfig.basePath}/api/files`, + { + status: 301, + }, + ); + } + + const file = await driveClient.files.get({ + fileId: id, + fields: apiConfig.files.field, + }); + + if (!file || file.data.trashed) { + const msg = file.data.trashed + ? "File has been deleted" + : "File not found"; + throw new ExtendedError(msg, 404, "notFound"); + } + + if ( + file.data.mimeType !== + "application/vnd.google-apps.folder" + ) { + if (thumbnail === "1") { + return NextResponse.redirect( + file.data.thumbnailLink as string, + { + status: 302, + }, + ); + } + + const payload: FileResponse = { + success: true, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + file: { + ...file.data, + id: shortEncrypt(file.data.id as string), + webContentLink: + shortEncrypt( + file.data.webContentLink as string, + ) || undefined, + }, + }; + + return NextResponse.json(payload, { + status: 200, + headers: { + "Cache-Control": apiConfig.cache, + }, + }); + } + + const query = [ + `'${id}' in parents`, + "trashed = false", + "'me' in owners", + ]; + const folderContents = await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: `files(${apiConfig.files.field}), nextPageToken`, + orderBy: apiConfig.files.orderBy, + pageSize: apiConfig.files.itemsPerPage, + pageToken: pageToken || undefined, + }); + + const readmeFile = folderContents.data.files?.find( + (file) => + file.name === apiConfig.files.specialFile.readme, + ); + const bannerFile = folderContents.data.files?.find( + (file) => + file.name?.startsWith( + apiConfig.files.specialFile.banner, + ) && file.mimeType?.startsWith("image/"), + ); + + if (banner === "1") { + if (!bannerFile) { + throw new ExtendedError( + "Banner not found.", + 404, + "notFound", + ); + } + if ( + Number(bannerFile.size) > + apiConfig.files.download.maxFileSize + ) { + return NextResponse.redirect( + bannerFile.webContentLink as string, + { status: 302 }, + ); + } + + return NextResponse.redirect( + `${apiConfig.basePath}/api/banner?id=${shortEncrypt( + bannerFile.id as string, + )}`, + { + status: 302, + }, + ); + } + + const folderList = + folderContents.data.files + ?.filter( + (file) => + file.mimeType === + "application/vnd.google-apps.folder", + ) + .map((file) => ({ + ...file, + id: shortEncrypt(file.id as string), + })) || []; + const fileList = + folderContents.data.files + ?.filter( + (file) => + file.mimeType !== + "application/vnd.google-apps.folder" && + !hiddenFiles.some((hiddenFile) => + file.name?.startsWith(hiddenFile), + ), + ) + .map((file) => ({ + ...file, + id: shortEncrypt(file.id as string), + webContentLink: + shortEncrypt(file.webContentLink as string) || + undefined, + })) || []; + + const payload: FilesResponse = { + success: true, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + folders: folderList, + files: fileList, + isReadmeExists: !!readmeFile, + isBannerExists: !!bannerFile, + nextPageToken: + folderContents.data.nextPageToken || undefined, + }; + + return NextResponse.json(payload, { + status: 200, + headers: { + "Cache-Control": apiConfig.cache, + }, + }); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return NextResponse.json(payload, { + status: payload.code || 500, + }); + } +} diff --git a/src/app/(api)/api/files/route.ts b/src/app/(api)/api/files/route.ts new file mode 100644 index 0000000..5a3dfaa --- /dev/null +++ b/src/app/(api)/api/files/route.ts @@ -0,0 +1,145 @@ +import { NextRequest, NextResponse } from "next/server"; +import apiConfig from "config/api.config"; +import getSearchParams from "utils/getSearchParams"; +import { + ErrorResponse, + FilesResponse, +} from "types/googleapis"; +import driveClient from "utils/driveClient"; +import { + ExtendedError, + hiddenFiles, +} from "utils/driveHelper"; +import { shortEncrypt } from "utils/encryptionHelper"; + +export async function GET(request: NextRequest) { + const _start = Date.now(); + try { + const { pageToken, banner } = getSearchParams( + request.url, + ["pageToken", "banner"], + ); + + const query: string[] = [ + "trashed = false", + "'me' in owners", + `parents = '${apiConfig.files.rootFolder}'`, + ]; + const fetchFolderContents = + await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: `files(${apiConfig.files.field}), nextPageToken`, + orderBy: apiConfig.files.orderBy, + pageSize: apiConfig.files.itemsPerPage, + pageToken: pageToken || undefined, + }); + + const readmeFile = fetchFolderContents.data.files?.find( + (file) => + file.name === apiConfig.files.specialFile.readme, + ); + const bannerFile = fetchFolderContents.data.files?.find( + (file) => + file.name?.startsWith( + apiConfig.files.specialFile.banner, + ) && file.mimeType?.startsWith("image/"), + ); + + if (banner === "1") { + if (!bannerFile) { + throw new ExtendedError( + "Banner not found.", + 404, + "notFound", + ); + } + if ( + Number(bannerFile.size) > + apiConfig.files.download.maxFileSize + ) { + return NextResponse.redirect( + bannerFile.webContentLink as string, + { status: 302 }, + ); + } + + return NextResponse.redirect( + `${apiConfig.basePath}/api/banner?id=${shortEncrypt( + bannerFile.id as string, + )}`, + { + status: 302, + }, + ); + } + + const folderList = + fetchFolderContents.data.files + ?.filter( + (file) => + file.mimeType === + "application/vnd.google-apps.folder", + ) + .map((file) => ({ + ...file, + id: shortEncrypt(file.id as string), + })) || []; + const fileList = + fetchFolderContents.data.files + ?.filter( + (file) => + file.mimeType !== + "application/vnd.google-apps.folder" && + !hiddenFiles.some((hiddenFile) => + file.name?.startsWith(hiddenFile), + ), + ) + .map((file) => ({ + ...file, + id: shortEncrypt(file.id as string), + webContentLink: + shortEncrypt(file.webContentLink as string) || + undefined, + })) || []; + + const payload: FilesResponse = { + success: true, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + folders: folderList, + files: fileList, + isReadmeExists: !!readmeFile, + isBannerExists: !!bannerFile, + nextPageToken: + fetchFolderContents.data.nextPageToken || undefined, + }; + + return NextResponse.json(payload, { + status: 200, + headers: { + "Cache-Control": apiConfig.cache, + }, + }); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return NextResponse.json(payload, { + status: payload.code || 500, + }); + } +} diff --git a/src/app/(api)/api/test/route.ts b/src/app/(api)/api/test/route.ts new file mode 100644 index 0000000..6137760 --- /dev/null +++ b/src/app/(api)/api/test/route.ts @@ -0,0 +1,94 @@ +import { NextResponse } from "next/server"; +import { ErrorResponse } from "types/googleapis"; +import driveClient from "utils/driveClient"; + +export async function GET() { + const _start = Date.now(); + try { + const ids = [ + "18h88Fit0MgIrHTGBEtpIbcHIZ_GEgpuP", + "1KgPV6QB1GYT8fmn2uTfbtr9rDXqcRR0j", + "1pctigJQKaF7GbDU1t8s0gT-0fPfNhl0B", + "16p09HGtImeuuvn06W6hDlDzSPqF4e80g", + ]; + const fields = "id, name, mimeType, parents"; + const startFetch0 = Date.now(); + const id0 = driveClient.files + .get({ + fileId: ids[0], + fields, + }) + .then((res) => res.data); + const startFetch1 = Date.now(); + const id1 = driveClient.files.get({ + fileId: ids[1], + fields, + }); + const startFetch2 = Date.now(); + const id2 = driveClient.files + .get({ + fileId: ids[2], + fields, + }) + .then((res) => res.data); + const startFetch3 = Date.now(); + const id3 = driveClient.files.get({ + fileId: ids[3], + fields, + }); + const fetchStarted = Date.now(); + const files = await Promise.all([id0, id1, id2, id3]); + const fetchFinished = Date.now(); + // const files = await Promise.all( + // ids.map(async (id) => { + // const file = await driveClient.files.get({ + // fileId: id, + // fields: "id, name, mimeType, parents", + // }); + // return file.data; + // }), + // ); + + const payload = { + success: true, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: 200, + timing: { + fetch0: startFetch0, + fetch1: startFetch1, + fetch2: startFetch2, + fetch3: startFetch3, + fetchStarted, + fetchFinished, + duration: fetchFinished - startFetch0, + }, + files, + }; + + return NextResponse.json(payload, { + status: 200, + }); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return NextResponse.json(payload, { + status: payload.code || 500, + }); + } +} diff --git a/src/app/(api)/api/validatePath/route.ts b/src/app/(api)/api/validatePath/route.ts new file mode 100644 index 0000000..8b770b2 --- /dev/null +++ b/src/app/(api)/api/validatePath/route.ts @@ -0,0 +1,241 @@ +import { NextRequest, NextResponse } from "next/server"; +import { + ErrorResponse, + TPath, + ValidatePathResponse, +} from "types/googleapis"; +import { ExtendedError } from "utils/driveHelper"; +import apiConfig from "config/api.config"; +import driveClient from "utils/driveClient"; +import { + shortDecrypt, + shortEncrypt, +} from "utils/encryptionHelper"; +import { cookies } from "next/headers"; + +export async function GET(request: NextRequest) { + const _start = Date.now(); + try { + const path = new URL(request.url).searchParams.get( + "path", + ); + if (!path) { + throw new ExtendedError( + "No path provided", + 400, + "badRequest", + ); + } + + let pathArray: string[] = path.split(/[\\/]/); + pathArray = pathArray.filter((path) => path !== ""); + + const getRootId = + apiConfig.files.rootFolder !== "root" + ? apiConfig.files.rootFolder + : driveClient.files + .get({ + fileId: "root", + fields: "id", + }) + .then((res) => res.data.id); + const getPathId = pathArray.map(async (path, index) => { + const query = [ + "trashed = false", + "'me' in owners", + `name = '${path}'`, + ]; + const fetchFolderContents = + await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: "files(id, name, mimeType, parents)", + }); + + if (!fetchFolderContents.data.files?.length) { + throw new ExtendedError( + `Path ${path} is not found`, + 404, + "notFound", + ); + } + + return { + path, + data: fetchFolderContents.data.files.map( + (file) => ({ + id: file.id, + parents: file.parents?.[0], + mimeType: file.mimeType, + }), + ), + }; + }); + + const [rootId, pathId] = await Promise.all([ + getRootId, + Promise.all(getPathId), + ]); + + const selectedPath: string[] = []; + const mapIds: TPath[] = []; + + // Check path validity + pathId.forEach((path, index) => { + if (index === 0) { + // Check if root folder inside data id + const checkRoot = path.data.find( + (item) => item.parents === rootId, + ); + if (!checkRoot) { + throw new ExtendedError( + `Path "${path.path}" is either not found or not valid`, + 400, + "badRequest", + ); + } + + selectedPath.push(checkRoot.id as string); + mapIds.push({ + name: path.path, + id: shortEncrypt(checkRoot.id as string), + mimeType: checkRoot.mimeType as string, + }); + } else { + const checkPath = path.data.find( + (item) => + item.parents === selectedPath[index - 1], + ); + if (!checkPath) { + throw new ExtendedError( + `Path "${path.path}" is either not found or not valid`, + 400, + "badRequest", + ); + } + + selectedPath.push(checkPath.id as string); + mapIds.push({ + name: path.path, + id: shortEncrypt(checkPath.id as string), + mimeType: checkPath.mimeType as string, + }); + } + }); + + // Check for protected folder + const getPassword = mapIds.map(async (path) => { + if ( + path.mimeType !== + "application/vnd.google-apps.folder" + ) + return; + + const query = [ + "trashed = false", + "'me' in owners", + `'${shortDecrypt(path.id)}' in parents`, + `name = '${apiConfig.files.specialFile.password}'`, + ]; + + const folderContents = await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: "files(id, name, mimeType, parents)", + }); + + if (!folderContents.data.files?.length) return; + + return { + path: path.name, + password: folderContents.data.files?.[0].id, + }; + }); + + let password = await Promise.all(getPassword); + password = password.filter( + (item) => item !== undefined, + ); + const lastProtectedFolder = + password[password.length - 1]; + + // Check password + if (lastProtectedFolder?.password) { + const fetchPassword = await driveClient.files.get( + { + fileId: lastProtectedFolder.password, + alt: "media", + }, + { responseType: "text" }, + ); + + const userPassword = cookies().get( + `next-gdrive-password/${encodeURIComponent( + lastProtectedFolder.path, + )}`, + )?.value; + console.log( + `next-gdrive-password/${encodeURIComponent( + lastProtectedFolder.path, + )}`, + shortEncrypt("loremipsum"), + ); + if (!userPassword) { + throw new ExtendedError( + `You are not authorized to access this folder`, + 401, + "unauthorized", + ); + } + console.log( + shortDecrypt(userPassword), + fetchPassword.data, + ); + if ( + shortDecrypt(userPassword) !== fetchPassword.data + ) { + throw new ExtendedError( + `The password you entered is incorrect`, + 401, + "unauthorized", + ); + } + } + + const payload: ValidatePathResponse = { + success: true, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + data: mapIds, + password: lastProtectedFolder?.password + ? shortEncrypt(lastProtectedFolder.password) + : undefined, + }; + + return NextResponse.json(payload, { + status: 200, + headers: { + "Cache-Control": apiConfig.cache, + }, + }); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return NextResponse.json(payload, { + status: payload.code || 500, + }); + } +} diff --git a/src/app/(setup)/next-gdrive-index/page.tsx b/src/app/(setup)/next-gdrive-index/page.tsx new file mode 100644 index 0000000..1ad0374 --- /dev/null +++ b/src/app/(setup)/next-gdrive-index/page.tsx @@ -0,0 +1,3 @@ +function SetupPage() {} + +export default SetupPage; diff --git a/src/app/(setup)/next-gdrive-index/step-1/page.tsx b/src/app/(setup)/next-gdrive-index/step-1/page.tsx new file mode 100644 index 0000000..5c257bb --- /dev/null +++ b/src/app/(setup)/next-gdrive-index/step-1/page.tsx @@ -0,0 +1,5 @@ +function SetupFirstStep() { + return ; +} + +export default SetupFirstStep; diff --git a/src/app/[...path]/error.tsx b/src/app/[...path]/error.tsx new file mode 100644 index 0000000..68cea11 --- /dev/null +++ b/src/app/[...path]/error.tsx @@ -0,0 +1,22 @@ +"use client"; + +import { useEffect } from "react"; + +export default function Error({ + error, + reset, +}: { + error: Error; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( + + Something went wrong! + reset()}>Try again + + ); +} diff --git a/src/app/[...path]/page.tsx b/src/app/[...path]/page.tsx new file mode 100644 index 0000000..5c7202a --- /dev/null +++ b/src/app/[...path]/page.tsx @@ -0,0 +1,26 @@ +import { cookies } from "next/headers"; +import axios from "axios"; +import { ValidatePathResponse } from "types/googleapis"; + +async function _validatePath(path: string) { + const { data } = await axios.get( + `/api/validate-path?path=${path}`, + ); + return data; +} + +type Props = { + params: { + path: string[]; + }; +}; +async function ListIdPage({ params }: Props) { + const validatePath = await _validatePath( + params.path.join("/"), + ); + const c = cookies(); + const token = c.has("token") ? c.get("token")?.name : ""; + return lorem - {token}; +} + +export default ListIdPage; diff --git a/src/app/contextWrapper.tsx b/src/app/contextWrapper.tsx new file mode 100644 index 0000000..bb2c076 --- /dev/null +++ b/src/app/contextWrapper.tsx @@ -0,0 +1,63 @@ +"use client"; + +import { ThemeContext, TTheme } from "context/themeContext"; +import { useEffect, useState } from "react"; +import { TLayout } from "context/layoutContext"; + +type Props = { + children: React.ReactNode; +}; +function ContextWrapper({ children }: Props) { + const [theme, setTheme] = useState("light"); + const [layout, setLayout] = useState("grid"); + + useEffect(() => { + console.log("ContextWrapper useEffect"); + if (typeof window === "undefined") return; + const lsTheme = localStorage.getItem("theme"); + const lsLayout = localStorage.getItem("layout"); + const lsSitePassword = + localStorage.getItem("sitePassword"); + + if ( + lsTheme && + (lsTheme === "dark" || lsTheme === "light") + ) { + setTheme(lsTheme as TTheme); + } + if ( + lsLayout && + (lsLayout === "grid" || lsLayout === "list") + ) { + setLayout(lsLayout as TLayout); + } + }, []); + + useEffect(() => { + if (theme === "light") { + document.body.classList.remove("dark"); + } else { + document.body.classList.add("dark"); + } + }, [theme]); + + return ( + <> + { + if (typeof window !== "undefined") { + localStorage.setItem("theme", theme); + } + setTheme(theme); + }, + }} + > + {children} + + > + ); +} + +export default ContextWrapper; diff --git a/src/app/download/[...path]/route.ts b/src/app/download/[...path]/route.ts new file mode 100644 index 0000000..f47ca13 --- /dev/null +++ b/src/app/download/[...path]/route.ts @@ -0,0 +1,142 @@ +import { ErrorResponse } from "types/googleapis"; +import { NextRequest, NextResponse } from "next/server"; +import driveClient from "utils/driveClient"; +import apiConfig from "config/api.config"; +import { ExtendedError } from "utils/driveHelper"; +import axios from "axios"; + +export async function GET( + request: NextRequest, + { params }: { params: { path: string[] } }, +) { + const _start = Date.now(); + + try { + const validatePath = await axios.get( + `${ + apiConfig.basePath + }/api/validatePath?path=${params.path.join("/")}`, + ); + + return NextResponse.json( + { data: validatePath.data }, + { status: 200 }, + ); + + let id = ""; + // const id = shortDecrypt(params.encryptedId); + // const { token } = getSearchParams(request.url, [ + // "token", + // ]); + // + // if (!apiConfig.files.download.allowProtectedFile) { + // const decryptToken = shortDecrypt(token ? token : ""); + // const parsedToken = JSON.parse( + // decryptToken || "{}", + // ) as DownloadToken; + // if (!parsedToken) { + // throw new ExtendedError( + // "Protected file not allowed.", + // 403, + // "forbidden", + // ); + // } + // } + + const getMetadata = driveClient.files.get({ + fileId: id, + fields: apiConfig.files.field, + }); + const getStream = driveClient.files.get( + { + fileId: id, + alt: "media", + }, + { responseType: "stream" }, + ); + + const [metadata, stream] = await Promise.all([ + getMetadata, + getStream, + ]); + + if (!metadata || metadata.data.trashed) { + const msg = metadata.data.trashed + ? "File has been deleted" + : "File not found"; + throw new ExtendedError(msg, 404, "notFound"); + } + + if ( + metadata.data.mimeType === + "application/vnd.google-apps.folder" + ) { + throw new ExtendedError( + "Can't download folder", + 400, + "badRequest", + ); + } + if ( + Number(metadata.data.size) > + apiConfig.files.download.maxFileSize + ) { + return NextResponse.redirect( + metadata.data.webContentLink as string, + { status: 302 }, + ); + } + + const arrayBuffer = await new Promise( + (resolve, reject) => { + const chunks: Buffer[] = []; + stream.data.on("data", (chunk) => + chunks.push(chunk), + ); + stream.data.on("end", () => { + const buffer = Buffer.concat(chunks); + resolve( + buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength, + ), + ); + }); + stream.data.on("error", reject); + }, + ); + + return new NextResponse(arrayBuffer, { + status: 200, + headers: { + "Content-Type": + metadata.data.mimeType || + "application/octet-stream", + "Cache-Control": + "public, max-age=31536000, immutable", + "Content-Disposition": `inline; filename="${metadata.data.name}"`, + }, + }); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return NextResponse.json(payload, { + status: payload.code || 500, + }); + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..be3a0d1 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,70 @@ +import { Metadata } from "next"; + +import Navbar from "components/Navbar"; +import Footer from "components/Footer"; +import ContextWrapper from "./contextWrapper"; +import { + Exo_2, + JetBrains_Mono, + Source_Sans_Pro, +} from "next/font/google"; + +import siteConfig from "config/site.config"; +import "styles/globals.css"; + +const exo2 = Exo_2({ + weight: ["300", "400", "600", "700"], + style: ["normal", "italic"], + display: "auto", + subsets: ["latin", "latin-ext"], + variable: "--font-exo2", +}); +const sourceSansPro = Source_Sans_Pro({ + weight: ["300", "400", "600", "700"], + style: ["normal", "italic"], + display: "auto", + subsets: ["latin", "latin-ext"], + variable: "--font-source-sans-pro", +}); +const jetBrainsMono = JetBrains_Mono({ + weight: ["300", "400", "600", "700"], + style: ["normal", "italic"], + display: "auto", + subsets: ["latin", "latin-ext"], + variable: "--font-jetbrains-mono", +}); + +export const metadata: Metadata = { + title: siteConfig.siteName, + description: siteConfig.siteDescription, +}; + +type Props = { + children: React.ReactNode; +}; + +function RootLayout({ children }: Props) { + return ( + + + + + + + {children} + + + + + + + ); +} + +export default RootLayout; diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..9baac6d --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,22 @@ +import driveClient from "utils/driveClient"; + +async function getRootFiles() {} +async function getPassword() {} +async function getReadme() {} + +async function RootPage() { + const start = new Date().getTime(); + const [files, password, readme] = await Promise.all([ + getRootFiles(), + getPassword(), + getReadme(), + ]); + const end = new Date().getTime(); + return ( + + {end - start} + + ); +} + +export default RootPage; diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx new file mode 100644 index 0000000..76766eb --- /dev/null +++ b/src/components/Footer/index.tsx @@ -0,0 +1,31 @@ +import siteConfig from "config/site.config"; +import Link from "next/link"; + +function Footer() { + return ( + + ); +} + +export default Footer; diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx new file mode 100644 index 0000000..f559e94 --- /dev/null +++ b/src/components/Navbar/index.tsx @@ -0,0 +1,186 @@ +"use client"; + +import Link from "next/link"; + +import siteConfig from "config/site.config"; +import { + MdClose, + MdDarkMode, + MdLightMode, + MdLogout, + MdMenu, + MdSearch, +} from "react-icons/md"; +import { useContext, useState } from "react"; +import { + ThemeContext, + TThemeContext, +} from "context/themeContext"; + +function Navbar() { + const { theme, setTheme } = + useContext(ThemeContext); + const [isMenuOpen, setIsMenuOpen] = + useState(false); + + return ( + <> + + + + + {siteConfig.navbar.title || siteConfig.siteName} + + + + + + {siteConfig.navbar.links.map((link) => ( + + {link.icon && } + {link.name} + + ))} + + + + + + + setTheme(theme === "dark" ? "light" : "dark") + } + > + + + + + + + setIsMenuOpen(!isMenuOpen)} + > + + + + + + + + {siteConfig.navbar.links.map((link, index) => ( + + {link.icon && } + {link.name} + + ))} + + > + ); +} + +export default Navbar; diff --git a/src/components/layout/Footer/index.tsx b/src/components/layout/Footer/index.tsx deleted file mode 100644 index 8919336..0000000 --- a/src/components/layout/Footer/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import siteConfig from "config/site.config"; - -export default function Footer() { - const currentYear = new Date().getFullYear(); - - return ( - - - {currentYear} {siteConfig.footerText} - Powered by{" "} - - next-gdrive-index - {" "} - ❤️ - - - ); -} diff --git a/src/config/api.config.js b/src/config/api.config.js index bb65dd8..f019bba 100644 --- a/src/config/api.config.js +++ b/src/config/api.config.js @@ -2,46 +2,111 @@ module.exports = { // Client ID are safe to expose client_id: "126409166174-l2unckghm8d5m1gp3deu0uaps5son64f.apps.googleusercontent.com", - // Client secret and refresh token are encrypted - // Using encryption key - // https://drive.mbaharip.com/gudora-setup - client_secret: - "54f0505d11a8fe04d22ec642cdde4728:a6f911c70b0305a51160a2133a9bd2ada3c120567857002311f17bf29cc69e07d7f1a9ca20fd119d684191aee6e3866a", - refresh_token: - "209b4d2ff44bf877ce0636ddc81cdc9a:4a40e172ba5d6c1ee8daf50389841bd2c9d1153163196da0dab63c07b6f305bedafaa209a9e6adbb8d2377c74ba98cf818b6e73b8f57098686ed0b89bec93b0186047ccaab8804310a2ab4e46069d1cac8c322f0f1e206808ac62c18639610be07559eebad904905ba185cae8ba9fbe9", + dev_client_id: + "126409166174-l0f9hdblsrmhkt9jeue9m8o93skfs1sr.apps.googleusercontent.com", - files: { - // How many files to show per page - itemsPerPage: 25, - // Max number of files to show in search result - searchResult: 5, - // Starting point of the drive - // Use 'root' to use My Drive as starting point - // Or use folder id to use a specific folder as starting point - // TODO: Change when final - // rootFolder: "root", - rootFolder: "1KgPV6QB1GYT8fmn2uTfbtr9rDXqcRR0j", // Test folder - // Limit breadcrumb to specific depth - // 0 = Unlimited - // 1 = Only show current folder - // 2 = Show current folder and its parent - // 3 = Show current folder and its parent and grandparent - // and so on. - // Warning: More parent = More API calls = Slower loading time - // There are no workaround for this yet, since Google Drive API v3 return only 1 parent - // Default: 2 - breadcrumbDepth: 2, + /** + * Change this value to match your platform. + */ + basePath: + process.env.NODE_ENV === "production" + ? `https://${process.env.VERCEL_URL}` + : "http://localhost:5000", + + old: { + files: { + // How many files to show per page + itemsPerPage: 25, + // Max number of files to show in search result + searchResult: 5, + // Starting point of the drive + // Use 'root' to use My Drive as starting point + // Or use folder id to use a specific folder as starting point + // TODO: Change when final + // rootFolder: "root", + rootFolder: "1KgPV6QB1GYT8fmn2uTfbtr9rDXqcRR0j", // Test folder + // Limit breadcrumb to specific depth + // 0 = Unlimited + // 1 = Only show current folder + // 2 = Show current folder and its parent + // 3 = Show current folder and its parent and grandparent + // and so on. + // Warning: More parent = More API calls = Slower loading time + // There are no workaround for this yet, since Google Drive API v3 return only 1 parent + // Default: 2 + breadcrumbDepth: 2, + }, + + // Cache control header + // https://web.dev/uses-long-cache-ttl/ + // Default: 1 minute + cache: "s-maxage=60, stale-while-revalidate", + + // Max response body size + // If you're using Vercel, the max response size is 4.5MB + // https://vercel.com/docs/platform/limits#serverless-function-payload-size-limit + // If you're using another platform that has different limit, + // change this value to match your platform. + maxResponseSize: 4 * 1024 * 1024, }, - // Cache control header - // https://web.dev/uses-long-cache-ttl/ - // Default: 1 minute - cache: "s-maxage=60, stale-while-revalidate", + files: { + field: + "id, name, mimeType, thumbnailLink, fileExtension, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, iconLink, trashed", + orderBy: "folder, name asc, modifiedTime desc", + /** + * Special file names that will be used for certain purposes + * These files will be ignored when searching for files + * and will be hidden from the file list + */ + specialFile: { + password: ".password", + readme: ".readme.md", + /** + * Banner are used for generating custom open graph image for folder + * By default, all folder will use the og.png inside public folder + */ + banner: ".banner", + }, + itemsPerPage: 50, + searchResult: 10, + /** + * Starting point of the drive + * Use 'root' to use My Drive as starting point + * Or use folder id to use a specific folder as starting point + */ + rootFolder: "1KgPV6QB1GYT8fmn2uTfbtr9rDXqcRR0j", + /** + * Limit breadcrumb to specific depth + * 0 = Unlimited + * 1 = Only show current file + * 2 = Show current file and its parent + * 3 = Show current file, its parent and grandparent + * and so on. + * Default: 2 + */ + breadcrumbDepth: 2, + download: { + /** + * Allow user to download protected file without password + * If this set to false, the download link will have temporary token to download the files + * If this set to true, the download link will be permanent + * Default: false + */ + allowProtectedFile: false, + temporaryTokenDuration: 60 * 60, // 1 hour + /** + * If you're using Vercel, the max response size is 4.5MB + * https://vercel.com/docs/platform/limits#serverless-function-payload-size-limit + * If you're using another platform that has different limit, + * change this value to match your platform. + */ + maxFileSize: 4 * 1024 * 1024, + }, + }, - // Max response body size - // If you're using Vercel, the max response size is 4.5MB - // https://vercel.com/docs/platform/limits#serverless-function-payload-size-limit - // If you're using another platform that has different limit, - // change this value to match your platform. - maxResponseSize: 4 * 1024 * 1024, + /** + * https://web.dev/uses-long-cache-ttl/ + */ + cache: "s-maxage=60, stale-while-revalidate", }; diff --git a/src/config/site.config.js b/src/config/site.config.js index 32e20a7..29a94e7 100644 --- a/src/config/site.config.js +++ b/src/config/site.config.js @@ -1,12 +1,18 @@ -import { BsDiscord, BsGithub, BsPaypal } from "react-icons/bs"; +import { + BsDiscord, + BsEnvelopeAt, + BsGithub, + BsPaypal, +} from "react-icons/bs"; -const config = { +const oldConfig = { /* Site MetaData */ // The name of the site siteName: "mbahArip Stash", // The description of the site // Used in meta tags and document head - siteDescription: "Personal Stash of mbahArip, a place to store my files.", + siteDescription: + "Personal Stash of mbahArip, a place to store my files.", // Fav icon of the site // Also used as the logo of the site on the navbar siteIcon: "/favicon.svg", @@ -90,5 +96,59 @@ const config = { pdfProvider: "mozilla", }, }; +const config = { + /* Site MetaData */ + siteName: "mbahArip Stash", + siteDescription: + "Personal Stash of mbahArip, a place to store my files.", + + /* General */ + /** + * Site wide password protection + */ + privateIndex: true, + indexPassword: + "640e3e38dd31aec254f214ba38541a82ddb615e73ce9c6129f33ef549b154ab9", + + navbar: { + /** + * Title beside the logo + * If not set, will be using `siteName` instead + */ + title: "", + links: [ + { + icon: BsGithub, + name: "GitHub", + href: "https://www.github.com/mbaharip", + newTab: true, + }, + { + icon: BsPaypal, + name: "Donate", + href: "https://www.paypal.me/mbaharip", + newTab: true, + }, + { + icon: BsEnvelopeAt, + name: "Email", + href: "mailto:support@mbaharip.com", + }, + { + name: "Main Website", + href: "https://www.mbaharip.com", + newTab: true, + }, + ], + }, + footer: { + /** + * Footer text will be rendered as + * {year} {footerText} - Powered by next-gdrive-index ❤️ + */ + text: "mbahArip Stash", + renderYear: true, + }, +}; export default config; diff --git a/src/context/themeContext.tsx b/src/context/themeContext.tsx index 92ccdcd..71cd06c 100644 --- a/src/context/themeContext.tsx +++ b/src/context/themeContext.tsx @@ -1,4 +1,10 @@ -import React, { createContext, useState, useEffect } from "react"; +"use client"; + +import React, { + createContext, + useState, + useEffect, +} from "react"; export type TTheme = "dark" | "light"; diff --git a/src/pages/api/banner/[folderId]/index.ts b/src/pages/apis/banner/[folderId]/index.ts similarity index 74% rename from src/pages/api/banner/[folderId]/index.ts rename to src/pages/apis/banner/[folderId]/index.ts index 377ec96..ece6f05 100644 --- a/src/pages/api/banner/[folderId]/index.ts +++ b/src/pages/apis/banner/[folderId]/index.ts @@ -1,9 +1,12 @@ import initMiddleware from "utils/apiMiddleware"; import { NextApiRequest, NextApiResponse } from "next"; -import { BannerResponse, ErrorResponse } from "types/googleapis"; +import { + BannerResponse, + ErrorResponse, +} from "types/googleapis"; import { ExtendedError } from "utils/driveHelper"; import driveClient from "utils/driveClient"; -import { urlEncrypt } from "utils/encryptionHelper"; +import { shortEncrypt } from "utils/encryptionHelper"; export default initMiddleware(async function handler( request: NextApiRequest, @@ -14,7 +17,9 @@ export default initMiddleware(async function handler( try { const { folderId } = request.query; - const [name, partialId] = (folderId as string).split(":"); + const [name, partialId] = (folderId as string).split( + ":", + ); if (!name || !partialId || partialId.length !== 8) { throw new ExtendedError( @@ -45,7 +50,7 @@ export default initMiddleware(async function handler( } payload.banner = { - id: urlEncrypt(banner.id as string), + id: shortEncrypt(banner.id as string), name: banner.name as string, }; @@ -57,11 +62,19 @@ export default initMiddleware(async function handler( responseTime: Date.now() - _start, code: error.code || 500, errors: { - message: error.errors?.[0].message || error.message || "Unknown error", - reason: error.errors?.[0].reason || error.cause || "internalError", + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", }, }; - return response.status(payload.code || 500).json(payload); + return response + .status(payload.code || 500) + .json(payload); } }); diff --git a/src/pages/api/banner/index.ts b/src/pages/apis/banner/index.ts similarity index 74% rename from src/pages/api/banner/index.ts rename to src/pages/apis/banner/index.ts index b7faeb7..8ba889a 100644 --- a/src/pages/api/banner/index.ts +++ b/src/pages/apis/banner/index.ts @@ -1,9 +1,12 @@ -import { BannerResponse, ErrorResponse } from "types/googleapis"; +import { + BannerResponse, + ErrorResponse, +} from "types/googleapis"; import { NextApiRequest, NextApiResponse } from "next"; import initMiddleware from "utils/apiMiddleware"; import apiConfig from "config/api.config"; import driveClient from "utils/driveClient"; -import { urlEncrypt } from "utils/encryptionHelper"; +import { shortEncrypt } from "utils/encryptionHelper"; export default initMiddleware(async function handler( request: NextApiRequest, @@ -31,7 +34,7 @@ export default initMiddleware(async function handler( } payload.banner = { - id: urlEncrypt(banner.id as string), + id: shortEncrypt(banner.id as string), name: banner.name as string, }; @@ -43,11 +46,19 @@ export default initMiddleware(async function handler( responseTime: Date.now() - _start, code: error.code || 500, errors: { - message: error.errors?.[0].message || error.message || "Unknown error", - reason: error.errors?.[0].reason || error.cause || "internalError", + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", }, }; - return response.status(payload.code || 500).json(payload); + return response + .status(payload.code || 500) + .json(payload); } }); diff --git a/src/pages/api/encrypt.ts b/src/pages/apis/encrypt.ts similarity index 57% rename from src/pages/api/encrypt.ts rename to src/pages/apis/encrypt.ts index 6558716..fbbaceb 100644 --- a/src/pages/api/encrypt.ts +++ b/src/pages/apis/encrypt.ts @@ -4,8 +4,8 @@ import { ExtendedError } from "utils/driveHelper"; import { decrypt, encrypt, - urlDecrypt, - urlEncrypt, + shortDecrypt, + shortEncrypt, } from "utils/encryptionHelper"; export default function handler( @@ -15,23 +15,36 @@ export default function handler( const _start = Date.now(); try { const { text, isUrl, isDecrypt } = request.query; - if (!text) throw new ExtendedError("Missing text", 400, "missingText"); + if (!text) + throw new ExtendedError( + "Missing text", + 400, + "missingText", + ); let encrypted; let decrypted; if (!isDecrypt) { if (isUrl) { - const encodedText = encodeURIComponent(text as string); - encrypted = urlEncrypt(encodedText); + const encodedText = encodeURIComponent( + text as string, + ); + encrypted = shortEncrypt(encodedText); } else { - const encodedText = encodeURIComponent(text as string); + const encodedText = encodeURIComponent( + text as string, + ); encrypted = encrypt(encodedText); } } else { if (isUrl) { - decrypted = decodeURIComponent(urlDecrypt(text as string)); + decrypted = decodeURIComponent( + shortDecrypt(text as string), + ); } else { - decrypted = decodeURIComponent(decrypt(text as string)); + decrypted = decodeURIComponent( + decrypt(text as string), + ); } } @@ -55,11 +68,19 @@ export default function handler( responseTime: Date.now() - _start, code: error.code || 500, errors: { - message: error.errors?.[0].message || error.message || "Unknown error", - reason: error.errors?.[0].reason || error.cause || "internalError", + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", }, }; - return response.status(payload.code || 500).json(payload); + return response + .status(payload.code || 500) + .json(payload); } } diff --git a/src/pages/apis/exchangeAuthCode.ts b/src/pages/apis/exchangeAuthCode.ts new file mode 100644 index 0000000..9b21848 --- /dev/null +++ b/src/pages/apis/exchangeAuthCode.ts @@ -0,0 +1,58 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { ErrorResponse } from "types/googleapis"; +import { ExtendedError } from "utils/driveHelper"; +import { + decrypt, + encrypt, + shortDecrypt, + shortEncrypt, +} from "utils/encryptionHelper"; +import { OAuth2Client } from "google-auth-library"; +import apiConfig from "config/api.config"; + +export default async function handler( + request: NextApiRequest, + response: NextApiResponse, +) { + const _start = Date.now(); + try { + const { code } = request.body; + if (!code) + throw new ExtendedError( + "Missing code", + 400, + "missingCode", + ); + + const oauth2Client = new OAuth2Client( + "126409166174-l0f9hdblsrmhkt9jeue9m8o93skfs1sr.apps.googleusercontent.com", + "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", + "http://localhost", + ); + + const tokens = await oauth2Client.getToken(code); + + return response.status(200).json(tokens.tokens); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return response + .status(payload.code || 500) + .json(payload); + } +} diff --git a/src/pages/apis/exchangeRefreshCode.ts b/src/pages/apis/exchangeRefreshCode.ts new file mode 100644 index 0000000..655ca0c --- /dev/null +++ b/src/pages/apis/exchangeRefreshCode.ts @@ -0,0 +1,61 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { ErrorResponse } from "types/googleapis"; +import { ExtendedError } from "utils/driveHelper"; +import { + decrypt, + encrypt, + shortDecrypt, + shortEncrypt, +} from "utils/encryptionHelper"; +import { OAuth2Client } from "google-auth-library"; +import apiConfig from "config/api.config"; + +export default async function handler( + request: NextApiRequest, + response: NextApiResponse, +) { + const _start = Date.now(); + try { + const { code } = request.body; + if (!code) + throw new ExtendedError( + "Missing code", + 400, + "missingCode", + ); + + const oauth2Client = new OAuth2Client( + "126409166174-l0f9hdblsrmhkt9jeue9m8o93skfs1sr.apps.googleusercontent.com", + "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", + "http://localhost", + ); + oauth2Client.setCredentials({ + refresh_token: code, + }); + + const tokens = await oauth2Client.getAccessToken(); + + return response.status(200).json(tokens); + } catch (error: any) { + const payload: ErrorResponse = { + success: false, + timestamp: new Date().toISOString(), + responseTime: Date.now() - _start, + code: error.code || 500, + errors: { + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", + }, + }; + + return response + .status(payload.code || 500) + .json(payload); + } +} diff --git a/src/pages/api/files/[id]/getPath.ts b/src/pages/apis/files/[id]/getPath.ts similarity index 100% rename from src/pages/api/files/[id]/getPath.ts rename to src/pages/apis/files/[id]/getPath.ts diff --git a/src/pages/api/files/[id]/index.ts b/src/pages/apis/files/[id]/index.ts similarity index 59% rename from src/pages/api/files/[id]/index.ts rename to src/pages/apis/files/[id]/index.ts index 40e0995..cf2074d 100644 --- a/src/pages/api/files/[id]/index.ts +++ b/src/pages/apis/files/[id]/index.ts @@ -2,12 +2,19 @@ // [filename]:[partialId] import { NextApiRequest, NextApiResponse } from "next"; -import { ErrorResponse, FileResponse, FilesResponse } from "types/googleapis"; -import { ExtendedError, hiddenFiles } from "utils/driveHelper"; +import { + ErrorResponse, + FileResponse, + FilesResponse, +} from "types/googleapis"; +import { + ExtendedError, + hiddenFiles, +} from "utils/driveHelper"; import driveClient from "utils/driveClient"; import apiConfig from "config/api.config"; import initMiddleware from "utils/apiMiddleware"; -import { urlEncrypt } from "utils/encryptionHelper"; +import { shortEncrypt } from "utils/encryptionHelper"; export default initMiddleware(async function handler( request: NextApiRequest, @@ -16,7 +23,8 @@ export default initMiddleware(async function handler( const _start = Date.now(); try { - const { id, pageToken, download, thumbnail, banner } = request.query; + const { id, pageToken, download, thumbnail, banner } = + request.query; const [name, partialId] = (id as string).split(":"); @@ -40,7 +48,11 @@ export default initMiddleware(async function handler( (file.id as string).startsWith(partialId), ); if (!file) { - throw new ExtendedError("File not found.", 404, "notFound"); + throw new ExtendedError( + "File not found.", + 404, + "notFound", + ); } if (file.id === apiConfig.files.rootFolder) { return response.status(301).redirect("/api/files"); @@ -48,14 +60,23 @@ export default initMiddleware(async function handler( response.setHeader("Cache-Control", apiConfig.cache); - if (file.mimeType !== "application/vnd.google-apps.folder") { + if ( + file.mimeType !== "application/vnd.google-apps.folder" + ) { if (thumbnail === "1") { - return response.status(301).redirect(file.thumbnailLink as string); + return response + .status(301) + .redirect(file.thumbnailLink as string); } if (download === "1") { // Check size - if (Number(file.size as string) > apiConfig.maxResponseSize) { - return response.status(301).redirect(file.webContentLink as string); + if ( + Number(file.size as string) > + apiConfig.maxResponseSize + ) { + return response + .status(301) + .redirect(file.webContentLink as string); } const fileStream = await driveClient.files.get( @@ -72,9 +93,14 @@ export default initMiddleware(async function handler( ); response.setHeader( "Content-Disposition", - `attachment; filename=${encodeURIComponent(file.name as string)}`, + `attachment; filename=${encodeURIComponent( + file.name as string, + )}`, + ); + response.setHeader( + "Content-Length", + file.size as string, ); - response.setHeader("Content-Length", file.size as string); return response.status(200).send(fileStream.data); } @@ -85,9 +111,9 @@ export default initMiddleware(async function handler( responseTime: Date.now() - _start, file: { ...file, - id: urlEncrypt(file.id as string), + id: shortEncrypt(file.id as string), webContentLink: file.webContentLink - ? urlEncrypt(file.webContentLink) + ? shortEncrypt(file.webContentLink) : undefined, }, }; @@ -100,29 +126,37 @@ export default initMiddleware(async function handler( "trashed = false", "'me' in owners", ]; - const fetchFolderContents = await driveClient.files.list({ - q: `${query.join(" and ")}`, - fields: - "files(id, name, mimeType, thumbnailLink, fileExtension, createdTime, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, iconLink), nextPageToken", - orderBy: "folder, name asc, createdTime", - pageSize: apiConfig.files.itemsPerPage, - pageToken: (pageToken as string) || undefined, - }); + const fetchFolderContents = + await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: + "files(id, name, mimeType, thumbnailLink, fileExtension, createdTime, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, iconLink), nextPageToken", + orderBy: "folder, name asc, createdTime", + pageSize: apiConfig.files.itemsPerPage, + pageToken: (pageToken as string) || undefined, + }); - const isReadmeExists = !!fetchFolderContents.data.files?.find( - (file) => file.name === ".readme.md", - ); - const isBannerExists = !!fetchFolderContents.data.files?.find((file) => - file.name?.startsWith(".banner"), - ); + const isReadmeExists = + !!fetchFolderContents.data.files?.find( + (file) => file.name === ".readme.md", + ); + const isBannerExists = + !!fetchFolderContents.data.files?.find((file) => + file.name?.startsWith(".banner"), + ); if (banner === "1") { if (!isBannerExists) { - throw new ExtendedError("Banner not found.", 404, "notFound"); + throw new ExtendedError( + "Banner not found.", + 404, + "notFound", + ); } - const bannerFile = fetchFolderContents.data.files?.find((file) => - file.name?.startsWith(".banner"), - ); + const bannerFile = + fetchFolderContents.data.files?.find((file) => + file.name?.startsWith(".banner"), + ); const bannerFileStream = await driveClient.files.get( { fileId: bannerFile?.id as string, @@ -140,19 +174,26 @@ export default initMiddleware(async function handler( bannerFile?.name as string, )}`, ); - response.setHeader("Content-Length", bannerFile?.size as string); - return response.status(200).send(bannerFileStream.data); + response.setHeader( + "Content-Length", + bannerFile?.size as string, + ); + return response + .status(200) + .send(bannerFileStream.data); } // Get only folder, since we order the folder to be first, we can just get all the folder first const folderList = fetchFolderContents.data.files ?.filter( - (item) => item.mimeType === "application/vnd.google-apps.folder", + (item) => + item.mimeType === + "application/vnd.google-apps.folder", ) .map((item) => ({ ...item, - id: urlEncrypt(item.id as string), + id: shortEncrypt(item.id as string), })) || []; // Filter the files, excluding every google apps files and hidden files (.password and .readme.md) // .password currently not used, but will probably be used in the future @@ -160,7 +201,9 @@ export default initMiddleware(async function handler( fetchFolderContents.data.files ?.filter( (item) => - !item.mimeType?.startsWith("application/vnd.google-apps") && + !item.mimeType?.startsWith( + "application/vnd.google-apps", + ) && // !hiddenFiles.includes(item.name as string), !hiddenFiles.some((hiddenFile) => item.name?.startsWith(hiddenFile), @@ -168,9 +211,9 @@ export default initMiddleware(async function handler( ) .map((item) => ({ ...item, - id: urlEncrypt(item.id as string), + id: shortEncrypt(item.id as string), webContentLink: item.webContentLink - ? urlEncrypt(item.webContentLink) + ? shortEncrypt(item.webContentLink) : undefined, })) || []; @@ -181,7 +224,8 @@ export default initMiddleware(async function handler( isReadmeExists: isReadmeExists, folders: folderList, files: fileList, - nextPageToken: fetchFolderContents.data.nextPageToken || undefined, + nextPageToken: + fetchFolderContents.data.nextPageToken || undefined, }; return response.status(200).json(payload); @@ -192,11 +236,19 @@ export default initMiddleware(async function handler( responseTime: Date.now() - _start, code: error.code || 500, errors: { - message: error.errors?.[0].message || error.message || "Unknown error", - reason: error.errors?.[0].reason || error.cause || "internalError", + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", }, }; - return response.status(payload.code || 500).json(payload); + return response + .status(payload.code || 500) + .json(payload); } }); diff --git a/src/pages/api/files/index.ts b/src/pages/apis/files/index.ts similarity index 52% rename from src/pages/api/files/index.ts rename to src/pages/apis/files/index.ts index adff645..a0202ca 100644 --- a/src/pages/api/files/index.ts +++ b/src/pages/apis/files/index.ts @@ -1,10 +1,16 @@ import { NextApiRequest, NextApiResponse } from "next"; import driveClient from "utils/driveClient"; import apiConfig from "config/api.config"; -import { ExtendedError, hiddenFiles } from "utils/driveHelper"; +import { + ExtendedError, + hiddenFiles, +} from "utils/driveHelper"; import initMiddleware from "utils/apiMiddleware"; -import { ErrorResponse, FilesResponse } from "types/googleapis"; -import { urlEncrypt } from "utils/encryptionHelper"; +import { + ErrorResponse, + FilesResponse, +} from "types/googleapis"; +import { shortEncrypt } from "utils/encryptionHelper"; export default initMiddleware(async function handler( request: NextApiRequest, @@ -20,32 +26,41 @@ export default initMiddleware(async function handler( "'me' in owners", `parents = '${apiConfig.files.rootFolder}'`, ]; - const fetchFolderContents = await driveClient.files.list({ - q: `${query.join(" and ")}`, - fields: - "files(id, name, mimeType, thumbnailLink, fileExtension, fullFileExtension, createdTime, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, iconLink), nextPageToken", - orderBy: "folder, name asc, createdTime", - pageSize: apiConfig.files.itemsPerPage, - pageToken: (pageToken as string) || undefined, - }); + const fetchFolderContents = + await driveClient.files.list({ + q: `${query.join(" and ")}`, + fields: + "files(id, name, mimeType, thumbnailLink, fileExtension, fullFileExtension, createdTime, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, iconLink), nextPageToken", + orderBy: "folder, name asc, createdTime", + pageSize: apiConfig.files.itemsPerPage, + pageToken: (pageToken as string) || undefined, + }); - const isReadmeExists = fetchFolderContents.data.files?.find( - (file) => file.name === ".readme.md", - ); - const isBannerExists = fetchFolderContents.data.files?.find((file) => - file.name?.startsWith(".banner"), - ); - const isPasswordExists = fetchFolderContents.data.files?.find((file) => - file.name?.startsWith(".password"), - ); + const isReadmeExists = + fetchFolderContents.data.files?.find( + (file) => file.name === ".readme.md", + ); + const isBannerExists = + fetchFolderContents.data.files?.find((file) => + file.name?.startsWith(".banner"), + ); + const isPasswordExists = + fetchFolderContents.data.files?.find((file) => + file.name?.startsWith(".password"), + ); if (banner === "1") { if (!isBannerExists) { - throw new ExtendedError("Banner not found.", 404, "notFound"); + throw new ExtendedError( + "Banner not found.", + 404, + "notFound", + ); } - const bannerFile = fetchFolderContents.data.files?.find((file) => - file.name?.startsWith(".banner"), - ); + const bannerFile = + fetchFolderContents.data.files?.find((file) => + file.name?.startsWith(".banner"), + ); const bannerFileStream = await driveClient.files.get( { fileId: bannerFile?.id as string, @@ -63,24 +78,33 @@ export default initMiddleware(async function handler( bannerFile?.name as string, )}`, ); - response.setHeader("Content-Length", bannerFile?.size as string); - return response.status(200).send(bannerFileStream.data); + response.setHeader( + "Content-Length", + bannerFile?.size as string, + ); + return response + .status(200) + .send(bannerFileStream.data); } const folderList = fetchFolderContents.data.files ?.filter( - (item) => item.mimeType === "application/vnd.google-apps.folder", + (item) => + item.mimeType === + "application/vnd.google-apps.folder", ) .map((item) => ({ ...item, - id: urlEncrypt(item.id as string), + id: shortEncrypt(item.id as string), })) || []; const fileList = fetchFolderContents.data.files ?.filter( (item) => - !item.mimeType?.startsWith("application/vnd.google-apps") && + !item.mimeType?.startsWith( + "application/vnd.google-apps", + ) && // !hiddenFiles.includes(item.name as string), !hiddenFiles.some((hiddenFile) => item.name?.startsWith(hiddenFile), @@ -88,9 +112,9 @@ export default initMiddleware(async function handler( ) .map((item) => ({ ...item, - id: urlEncrypt(item.id as string), + id: shortEncrypt(item.id as string), webContentLink: item.webContentLink - ? urlEncrypt(item.webContentLink) + ? shortEncrypt(item.webContentLink) : undefined, })) || []; @@ -103,7 +127,8 @@ export default initMiddleware(async function handler( isReadmeExists: !!isReadmeExists, isBannerExists: !!isBannerExists, isPasswordExists: !!isPasswordExists, - nextPageToken: fetchFolderContents.data.nextPageToken || undefined, + nextPageToken: + fetchFolderContents.data.nextPageToken || undefined, }; return response @@ -117,11 +142,19 @@ export default initMiddleware(async function handler( responseTime: Date.now() - _start, code: error.code || 500, errors: { - message: error.errors?.[0].message || error.message || "Unknown error", - reason: error.errors?.[0].reason || error.cause || "internalError", + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", }, }; - return response.status(payload.code || 500).json(payload); + return response + .status(payload.code || 500) + .json(payload); } }); diff --git a/src/pages/api/og-folder.tsx b/src/pages/apis/og-folder.tsx similarity index 90% rename from src/pages/api/og-folder.tsx rename to src/pages/apis/og-folder.tsx index c14734a..15944d0 100644 --- a/src/pages/api/og-folder.tsx +++ b/src/pages/apis/og-folder.tsx @@ -3,27 +3,38 @@ import siteConfig from "config/site.config"; import { NextRequest } from "next/server"; export const config = { - runtime: "edge", + runtime: "experimental-edge", }; const fontBold = fetch( - new URL("../../../public/fonts/Exo2-Bold.ttf", import.meta.url), + new URL( + "../../../public/fonts/Exo2-Bold.ttf", + import.meta.url, + ), ).then((res) => res.arrayBuffer()); const fontRegular = fetch( - new URL("../../../public/fonts/Exo2-Regular.ttf", import.meta.url), + new URL( + "../../../public/fonts/Exo2-Regular.ttf", + import.meta.url, + ), ).then((res) => res.arrayBuffer()); -export default async function handler(request: NextRequest) { +export default async function handler( + request: NextRequest, +) { try { const exoFont = await fontRegular; const exoFontBold = await fontBold; const { searchParams } = new URL(request.url); - const siteName = searchParams.get("siteName") || siteConfig.siteName; + const siteName = + searchParams.get("siteName") || siteConfig.siteName; const fileId = searchParams.get("fileId") || null; const fileName = - searchParams.get("fileName") || fileId?.split(":")[0] || null; + searchParams.get("fileName") || + fileId?.split(":")[0] || + null; if (!fileId) { throw new Error("No file ID"); diff --git a/src/pages/api/og.tsx b/src/pages/apis/og.tsx similarity index 88% rename from src/pages/api/og.tsx rename to src/pages/apis/og.tsx index 90feafa..bf2851f 100644 --- a/src/pages/api/og.tsx +++ b/src/pages/apis/og.tsx @@ -3,32 +3,47 @@ import siteConfig from "config/site.config"; import { NextRequest } from "next/server"; export const config = { - runtime: "edge", + runtime: "experimental-edge", }; const fontBold = fetch( - new URL("../../../public/fonts/Exo2-Bold.ttf", import.meta.url), + new URL( + "../../../public/fonts/Exo2-Bold.ttf", + import.meta.url, + ), ).then((res) => res.arrayBuffer()); const fontRegular = fetch( - new URL("../../../public/fonts/Exo2-Regular.ttf", import.meta.url), + new URL( + "../../../public/fonts/Exo2-Regular.ttf", + import.meta.url, + ), ).then((res) => res.arrayBuffer()); -export default async function handler(request: NextRequest) { +export default async function handler( + request: NextRequest, +) { try { const exoFont = await fontRegular; const exoFontBold = await fontBold; const { searchParams } = new URL(request.url); - const siteName = searchParams.get("siteName") || siteConfig.siteName; + const siteName = + searchParams.get("siteName") || siteConfig.siteName; const fileId = searchParams.get("fileId") || null; const fileName = - searchParams.get("fileName") || fileId?.split(":")[0] || null; + searchParams.get("fileName") || + fileId?.split(":")[0] || + null; const fileExt = fileName?.split(".").pop() || null; - const isImage = ["jpg", "jpeg", "png", "gif", "webp"].includes( - fileExt || "", - ); + const isImage = [ + "jpg", + "jpeg", + "png", + "gif", + "webp", + ].includes(fileExt || ""); if (!fileId) { throw new Error("No file ID"); diff --git a/src/pages/api/readme/[folderId]/index.tsx b/src/pages/apis/readme/[folderId]/index.tsx similarity index 100% rename from src/pages/api/readme/[folderId]/index.tsx rename to src/pages/apis/readme/[folderId]/index.tsx diff --git a/src/pages/api/readme/index.ts b/src/pages/apis/readme/index.ts similarity index 100% rename from src/pages/api/readme/index.ts rename to src/pages/apis/readme/index.ts diff --git a/src/pages/api/search.ts b/src/pages/apis/search.ts similarity index 72% rename from src/pages/api/search.ts rename to src/pages/apis/search.ts index c4ba1f9..c6ccf34 100644 --- a/src/pages/api/search.ts +++ b/src/pages/apis/search.ts @@ -1,8 +1,11 @@ import apiConfig from "config/api.config"; import { NextApiRequest, NextApiResponse } from "next"; -import { ErrorResponse, SearchResponse } from "types/googleapis"; +import { + ErrorResponse, + SearchResponse, +} from "types/googleapis"; import driveClient from "utils/driveClient"; -import { urlEncrypt } from "utils/encryptionHelper"; +import { shortEncrypt } from "utils/encryptionHelper"; import { hiddenFiles } from "utils/driveHelper"; export default async function handler( @@ -43,12 +46,15 @@ export default async function handler( !hiddenFiles.some((hiddenFile) => item.name?.startsWith(hiddenFile), ) && - (!item.mimeType?.startsWith("application/vnd.google-apps") || - item.mimeType === "application/vnd.google-apps.folder"), + (!item.mimeType?.startsWith( + "application/vnd.google-apps", + ) || + item.mimeType === + "application/vnd.google-apps.folder"), ) .map((item) => ({ ...item, - id: urlEncrypt(item.id as string), + id: shortEncrypt(item.id as string), })) || [], }; @@ -63,11 +69,19 @@ export default async function handler( responseTime: Date.now() - _start, code: error.code || 500, errors: { - message: error.errors?.[0].message || error.message || "Unknown error", - reason: error.errors?.[0].reason || error.cause || "internalError", + message: + error.errors?.[0].message || + error.message || + "Unknown error", + reason: + error.errors?.[0].reason || + error.cause || + "internalError", }, }; - return response.status(payload.code || 500).json(payload); + return response + .status(payload.code || 500) + .json(payload); } } diff --git a/src/pages/file/[id].tsx b/src/pages/file/[id].tsx index 553309d..a1617c7 100644 --- a/src/pages/file/[id].tsx +++ b/src/pages/file/[id].tsx @@ -1,13 +1,19 @@ import { GetServerSideProps } from "next"; import axios from "axios"; -import { ErrorResponse, FileResponse } from "types/googleapis"; +import { + ErrorResponse, + FileResponse, +} from "types/googleapis"; import { useEffect, useState } from "react"; import useSWR from "swr"; -import { urlDecrypt } from "utils/encryptionHelper"; +import { shortDecrypt } from "utils/encryptionHelper"; import DefaultLayout from "components/layout/DefaultLayout"; import { NextSeo } from "next-seo"; import SWRLayout from "components/layout/SWRLayout"; -import { getFilePreview, getFileType } from "utils/mimeTypesHelper"; +import { + getFilePreview, + getFileType, +} from "utils/mimeTypesHelper"; import { capitalize, formatBytes, @@ -27,7 +33,8 @@ type Metadata = { }; export default function File({ id, fileName }: Props) { const [data, setData] = useState(); - const [PreviewComponent, setPreviewComponent] = useState(); + const [PreviewComponent, setPreviewComponent] = + useState(); const [metadata, setMetadata] = useState([]); const copyLink = useCopyText(); @@ -41,7 +48,9 @@ export default function File({ id, fileName }: Props) { data: swrData, error, isLoading, - } = useSWR(`/api/files/${id}`); + } = useSWR( + `/api/files/${id}`, + ); useEffect(() => { if (swrData) { @@ -49,8 +58,10 @@ export default function File({ id, fileName }: Props) { ...swrData, file: { ...swrData.file, - id: urlDecrypt(swrData.file.id as string), - webContentLink: urlDecrypt(swrData.file.webContentLink as string), + id: shortDecrypt(swrData.file.id as string), + webContentLink: shortDecrypt( + swrData.file.webContentLink as string, + ), }, }; setData(decryptedData); @@ -90,11 +101,15 @@ export default function File({ id, fileName }: Props) { }, { label: "Created", - value: formatDate(new Date(data.file.createdTime as string)), + value: formatDate( + new Date(data.file.createdTime as string), + ), }, { label: "Modified", - value: formatDate(new Date(data.file.modifiedTime as string)), + value: formatDate( + new Date(data.file.modifiedTime as string), + ), }, ]; if (data.file.imageMediaMetadata) { @@ -107,7 +122,8 @@ export default function File({ id, fileName }: Props) { defaultMetadata.push({ label: "Duration", value: formatDuration( - data.file.videoMediaMetadata.durationMillis as string, + data.file.videoMediaMetadata + .durationMillis as string, ), }); } @@ -121,13 +137,19 @@ export default function File({ id, fileName }: Props) { renderSwitchLayout={false} > ( - {item.label} + {item.label} + + {item.value} @@ -195,14 +225,20 @@ export default function File({ id, fileName }: Props) { - + - Download + + Download + { - const { id } = context.query; +export const getServerSideProps: GetServerSideProps = + async (context) => { + const { id } = context.query; + + const fetchFileMetadata = await axios.get( + `${process.env.NEXT_PUBLIC_DOMAIN}/api/files/${id}`, + ); + if (!fetchFileMetadata.data.success) { + return { + notFound: true, + }; + } - const fetchFileMetadata = await axios.get( - `${process.env.NEXT_PUBLIC_DOMAIN}/api/files/${id}`, - ); - if (!fetchFileMetadata.data.success) { return { - notFound: true, + props: { + id, + fileName: fetchFileMetadata.data.file.name, + }, }; - } - - return { - props: { - id, - fileName: fetchFileMetadata.data.file.name, - }, }; -}; diff --git a/src/pages/media/[fileId].tsx b/src/pages/media/[fileId].tsx index 2b5d86e..4981488 100644 --- a/src/pages/media/[fileId].tsx +++ b/src/pages/media/[fileId].tsx @@ -1,7 +1,7 @@ import { GetServerSideProps } from "next"; import { FileResponse } from "types/googleapis"; import axios from "axios"; -import { urlDecrypt } from "utils/encryptionHelper"; +import { shortDecrypt } from "utils/encryptionHelper"; import { promisify } from "util"; import { pipeline } from "stream"; import driveClient from "utils/driveClient"; @@ -12,58 +12,67 @@ export default function Media() { } const pipelineAsync = promisify(pipeline); -export const getServerSideProps: GetServerSideProps = async (context) => { - const { fileId } = context.query; +export const getServerSideProps: GetServerSideProps = + async (context) => { + const { fileId } = context.query; - const searchFile = await axios.get( - `${process.env.NEXT_PUBLIC_DOMAIN}/api/files/${fileId}`, - ); - if (!searchFile.data.success) { - return { - notFound: true, - }; - } + const searchFile = await axios.get( + `${process.env.NEXT_PUBLIC_DOMAIN}/api/files/${fileId}`, + ); + if (!searchFile.data.success) { + return { + notFound: true, + }; + } - const id = urlDecrypt(searchFile.data.file.id as string); - const fileName = searchFile.data.file.name as string; - const mimeType = searchFile.data.file.mimeType as string; + const id = shortDecrypt( + searchFile.data.file.id as string, + ); + const fileName = searchFile.data.file.name as string; + const mimeType = searchFile.data.file + .mimeType as string; - if ( - !mimeType.startsWith("image/") && - !mimeType.startsWith("video/") && - !mimeType.startsWith("audio/") - ) { - return { - notFound: true, - }; - } + if ( + !mimeType.startsWith("image/") && + !mimeType.startsWith("video/") && + !mimeType.startsWith("audio/") + ) { + return { + notFound: true, + }; + } - if (Number(searchFile.data.file.size as string) > apiConfig.maxResponseSize) { - return { - redirect: { - destination: urlDecrypt(searchFile.data.file.webContentLink as string), - permanent: false, + if ( + Number(searchFile.data.file.size as string) > + apiConfig.maxResponseSize + ) { + return { + redirect: { + destination: shortDecrypt( + searchFile.data.file.webContentLink as string, + ), + permanent: false, + }, + }; + } + + const getImageStream = await driveClient.files.get( + { + fileId: id, + alt: "media", }, + { responseType: "stream" }, + ); + + context.res.setHeader("Content-Type", mimeType); + context.res.setHeader( + "Content-Disposition", + `inline; filename="${fileName}"`, + ); + + await pipelineAsync(getImageStream.data, context.res); + + return { + props: {}, }; - } - - const getImageStream = await driveClient.files.get( - { - fileId: id, - alt: "media", - }, - { responseType: "stream" }, - ); - - context.res.setHeader("Content-Type", mimeType); - context.res.setHeader( - "Content-Disposition", - `inline; filename="${fileName}"`, - ); - - await pipelineAsync(getImageStream.data, context.res); - - return { - props: {}, }; -}; diff --git a/src/pages/_app.tsx b/src/pages/old_app.tsx similarity index 100% rename from src/pages/_app.tsx rename to src/pages/old_app.tsx diff --git a/src/pages/_document.tsx b/src/pages/old_document.tsx similarity index 100% rename from src/pages/_document.tsx rename to src/pages/old_document.tsx diff --git a/src/pages/index.tsx b/src/pages/old_index.tsx similarity index 100% rename from src/pages/index.tsx rename to src/pages/old_index.tsx diff --git a/src/pages/test.tsx b/src/pages/test.tsx deleted file mode 100644 index 297ae4c..0000000 --- a/src/pages/test.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { GetServerSideProps } from "next"; -import driveClient from "utils/driveClient"; -import { promisify } from "util"; -import { pipeline } from "stream"; - -export default function Test() { - return ; -} - -const pipelineAsync = promisify(pipeline); -export const getServerSideProps: GetServerSideProps = async ({ res }) => { - const getImageMetadata = driveClient.files.get({ - fileId: "1uAmdeBGJPAEkyEG_M_gYczjP0eVy6ncH", - fields: "name, mimeType", - }); - const getImageStream = driveClient.files.get( - { - fileId: "1uAmdeBGJPAEkyEG_M_gYczjP0eVy6ncH", - alt: "media", - }, - { responseType: "stream" }, - ); - const [imageMetadata, imageStream] = await Promise.all([ - getImageMetadata, - getImageStream, - ]); - res.setHeader("Content-Type", imageMetadata.data.mimeType as string); - await pipelineAsync(imageStream.data, res); - - return { - props: {}, - }; -}; diff --git a/src/styles/globals.css b/src/styles/globals.css index f422c26..e046a6c 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -2,265 +2,79 @@ @tailwind components; @tailwind utilities; -html, body { - margin: 0; - padding: 0; - box-sizing: border-box; +:root { + --animation-duration: 300ms; + --animation-timing: cubic-bezier(0.4, 0, 0.2, 1); } +html, body { + margin: 0; + padding: 0; + box-sizing: border-box; +} /* Base */ @layer base { - html { - @apply text-sm tablet:text-base; - } - - /* Scrollbar */ - ::-webkit-scrollbar { - @apply w-1.5; - } - html ::-webkit-scrollbar-track { - @apply bg-zinc-100 rounded; - } - html ::-webkit-scrollbar-thumb { - @apply bg-zinc-400 rounded; - } - html ::-webkit-scrollbar-thumb:hover { - @apply bg-zinc-500; - } - - html.dark ::-webkit-scrollbar-track { - @apply bg-zinc-500; - } - html.dark ::-webkit-scrollbar-thumb { - @apply bg-zinc-900; - } - html.dark ::-webkit-scrollbar-thumb:hover { - @apply bg-zinc-800; - } - - main { - @apply flex flex-col items-center justify-start min-h-screen h-full w-full; - @apply bg-zinc-100 dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100; - } - - /* Typography */ - h1 { - @apply text-4xl leading-loose font-bold; - } - h2 { - @apply text-3xl leading-loose font-bold; - } - h3 { - @apply text-2xl leading-loose font-bold; - } - h4 { - @apply text-xl leading-relaxed font-semibold; - } - h5 { - @apply text-lg leading-relaxed font-semibold; - } - h6 { - @apply text-base leading-normal font-semibold; - } - p { - @apply text-base leading-relaxed whitespace-pre-wrap; - } - span { - @apply text-base leading-normal; - } - a { - @apply hover:opacity-75 transition duration-150; - @apply text-zinc-900 dark:text-zinc-100 hover:text-zinc-800 dark:hover:text-zinc-200; - } - a.file { - @apply opacity-90 hover:opacity-100 !important; - } - a.link { - @apply text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-500 underline underline-offset-2; - } - - - input, textarea, button, select { - @apply rounded-lg px-4 py-2 tablet:py-1; - @apply text-sm tablet:text-base; - @apply outline-none border; - @apply border-zinc-400 hover:border-blue-300 focus:border-blue-400; - @apply bg-zinc-200 hover:bg-zinc-300 focus:bg-zinc-50; - @apply dark:border-zinc-600 dark:hover:border-blue-400 dark:focus:border-blue-500; - @apply dark:bg-zinc-800 dark:hover:bg-zinc-700 dark:focus:bg-zinc-950; - @apply disabled:border-zinc-300 dark:disabled:border-zinc-700; - @apply disabled:bg-zinc-400 dark:disabled:bg-zinc-800; - @apply disabled:text-zinc-500; - @apply transition duration-150; - } - - input.error { - @apply border-red-400 hover:border-red-500 focus:border-red-600; - @apply bg-red-500 hover:bg-red-600 focus:bg-red-700 placeholder-zinc-100; - @apply dark:border-red-500 dark:hover:border-red-600 dark:focus:border-red-700 dark:placeholder-zinc-100; - @apply dark:bg-red-500 dark:hover:bg-red-600 dark:focus:bg-red-700; - } - - button { - @apply cursor-pointer disabled:cursor-default; - } - button[disabled] { - @apply cursor-not-allowed !important; - @apply border-zinc-300 dark:border-zinc-700 !important; - @apply bg-zinc-400 dark:bg-zinc-800 !important; - @apply text-zinc-500 !important; - } - button.primary { - @apply text-zinc-100 dark:text-zinc-100; - @apply border-blue-400 hover:border-blue-500 active:border-blue-600; - @apply bg-blue-500 hover:bg-blue-700 active:bg-blue-400; - @apply dark:border-blue-500 dark:hover:border-blue-600 dark:active:border-blue-700; - @apply dark:bg-blue-500 dark:hover:bg-blue-600 dark:active:bg-blue-700; - } - button.secondary{ - @apply text-zinc-100 dark:text-zinc-100; - @apply border-zinc-400 hover:border-zinc-500 active:border-zinc-600; - @apply bg-zinc-500 hover:bg-zinc-700 active:bg-zinc-400; - @apply dark:border-zinc-500 dark:hover:border-zinc-600 dark:active:border-zinc-700; - @apply dark:bg-zinc-500 dark:hover:bg-zinc-600 dark:active:bg-zinc-700; - } - button.danger { - @apply text-zinc-100 dark:text-zinc-100; - @apply border-red-400 hover:border-red-500 active:border-red-600; - @apply bg-red-500 hover:bg-red-700 active:bg-red-400; - @apply dark:border-red-500 dark:hover:border-red-600 dark:active:border-red-700; - @apply dark:bg-red-500 dark:hover:bg-red-600 dark:active:bg-red-700; - } - - table { - @apply w-full border-collapse; - } - th, td { - @apply text-left; + html { + @apply text-sm tablet:text-base; } - th { - @apply border-b border-zinc-400 dark:border-zinc-700 pt-1 pb-2; - } - td { - @apply border-b border-zinc-400 dark:border-zinc-700 py-1.5; + * { + @apply focus:outline focus:outline-blue-400 focus:rounded; + @apply dark:focus:outline-blue-600; } - img.thumbnail { - @apply rounded-lg w-full max-w-screen-tablet mt-2 mb-4; - } + /* Scrollbar */ + ::-webkit-scrollbar { + @apply w-1.5; + } + + html ::-webkit-scrollbar-track { + @apply bg-zinc-100 rounded; + } + + html ::-webkit-scrollbar-thumb { + @apply bg-zinc-400 rounded; + } + + html ::-webkit-scrollbar-thumb:hover { + @apply bg-zinc-500; + } + + html.dark ::-webkit-scrollbar-track { + @apply bg-zinc-500; + } + + html.dark ::-webkit-scrollbar-thumb { + @apply bg-zinc-900; + } + + html.dark ::-webkit-scrollbar-thumb:hover { + @apply bg-zinc-800; + } + + /* Text */ + + a, .interactive { + @apply opacity-90 transition-opacity duration-[var(--animation-duration)] ease-[var(--animation-timing)]; + @apply hover:opacity-100; + } + a[role='url']{ + @apply opacity-100 text-blue-600 transition-colors duration-[var(--animation-duration)] ease-[var(--animation-timing)]; + @apply hover:text-blue-500; + @apply active:text-blue-700; + @apply focus:underline; + @apply dark:text-blue-500; + @apply dark:hover:text-blue-400; + @apply dark:active:text-blue-600; + } } @layer components { - div.card { - @apply rounded-lg p-2 tablet:p-4; - @apply border border-zinc-400 dark:border-zinc-700; - @apply drop-shadow; - @apply bg-zinc-100 dark:bg-zinc-900; - } - - div.banner { - @apply rounded-lg p-2 tablet:p-4 my-2; - @apply border border-zinc-400 dark:border-zinc-700; - @apply drop-shadow; - @apply bg-zinc-100 dark:bg-zinc-900; - @apply flex gap-4 items-center; - } - div.banner.warning { - @apply border-yellow-400 dark:border-yellow-600; - @apply bg-yellow-100 dark:bg-yellow-900; - } - - div.layoutDropdown { - @apply rounded-lg pr-2 pl-4 py-1 cursor-pointer; - @apply flex items-center justify-between gap-8; - @apply text-sm tablet:text-base; - @apply border; - @apply border-zinc-400 hover:border-blue-300 focus:border-blue-400; - @apply bg-zinc-100 hover:bg-zinc-300 focus:bg-zinc-50; - @apply dark:border-zinc-600 dark:hover:border-blue-400 dark:focus:border-blue-500; - @apply dark:bg-zinc-900 dark:hover:bg-zinc-800 dark:focus:bg-zinc-950; - @apply disabled:border-zinc-300 dark:disabled:border-zinc-700; - @apply disabled:bg-zinc-400 dark:disabled:bg-zinc-800; - @apply disabled:text-zinc-500; - @apply transition duration-150; - } - div.layoutOption { - @apply rounded-lg overflow-hidden; - @apply justify-center; - @apply text-sm tablet:text-base; - @apply border; - @apply border-blue-400; - @apply bg-zinc-200; - @apply dark:border-blue-500; - @apply dark:bg-zinc-800; - @apply transition duration-150 ease-in-out; - } - div.layoutItem { - @apply flex items-center justify-start gap-2 pr-2 pl-4 py-1 h-full w-full cursor-pointer; - @apply bg-zinc-100 hover:bg-blue-300/25 focus:bg-zinc-50; - @apply dark:bg-zinc-900 dark:hover:bg-blue-400/25 dark:focus:bg-zinc-950; - } - div.layoutItem[data-active="true"] { - @apply bg-blue-300 cursor-default; - @apply dark:bg-blue-400; - } - - div.items { - @apply rounded-xl p-1 duration-75; - @apply bg-zinc-300/50 hover:bg-zinc-300; - @apply dark:bg-zinc-800/50 dark:hover:bg-zinc-800; - @apply text-zinc-900/80 hover:text-zinc-950; - @apply dark:text-zinc-100/80 dark:hover:text-zinc-50; - @apply transition duration-200; - } - - div.loading > svg { - @apply fill-zinc-900 dark:fill-zinc-100; - } - - div.preview-audio > div.rhap_container { - @apply rounded-lg; - @apply bg-zinc-50 dark:bg-zinc-800; - @apply text-zinc-900 dark:text-zinc-100; - } - div.preview-audio div.rhap_time { - @apply text-zinc-900 dark:text-zinc-100; - } - div.preview-audio button { - @apply bg-transparent hover:bg-transparent active:bg-transparent focus:bg-transparent; - } } /* Utility */ @layer utilities { - .react-icons { - } - - .fillCard { - @apply min-h-[25vh] - } - - .navbar a { - @apply text-inherit font-normal not-italic no-underline; - } - - .interactive { - @apply hover:opacity-75 transition duration-150; - } - - .center { - @apply top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2; - } - - div.divider-horizontal { - @apply w-full h-px my-2 bg-zinc-400 dark:bg-zinc-500 tablet:my-2; - } - div.divider-vertical { - @apply h-full w-px mx-2 bg-zinc-400 dark:bg-zinc-500 tablet:mx-2; - } - - .grid-auto-fit { - grid-template-columns: repeat(auto-fit, minmax(16px, 1fr)); - } + .global-duration { + @apply duration-[var(--animation-duration)] ease-[var(--animation-timing)]; + } } \ No newline at end of file diff --git a/src/types/api/index.ts b/src/types/api/index.ts new file mode 100644 index 0000000..851bbd3 --- /dev/null +++ b/src/types/api/index.ts @@ -0,0 +1,16 @@ +export interface API_Response { + success: boolean; + timestamp: string; + responseTime?: number; + data?: T; +} + +export interface API_Error { + success: boolean; + timestamp: string; + responseTime?: number; + code: number; + message: string; + category: string; + reason: string; +} diff --git a/src/types/default.ts b/src/types/default.ts index 745b609..02503d1 100644 --- a/src/types/default.ts +++ b/src/types/default.ts @@ -1,3 +1,8 @@ export interface ExtendedError extends Error { code?: number; } + +export interface DownloadToken { + password: string; + exp: Date | string | number; +} diff --git a/src/types/googleapis.ts b/src/types/googleapis.ts index 99030f7..436dd07 100644 --- a/src/types/googleapis.ts +++ b/src/types/googleapis.ts @@ -71,7 +71,16 @@ export type TFile = { "text/plain": string; }; }; +export type TPath = { + name: string; + id: string; + mimeType: string; +}; +export interface ValidatePathResponse extends APIResponse { + data: TPath[]; + password?: string; +} export interface FilesResponse extends APIResponse { passwordRequired?: boolean; passwordValidated?: boolean; diff --git a/src/utils/axiosHelper.ts b/src/utils/axiosHelper.ts new file mode 100644 index 0000000..16f335a --- /dev/null +++ b/src/utils/axiosHelper.ts @@ -0,0 +1,44 @@ +import axios, { AxiosError } from "axios"; +import apiConfig from "config/api.config"; + +const fetch = axios.create({ + baseURL: apiConfig.basePath, + maxRate: 5, + timeout: 10000, +}); + +fetch.interceptors.response.use( + (response) => response, + (error: AxiosError) => { + if (error.response) { + const payload = new ExtendedError( + error.message, + error.response.data.code, + error.response.data.category, + error.response.data.reason, + ); + // Terjadi ketika request berhasil dikirimkan, namun server memberikan response dengan status code di luar range 2xx. + return Promise.reject(JSON.stringify(payload)); + } else if (error.request) { + const payload = new ExtendedError( + Constant["noResponse"], + 500, + "noResponse", + error.message, + ); + // Terjadi ketika request dikirimkan namun tidak menerima response dari server. + return Promise.reject(JSON.stringify(payload)); + } else { + const payload = new ExtendedError( + Constant["badRequest"], + 400, + "badRequest", + error.message, + ); + // Terjadi ketika terjadi kesalahan saat melakukan request. + return Promise.reject(JSON.stringify(payload)); + } + }, +); + +export default fetch; diff --git a/src/utils/driveClient.ts b/src/utils/driveClient.ts index f73e33f..940b8df 100644 --- a/src/utils/driveClient.ts +++ b/src/utils/driveClient.ts @@ -2,21 +2,36 @@ import apiConfig from "config/api.config"; import { drive_v3, google } from "googleapis"; import { decrypt } from "utils/encryptionHelper"; -const decryptedSecret: string = decrypt( - apiConfig.client_secret, - process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string, -); -const decryptedRefreshToken: string = decrypt( - apiConfig.refresh_token, - process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string, -); +// const decryptedSecret: string = decrypt( +// apiConfig.client_secret, +// process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string, +// ); +// const decryptedRefreshToken: string = decrypt( +// apiConfig.refresh_token, +// process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string, +// ); + +const config = { + client_id: + process.env.NODE_ENV === "development" + ? apiConfig.dev_client_id + : apiConfig.client_id, + client_secret: + process.env.NODE_ENV === "development" + ? process.env.DEV_DRIVE_CLIENT_SECRET + : process.env.DRIVE_CLIENT_SECRET, + refresh_token: + process.env.NODE_ENV === "development" + ? process.env.DEV_DRIVE_REFRESH_TOKEN + : process.env.DRIVE_REFRESH_TOKEN, +}; const oauth2Client = new google.auth.OAuth2( - apiConfig.client_id, - process.env.DRIVE_CLIENT_SECRET as string, + config.client_id, + config.client_secret as string, ); oauth2Client.setCredentials({ - refresh_token: process.env.DRIVE_REFRESH_TOKEN as string, + refresh_token: config.refresh_token as string, }); let gdriveInstance; diff --git a/src/utils/driveHelper.ts b/src/utils/driveHelper.ts index 2589d3a..eb78905 100644 --- a/src/utils/driveHelper.ts +++ b/src/utils/driveHelper.ts @@ -1,7 +1,11 @@ import { drive_v3 } from "googleapis"; -import { urlDecrypt } from "utils/encryptionHelper"; +import { shortDecrypt } from "utils/encryptionHelper"; -export const hiddenFiles = [".password", ".readme.md", ".banner"]; +export const hiddenFiles = [ + ".password", + ".readme.md", + ".banner", +]; export function createFileId( data: drive_v3.Schema$File, encrypted: boolean = false, @@ -9,17 +13,23 @@ export function createFileId( if (process.env.ENCRYPTION_KEY) { } if (encrypted) { - return `${encodeURIComponent(data.name as string)}:${urlDecrypt( - data.id as string, - )?.slice(0, 8)}`; + return `${encodeURIComponent( + data.name as string, + )}:${shortDecrypt(data.id as string)?.slice(0, 8)}`; } - return `${encodeURIComponent(data.name as string)}:${data.id?.slice(0, 8)}`; + return `${encodeURIComponent( + data.name as string, + )}:${data.id?.slice(0, 8)}`; } export class ExtendedError extends Error { code?: number; - constructor(message?: string, code?: number, reason?: string) { + constructor( + message?: string, + code?: number, + reason?: string, + ) { super(message); this.code = code; this.cause = reason; diff --git a/src/utils/encryptionHelper.ts b/src/utils/encryptionHelper.ts index dbed390..33470ab 100644 --- a/src/utils/encryptionHelper.ts +++ b/src/utils/encryptionHelper.ts @@ -11,7 +11,9 @@ export function generateRandomEncryptionKey(): Promise { }); } -export function createEncryptionKey(passphrase: string): Promise { +export function createEncryptionKey( + passphrase: string, +): Promise { return new Promise((resolve, reject) => { const salt = crypto.randomBytes(16); // generate a random salt const iterations = 100000; // number of PBKDF2 iterations @@ -34,7 +36,8 @@ export function createEncryptionKey(passphrase: string): Promise { export function encrypt( data: string, - encryptionKey: string = process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string, + encryptionKey: string = process.env + .NEXT_PUBLIC_ENCRYPTION_KEY as string, ) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv( @@ -44,14 +47,19 @@ export function encrypt( ); let encrypted = cipher.update(data); encrypted = Buffer.concat([encrypted, cipher.final()]); - return iv.toString("hex") + ":" + encrypted.toString("hex"); + return ( + iv.toString("hex") + ":" + encrypted.toString("hex") + ); } export function decrypt( encryptedData: string, - encryptionKey: string = process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string, + encryptionKey: string = process.env + .NEXT_PUBLIC_ENCRYPTION_KEY as string, ) { - const [ivString, encryptedString] = encryptedData.split(":"); + if (!encryptedData) return ""; + const [ivString, encryptedString] = + encryptedData.split(":"); const iv = Buffer.from(ivString, "hex"); const encrypted = Buffer.from(encryptedString, "hex"); const decipher = crypto.createDecipheriv( @@ -65,19 +73,35 @@ export function decrypt( } const urlKey = - (process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string).slice(0, 16) || ""; + (process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string).slice( + 0, + 16, + ) || ""; const urlIV = Buffer.from(urlKey); -export function urlEncrypt(fileId: string): string { - const cipher = crypto.createCipheriv("aes-128-cbc", urlKey, urlIV); +export function shortEncrypt(fileId: string): string { + const cipher = crypto.createCipheriv( + "aes-128-cbc", + urlKey, + urlIV, + ); let cipherText = cipher.update(fileId, "utf8", "hex"); cipherText += cipher.final("hex"); return cipherText; } -export function urlDecrypt(cipherText: string): string { - const decipher = crypto.createDecipheriv("aes-128-cbc", urlKey, urlIV); - let plainText = decipher.update(cipherText, "hex", "utf8"); +export function shortDecrypt(cipherText: string): string { + if (!cipherText) return ""; + const decipher = crypto.createDecipheriv( + "aes-128-cbc", + urlKey, + urlIV, + ); + let plainText = decipher.update( + cipherText, + "hex", + "utf8", + ); plainText += decipher.final("utf8"); return plainText; } diff --git a/src/utils/encryptionHelper/createEncryptionKey.ts b/src/utils/encryptionHelper/createEncryptionKey.ts new file mode 100644 index 0000000..34855c8 --- /dev/null +++ b/src/utils/encryptionHelper/createEncryptionKey.ts @@ -0,0 +1,26 @@ +import crypto from "crypto"; + +function createEncryptionKey( + password: string, +): Promise { + return new Promise((resolve, reject) => { + const salt = crypto.randomBytes(16); // generate a random salt + const iterations = 100000; // number of PBKDF2 iterations + const keyLength = 32; // desired key length in bytes + + crypto.pbkdf2( + password, + salt, + iterations, + keyLength, + "sha256", + (err, derivedKey) => { + if (err) throw reject(err); + const encryptionKey = derivedKey.toString("hex"); + resolve(encryptionKey); + }, + ); + }); +} + +export default createEncryptionKey; diff --git a/src/utils/encryptionHelper/generateRandomEncryptionKey.ts b/src/utils/encryptionHelper/generateRandomEncryptionKey.ts new file mode 100644 index 0000000..e797751 --- /dev/null +++ b/src/utils/encryptionHelper/generateRandomEncryptionKey.ts @@ -0,0 +1,14 @@ +import crypto from "crypto"; + +function generateRandomEncryptionKey(): Promise { + return new Promise((resolve, reject) => { + crypto.randomBytes(32, (err, buffer) => { + if (err) { + reject(err); + } + resolve(buffer.toString("hex")); + }); + }); +} + +export default generateRandomEncryptionKey; diff --git a/src/utils/encryptionHelper/longEncryption.ts b/src/utils/encryptionHelper/longEncryption.ts new file mode 100644 index 0000000..a799a41 --- /dev/null +++ b/src/utils/encryptionHelper/longEncryption.ts @@ -0,0 +1,52 @@ +import crypto from "crypto"; + +function encrypt( + data: string, + key: string = process.env + .NEXT_PUBLIC_ENCRYPTION_KEY as string, +): string { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv( + "aes-256-cbc", + Buffer.from(key, "hex"), + iv, + ); + const encrypted = Buffer.concat([ + cipher.update(data), + cipher.final(), + ]); + + return ( + iv.toString("hex") + ":" + encrypted.toString("hex") + ); +} + +function decrypt( + encryptedData: string, + key: string = process.env + .NEXT_PUBLIC_ENCRYPTION_KEY as string, +): string { + if (!encryptedData) return ""; + + const [ivString, encryptedString] = + encryptedData.split(":"); + const iv = Buffer.from(ivString, "hex"); + const encrypted = Buffer.from(encryptedString, "hex"); + const decipher = crypto.createDecipheriv( + "aes-256-cbc", + Buffer.from(key, "hex"), + iv, + ); + const decrypted = Buffer.concat([ + decipher.update(encrypted), + decipher.final(), + ]); + + return decrypted.toString(); +} + +const longEncryption = { + encrypt, + decrypt, +}; +export default longEncryption; diff --git a/src/utils/encryptionHelper/shortEncryption.ts b/src/utils/encryptionHelper/shortEncryption.ts new file mode 100644 index 0000000..19645bb --- /dev/null +++ b/src/utils/encryptionHelper/shortEncryption.ts @@ -0,0 +1,44 @@ +import crypto from "crypto"; + +const key = + (process.env.NEXT_PUBLIC_ENCRYPTION_KEY as string).slice( + 0, + 16, + ) || ""; +const iv = Buffer.from(key); + +function encrypt(data: string): string { + const cipher = crypto.createCipheriv( + "aes-128-cbc", + key, + iv, + ); + const encrypted = Buffer.concat([ + cipher.update(data, "utf-8"), + cipher.final(), + ]); + return encrypted.toString("hex"); +} + +function decrypt(encryptedData: string): string { + if (!encryptedData) return ""; + + const decipher = crypto.createDecipheriv( + "aes-128-cbc", + key, + iv, + ); + const decrypted = Buffer.concat([ + decipher.update(encryptedData, "hex"), + decipher.final(), + ]); + + return decrypted.toString(); +} + +const shortEncryption = { + encrypt, + decrypt, +}; + +export default shortEncryption; diff --git a/src/utils/generalHelper/extendedError.ts b/src/utils/generalHelper/extendedError.ts new file mode 100644 index 0000000..1f2b4e0 --- /dev/null +++ b/src/utils/generalHelper/extendedError.ts @@ -0,0 +1,21 @@ +class ExtendedError extends Error { + public extendedMessage?: string; + public code?: number; + public category?: string; + public reason?: string; + + constructor( + msg: string, + code: number, + category: string, + reason: string, + ) { + super(msg); + this.extendedMessage = msg; + this.code = code; + this.category = category; + this.reason = reason; + } +} + +export default ExtendedError; diff --git a/src/utils/getSearchParams.ts b/src/utils/getSearchParams.ts new file mode 100644 index 0000000..96c46cf --- /dev/null +++ b/src/utils/getSearchParams.ts @@ -0,0 +1,23 @@ +/** + * Get search params from url string, and return it as object + * @param url - Request url + * @param key - Key to get from the url + * @returns Object containing the key and value + */ +function getSearchParams( + url: string, + key: string | string[], +): Record { + const { searchParams } = new URL(url); + if (typeof key === "string") { + return { [key]: searchParams.get(key) }; + } else { + const result: Record = {}; + key.forEach((k) => { + result[k] = searchParams.get(k); + }); + return result; + } +} + +export default getSearchParams; diff --git a/tailwind.config.js b/tailwind.config.js index 46107c3..fbfcee8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -10,6 +10,18 @@ module.exports = { darkMode: "class", theme: { extend: { + minHeight: { + dynamic: "100vh", + }, + minWidth: { + dynamic: "100vw", + }, + height: { + dynamic: "100dvh", + }, + width: { + dynamic: "100dvw", + }, fontFamily: { body: ["var(--font-exo2)", ...defaultTheme.fontFamily.sans], docs: ["var(--font-source-sans-pro", ...defaultTheme.fontFamily.sans], diff --git a/tsconfig.json b/tsconfig.json index f9525c6..85c9071 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,12 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext", "es2019"], + "lib": [ + "dom", + "dom.iterable", + "esnext", + "es2019" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -16,16 +21,44 @@ "incremental": true, "baseUrl": "./src", "paths": { -// "@/*": ["./src/*"], - "components/*": ["components/*"], - "config/*": ["config/*"], - "hooks/*": ["hooks/*"], - "styles/*": ["styles/*"], - "utils/*": ["utils/*"], - "types/*": ["types/*"], - "context/*": ["context/*"], - } + // "@/*": ["./src/*"], + "components/*": [ + "components/*" + ], + "config/*": [ + "config/*" + ], + "hooks/*": [ + "hooks/*" + ], + "styles/*": [ + "styles/*" + ], + "utils/*": [ + "utils/*" + ], + "types/*": [ + "types/*" + ], + "context/*": [ + "context/*" + ] + }, + "plugins": [ + { + "name": "next" + } + ] }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], - "exclude": ["node_modules", ".next"] -} \ No newline at end of file + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.js", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules", + ".next" + ] +} diff --git a/yarn.lock b/yarn.lock index aaf561c..0b234ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,9 +3,9 @@ "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.8", "@babel/runtime@^7.20.7": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" - integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" + integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== dependencies: regenerator-runtime "^0.13.11" @@ -81,18 +81,18 @@ eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.4.0": - version "4.5.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" - integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== "@eslint/eslintrc@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" - integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331" + integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.1" + espree "^9.5.2" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -171,62 +171,62 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba" - integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ== +"@next/env@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.3.tgz#cb00bdd43a0619a79a52c9336df8a0aa84f8f4bf" + integrity sha512-pa1ErjyFensznttAk3EIv77vFbfSYT6cLzVRK5jx4uiRuCQo+m2wCFAREaHKIy63dlgvOyMlzh6R8Inu8H3KrQ== -"@next/eslint-plugin-next@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.3.0.tgz#3a4742b0817575cc0dd4d152cb10363584c215ac" - integrity sha512-wuGN5qSEjSgcq9fVkH0Y/qIPFjnZtW3ZPwfjJOn7l/rrf6y8J24h/lo61kwqunTyzZJm/ETGfGVU9PUs8cnzEA== +"@next/eslint-plugin-next@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.3.tgz#9f3b9dedc8da57436e45d736f5fc6646e93a2656" + integrity sha512-5B0uOnh7wyUY9vNNdIA6NUvWozhrZaTMZOzdirYAefqD0ZBK5C/h3+KMYdCKrR7JrXGvVpWnHtv54b3dCzwICA== dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2" - integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w== +"@next/swc-darwin-arm64@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.3.tgz#2d6c99dd5afbcce37e4ba0f64196317a1259034d" + integrity sha512-yx18udH/ZmR4Bw4M6lIIPE3JxsAZwo04iaucEfA2GMt1unXr2iodHUX/LAKNyi6xoLP2ghi0E+Xi1f4Qb8f1LQ== -"@next/swc-darwin-x64@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb" - integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg== +"@next/swc-darwin-x64@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.3.tgz#162b15fb8a54d9f64e69c898ebeb55b7dac9bddd" + integrity sha512-Mi8xJWh2IOjryAM1mx18vwmal9eokJ2njY4nDh04scy37F0LEGJ/diL6JL6kTXi0UfUCGbMsOItf7vpReNiD2A== -"@next/swc-linux-arm64-gnu@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5" - integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw== +"@next/swc-linux-arm64-gnu@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.3.tgz#aee57422f11183d6a2e4a2e8aa23b9285873e18f" + integrity sha512-aBvtry4bxJ1xwKZ/LVPeBGBwWVwxa4bTnNkRRw6YffJnn/f4Tv4EGDPaVeYHZGQVA56wsGbtA6nZMuWs/EIk4Q== -"@next/swc-linux-arm64-musl@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2" - integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ== +"@next/swc-linux-arm64-musl@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.3.tgz#c10b6aaaa47b341c6c9ea15f8b0ddb37e255d035" + integrity sha512-krT+2G3kEsEUvZoYte3/2IscscDraYPc2B+fDJFipPktJmrv088Pei/RjrhWm5TMIy5URYjZUoDZdh5k940Dyw== -"@next/swc-linux-x64-gnu@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086" - integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA== +"@next/swc-linux-x64-gnu@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.3.tgz#3f85bc5591c6a0d4908404f7e88e3c04f4462039" + integrity sha512-AMdFX6EKJjC0G/CM6hJvkY8wUjCcbdj3Qg7uAQJ7PVejRWaVt0sDTMavbRfgMchx8h8KsAudUCtdFkG9hlEClw== -"@next/swc-linux-x64-musl@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b" - integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw== +"@next/swc-linux-x64-musl@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.3.tgz#f4535adc2374a86bc8e43af149b551567df065de" + integrity sha512-jySgSXE48shaLtcQbiFO9ajE9mqz7pcAVLnVLvRIlUHyQYR/WyZdK8ehLs65Mz6j9cLrJM+YdmdJPyV4WDaz2g== -"@next/swc-win32-arm64-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093" - integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA== +"@next/swc-win32-arm64-msvc@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.3.tgz#e76106d85391c308c5ed70cda2bca2c582d65536" + integrity sha512-5DxHo8uYcaADiE9pHrg8o28VMt/1kR8voDehmfs9AqS0qSClxAAl+CchjdboUvbCjdNWL1MISCvEfKY2InJ3JA== -"@next/swc-win32-ia32-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd" - integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w== +"@next/swc-win32-ia32-msvc@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.3.tgz#8eb5d9dd71ed7a971671291605ad64ad522fb3bc" + integrity sha512-LaqkF3d+GXRA5X6zrUjQUrXm2MN/3E2arXBtn5C7avBCNYfm9G3Xc646AmmmpN3DJZVaMYliMyCIQCMDEzk80w== -"@next/swc-win32-x64-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770" - integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ== +"@next/swc-win32-x64-msvc@13.4.3": + version "13.4.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.3.tgz#c7b2b1b9e158fd7749f8209e68ee8e43a997eb4c" + integrity sha512-jglUk/x7ZWeOJWlVoKyIAkHLTI+qEkOriOOV+3hr1GyiywzcqfI7TpFSiwC7kk1scOiH7NTFKp8mA3XPNO9bDw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -250,16 +250,16 @@ fastq "^1.6.0" "@pkgr/utils@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" - integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.0.tgz#b6373d2504aedaf2fc7cdf2d13ab1f48fa5f12d5" + integrity sha512-2OCURAmRtdlL8iUDTypMrrxfwe8frXTeXaxGsVOaYtc/wrUyk8Z/0OBetM7cdlsy7ZFWlMX72VogKeh+A4Xcjw== dependencies: cross-spawn "^7.0.3" + fast-glob "^3.2.12" is-glob "^4.0.3" - open "^8.4.0" + open "^9.1.0" picocolors "^1.0.0" - tiny-glob "^0.2.9" - tslib "^2.4.0" + tslib "^2.5.0" "@react-spring/animated@~9.6.1": version "9.6.1" @@ -308,9 +308,9 @@ integrity sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q== "@react-three/drei@^9.66.1": - version "9.66.1" - resolved "https://registry.yarnpkg.com/@react-three/drei/-/drei-9.66.1.tgz#b5684d267949ea83ff84e9db01ea41b842d70f9b" - integrity sha512-nbSMsdoL52bLZtOZFMjkjjGzLLkx8hOuYEgvxX19TxR9+YpFnjbfzC3NtsjK719O4OJdNEHZOxmmxoANJJAXEA== + version "9.68.3" + resolved "https://registry.yarnpkg.com/@react-three/drei/-/drei-9.68.3.tgz#93ceea62b44202153ac93db7f98ff37943b0255e" + integrity sha512-u+70dPPiM1rfJ/Wd4lbbyaC0QbQwvQd1NuMfIE78UzvMtOI8LjVLJNmdvzhAfcEL+RuqQlboSgd2/NjcLv9aDQ== dependencies: "@babel/runtime" "^7.11.2" "@react-spring/three" "~9.6.1" @@ -365,10 +365,10 @@ fflate "^0.7.3" string.prototype.codepointat "^0.2.1" -"@swc/helpers@0.4.14": - version "0.4.14" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" - integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== +"@swc/helpers@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a" + integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg== dependencies: tslib "^2.4.0" @@ -414,16 +414,16 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/jsonwebtoken@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#29b1369c4774200d6d6f63135bf3d1ba3ef997a4" - integrity sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw== + version "9.0.2" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#9eeb56c76dd555039be2a3972218de5bd3b8d83e" + integrity sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q== dependencies: "@types/node" "*" -"@types/katex@^0.11.0": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.11.1.tgz#34de04477dcf79e2ef6c8d23b41a3d81f9ebeaf5" - integrity sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg== +"@types/katex@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.14.0.tgz#b84c0afc3218069a5ad64fe2a95321881021b5fe" + integrity sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA== "@types/katex@^0.16.0": version "0.16.0" @@ -448,9 +448,9 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node@*": - version "18.16.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.0.tgz#4668bc392bb6938637b47e98b1f2ed5426f33316" - integrity sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ== + version "20.2.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.1.tgz#de559d4b33be9a808fd43372ccee822c70f39704" + integrity sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg== "@types/node@18.15.11": version "18.15.11" @@ -458,9 +458,9 @@ integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== "@types/node@^14.14.31": - version "14.18.42" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.42.tgz#fa39b2dc8e0eba61bdf51c66502f84e23b66e114" - integrity sha512-xefu+RBie4xWlK8hwAzGh3npDz/4VhF6icY/shU+zv/1fNn+ZVG7T7CRwe9LId9sAYRPxI+59QBPuKL3WpyGRg== + version "14.18.47" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.47.tgz#89a56b05804d136cb99bf2f823bb00814a889aae" + integrity sha512-OuJi8bIng4wYHHA3YpKauL58dZrPxro3d0tabPHyiNF8rKfGKuVfr83oFlPLmKri1cX+Z3cJP39GXmnqkP11Gw== "@types/nprogress@^0.2.0": version "0.2.0" @@ -508,7 +508,16 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@18.0.35": +"@types/react@*": + version "18.2.6" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571" + integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@18.0.35": version "18.0.35" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.35.tgz#192061cb1044fe01f2d3a94272cd35dd50502741" integrity sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag== @@ -553,9 +562,9 @@ integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== "@types/webxr@*": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.1.tgz#4908349419104bd476a4252d04e4c3abb496748d" - integrity sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ== + version "0.5.2" + resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.2.tgz#5d9627b0ffe223aa3b166de7112ac8a9460dc54f" + integrity sha512-szL74BnIcok9m7QwYtVmQ+EdIKwbjPANudfuvDrAF8Cljg9MKUlIoc1w5tjj9PMpeSH3U1Xnx//czQybJ0EfSw== "@types/yauzl@^2.9.1": version "2.10.0" @@ -565,53 +574,53 @@ "@types/node" "*" "@typescript-eslint/parser@^5.42.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.58.0.tgz#2ac4464cf48bef2e3234cb178ede5af352dddbc6" - integrity sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ== + version "5.59.6" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.6.tgz#bd36f71f5a529f828e20b627078d3ed6738dbb40" + integrity sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA== dependencies: - "@typescript-eslint/scope-manager" "5.58.0" - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/typescript-estree" "5.58.0" + "@typescript-eslint/scope-manager" "5.59.6" + "@typescript-eslint/types" "5.59.6" + "@typescript-eslint/typescript-estree" "5.59.6" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz#5e023a48352afc6a87be6ce3c8e763bc9e2f0bc8" - integrity sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA== +"@typescript-eslint/scope-manager@5.59.6": + version "5.59.6" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.6.tgz#d43a3687aa4433868527cfe797eb267c6be35f19" + integrity sha512-gLbY3Le9Dxcb8KdpF0+SJr6EQ+hFGYFl6tVY8VxLPFDfUZC7BHFw+Vq7bM5lE9DwWPfx4vMWWTLGXgpc0mAYyQ== dependencies: - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/visitor-keys" "5.58.0" + "@typescript-eslint/types" "5.59.6" + "@typescript-eslint/visitor-keys" "5.59.6" -"@typescript-eslint/types@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.58.0.tgz#54c490b8522c18986004df7674c644ffe2ed77d8" - integrity sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g== +"@typescript-eslint/types@5.59.6": + version "5.59.6" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.6.tgz#5a6557a772af044afe890d77c6a07e8c23c2460b" + integrity sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA== -"@typescript-eslint/typescript-estree@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz#4966e6ff57eaf6e0fce2586497edc097e2ab3e61" - integrity sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q== +"@typescript-eslint/typescript-estree@5.59.6": + version "5.59.6" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.6.tgz#2fb80522687bd3825504925ea7e1b8de7bb6251b" + integrity sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA== dependencies: - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/visitor-keys" "5.58.0" + "@typescript-eslint/types" "5.59.6" + "@typescript-eslint/visitor-keys" "5.59.6" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz#eb9de3a61d2331829e6761ce7fd13061781168b4" - integrity sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA== +"@typescript-eslint/visitor-keys@5.59.6": + version "5.59.6" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.6.tgz#673fccabf28943847d0c8e9e8d008e3ada7be6bb" + integrity sha512-zEfbFLzB9ETcEJ4HZEEsCR9HHeNku5/Qw1jSS5McYJv5BR+ftYXwFFAH5Al+xkGaZEqowMwl7uoJjQb1YSPF8Q== dependencies: - "@typescript-eslint/types" "5.58.0" + "@typescript-eslint/types" "5.59.6" eslint-visitor-keys "^3.3.0" "@upstash/redis@^1.20.4": - version "1.20.4" - resolved "https://registry.yarnpkg.com/@upstash/redis/-/redis-1.20.4.tgz#6c02665bc0e9b7c56f37342003fc58139c994d22" - integrity sha512-U7j7py+yPvafB5KS7o+F19j2CWzZCwmQ4Tvs+n2lpCWuw/8CeFtWrFWPtQa5dgrVu6tu+Ki9DmhDiAbgMS5fGA== + version "1.20.6" + resolved "https://registry.yarnpkg.com/@upstash/redis/-/redis-1.20.6.tgz#3d5773f2b463b19f612b6373fe516e6200a1762d" + integrity sha512-q1izaYEUsq/WiXNOjf4oOjFZe8fIeBSZN8d5cEyOD4nem+zxc4jccieorQQrNlEahKPE1ZYLzVEkMODRUfch2g== dependencies: isomorphic-fetch "^3.0.0" @@ -860,14 +869,14 @@ aws4@^1.8.0: integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== axe-core@^4.6.2: - version "4.6.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" - integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== + version "4.7.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.1.tgz#04392c9ccb3d7d7c5d2f8684f148d56d3442f33d" + integrity sha512-sCXXUhA+cljomZ3ZAwb8i1p3oOlkABzPy08ZDAoGcYuvtBPlQ1Ytde129ArXyHWDhfeewq7rlx9F+cUx2SSlkg== axios@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542" - integrity sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -914,6 +923,11 @@ bidi-js@^1.0.2: dependencies: require-from-string "^2.0.2" +big-integer@^1.6.44: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" @@ -934,6 +948,13 @@ bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -977,6 +998,13 @@ buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + busboy@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -1013,14 +1041,14 @@ camelize@^1.0.0: integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== camera-controls@^2.3.1: - version "2.3.4" - resolved "https://registry.yarnpkg.com/camera-controls/-/camera-controls-2.3.4.tgz#f372b5a4012b474df0ed0c67da7a3198d7efee0b" - integrity sha512-swhc87YVHf9te0glBI7Oa/QBgsSCL4ZxtoR4V3vE6l7mEebsYRNL8y7Y2m2E6MrT0UTphM1S7mQqs0Sp7QTZ2g== + version "2.3.5" + resolved "https://registry.yarnpkg.com/camera-controls/-/camera-controls-2.3.5.tgz#db00c6d0dfc6538b499ef94e47f31f5da81e7e83" + integrity sha512-T4AcGyUL4Dn2VIxEX5R+vDvrWNY4KqHziPwzUMca4zKCawf5KMbbcmVbuhUzohzm2DxTcr5gm5gNSmCXWVTyWQ== caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464: - version "1.0.30001479" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001479.tgz#ef3d6f76011e44181af411fd4896123afbc14eda" - integrity sha512-6nuRFim5dx8Eu2tO+KJ9PiBdPHs7WB5Hdf+klDcyefyEuOAcfhihIv7pS+JFknJLUiNQbm1AJYKm0c9QOlQS/Q== + version "1.0.30001488" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001488.tgz#d19d7b6e913afae3e98f023db97c19e9ddc5e91f" + integrity sha512-NORIQuuL4xGpIy6iCCQGN4iFjlBXtfKWIenlUuyZJumLRIindLb7wXM+GO8erEhb7vXfcnf4BAg2PrSDN5TNLQ== caseless@~0.12.0: version "0.12.0" @@ -1170,7 +1198,7 @@ commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== -commander@^8.0.0, commander@^8.3.0: +commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== @@ -1242,9 +1270,9 @@ csstype@^3.0.2: integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== cypress@^12.10.0: - version "12.10.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.10.0.tgz#b6264f77c214d63530ebac2b33c4d099bd40b715" - integrity sha512-Y0wPc221xKKW1/4iAFCphkrG2jNR4MjOne3iGn4mcuCaE7Y5EtXL83N8BzRsAht7GYfWVjJ/UeTqEdDKHz39HQ== + version "12.12.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.12.0.tgz#0da622a34c970d8699ca6562d8e905ed7ce33c77" + integrity sha512-UU5wFQ7SMVCR/hyKok/KmzG6fpZgBHHfrXcHzDmPHWrT+UUetxFzQgt7cxCszlwfozckzwkd22dxMwl/vNkWRw== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -1333,15 +1361,16 @@ decode-named-character-reference@^1.0.0: character-entities "^2.0.0" deep-equal@^2.0.5: - version "2.2.0" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" - integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== + version "2.2.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.1.tgz#c72ab22f3a7d3503a4ca87dde976fe9978816739" + integrity sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ== dependencies: + array-buffer-byte-length "^1.0.0" call-bind "^1.0.2" - es-get-iterator "^1.1.2" - get-intrinsic "^1.1.3" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.0" is-arguments "^1.1.1" - is-array-buffer "^3.0.1" + is-array-buffer "^3.0.2" is-date-object "^1.0.5" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" @@ -1349,7 +1378,7 @@ deep-equal@^2.0.5: object-is "^1.1.5" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.0" side-channel "^1.0.4" which-boxed-primitive "^1.0.2" which-collection "^1.0.1" @@ -1365,12 +1394,30 @@ deepmerge@^4.0.0: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +default-browser-id@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" -define-properties@^1.1.3, define-properties@^1.1.4: +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== @@ -1389,9 +1436,9 @@ dequal@^2.0.0: integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== detect-gpu@^5.0.14: - version "5.0.21" - resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-5.0.21.tgz#91fc05655e219a294b5030b8659ee05ce4307c48" - integrity sha512-Dcf+4zxB3/4+7nVje3zQffJ3ksS7DiUztlNR0jC4AUF8FTBTeKWoIRxRAq4dGaRft8hEadUIDbjZbiKhCFo/Bw== + version "5.0.24" + resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-5.0.24.tgz#3a8c6dbcb0395c82da1eff3c3ae9ea1dd37c89e9" + integrity sha512-cI+WJujCzsJDlYKhcildTtG7YZmWsvMSFH6+gnozHp/R+Wr/WtR361e2PFQ3oDLo8Ek2xy5Buaiv2ucAI2D1qg== dependencies: webgl-constants "^1.1.1" @@ -1452,9 +1499,9 @@ ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: safe-buffer "^5.0.1" electron-to-chromium@^1.4.284: - version "1.4.365" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.365.tgz#ccd9e352d4493aa288d87e6ea36f3edf350c045e" - integrity sha512-FRHZO+1tUNO4TOPXmlxetkoaIY8uwHzd1kKopK/Gx2SKn1L47wJXWD44wxP5CGRyyP98z/c8e1eBzJrgPeiBOg== + version "1.4.401" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.401.tgz#cbd2c332c4a833e9e8d2ec5b3a6cd85ec6920907" + integrity sha512-AswqHsYyEbfSn0x87n31Na/xttUqEAg7NUjpiyxC20MaWKLyadOYHMzyLdF78N1iw+FK8/2KHLpZxRdyRILgtA== emoji-regex@^10.2.1: version "10.2.1" @@ -1471,6 +1518,13 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +encoding@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -1479,9 +1533,9 @@ end-of-stream@^1.1.0: once "^1.4.0" enhanced-resolve@^5.12.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + version "5.14.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz#0b6c676c8a3266c99fa281e4433a706f5c0c61c4" + integrity sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -1493,6 +1547,11 @@ enquirer@^2.3.6: dependencies: ansi-colors "^4.1.1" +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.21.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" @@ -1533,7 +1592,7 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.9" -es-get-iterator@^1.1.2: +es-get-iterator@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== @@ -1593,12 +1652,12 @@ escape-string-regexp@^5.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== -eslint-config-next@13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.3.0.tgz#c302fbecfe2b976ea306f7622af637ef9d9e3802" - integrity sha512-6YEwmFBX0VjBd3ODGW9df0Is0FLaRFdMN8eAahQG9CN6LjQ28J8AFr19ngxqMSg7Qv6Uca/3VeeBosJh1bzu0w== +eslint-config-next@^13.4.3: + version "13.4.3" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.4.3.tgz#15fccfddd69a2634e8939dba6a428362e09cbb21" + integrity sha512-1lXwdFi29fKxzeugof/TUE7lpHyJQt5+U4LaUHyvQfHjvsWO77vFNicJv5sX6k0VDVSbnfz0lw+avxI+CinbMg== dependencies: - "@next/eslint-plugin-next" "13.3.0" + "@next/eslint-plugin-next" "13.4.3" "@rushstack/eslint-patch" "^1.1.3" "@typescript-eslint/parser" "^5.42.0" eslint-import-resolver-node "^0.3.6" @@ -1715,10 +1774,10 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" - integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== eslint@8.38.0: version "8.38.0" @@ -1766,14 +1825,14 @@ eslint@8.38.0: strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.1: - version "9.5.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" - integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== +espree@^9.5.1, espree@^9.5.2: + version "9.5.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" + integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.0" + eslint-visitor-keys "^3.4.1" esquery@^1.4.2: version "1.5.0" @@ -1819,6 +1878,36 @@ execa@4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" + integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + executable@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -2024,7 +2113,7 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functions-have-names@^1.2.2: +functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -2039,7 +2128,7 @@ gaxios@^5.0.0, gaxios@^5.0.1: is-stream "^2.0.0" node-fetch "^2.6.7" -gcp-metadata@^5.0.0: +gcp-metadata@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.2.0.tgz#b4772e9c5976241f5d3e69c4f446c906d25506ec" integrity sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw== @@ -2048,12 +2137,13 @@ gcp-metadata@^5.0.0: json-bigint "^1.0.0" get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== dependencies: function-bind "^1.1.1" has "^1.0.3" + has-proto "^1.0.1" has-symbols "^1.0.3" get-stream@^5.0.0, get-stream@^5.1.0: @@ -2063,6 +2153,11 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -2171,11 +2266,6 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globalyzer@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" - integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== - globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -2199,27 +2289,22 @@ globby@^13.1.3: merge2 "^1.4.1" slash "^4.0.0" -globrex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - glsl-noise@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/glsl-noise/-/glsl-noise-0.0.0.tgz#367745f3a33382c0eeec4cb54b7e99cfc1d7670b" integrity sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w== google-auth-library@^8.0.2: - version "8.7.0" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.7.0.tgz#e36e255baba4755ce38dded4c50f896cf8515e51" - integrity sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q== + version "8.8.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.8.0.tgz#2e17494431cef56b571420d483a4debff6c481cd" + integrity sha512-0iJn7IDqObDG5Tu9Tn2WemmJ31ksEa96IyK0J0OZCpTh6CrC6FrattwKX87h3qKVuprCJpdOGKc1Xi8V0kMh8Q== dependencies: arrify "^2.0.0" base64-js "^1.3.0" ecdsa-sig-formatter "^1.0.11" fast-text-encoding "^1.0.0" gaxios "^5.0.0" - gcp-metadata "^5.0.0" + gcp-metadata "^5.2.0" gtoken "^6.1.0" jws "^4.0.0" lru-cache "^6.0.0" @@ -2318,6 +2403,35 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hast-util-from-dom@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz#25836ddecc3cc0849d32749c2a7aec03e94b59a7" + integrity sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ== + dependencies: + hastscript "^7.0.0" + web-namespaces "^2.0.0" + +hast-util-from-html-isomorphic@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-1.0.0.tgz#592b2bea880d476665b76ca1cf7d1a94925c80ec" + integrity sha512-Yu480AKeOEN/+l5LA674a+7BmIvtDj24GvOt7MtQWuhzUwlaaRWdEPXAh3Qm5vhuthpAipFb2vTetKXWOjmTvw== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-dom "^4.0.0" + hast-util-from-html "^1.0.0" + unist-util-remove-position "^4.0.0" + +hast-util-from-html@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-1.0.2.tgz#2482fd701b2d8270b912b3909d6fb645d4a346cf" + integrity sha512-LhrTA2gfCbLOGJq2u/asp4kwuG0y6NhWTXiPKP+n0qNukKy7hc10whqqCFfyvIA1Q5U5d0sp9HhNim9gglEH4A== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^7.0.0" + parse5 "^7.0.0" + vfile "^5.0.0" + vfile-message "^3.0.0" + hast-util-from-parse5@^7.0.0: version "7.1.2" resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz#aecfef73e3ceafdfa4550716443e4eb7b02e22b0" @@ -2447,6 +2561,23 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -2576,10 +2707,10 @@ is-ci@^3.0.0: dependencies: ci-info "^3.2.0" -is-core-module@^2.11.0, is-core-module@^2.12.0, is-core-module@^2.9.0: - version "2.12.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" - integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== +is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== dependencies: has "^1.0.3" @@ -2595,11 +2726,16 @@ is-decimal@^2.0.0: resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== -is-docker@^2.0.0, is-docker@^2.1.1: +is-docker@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -2622,6 +2758,13 @@ is-hexadecimal@^2.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-installed-globally@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" @@ -2687,6 +2830,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -2911,17 +3059,10 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -katex@^0.15.0: - version "0.15.6" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.15.6.tgz#c4e2f6ced2ac4de1ef6f737fe7c67d3026baa0e5" - integrity sha512-UpzJy4yrnqnhXvRPhjEuLA4lcPn6eRngixW7Q3TJErjg3Aw2PuLFBzTkdUb89UtumxjhHTqL3a5GDGETMSwgJA== - dependencies: - commander "^8.0.0" - katex@^0.16.0: - version "0.16.6" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.6.tgz#cf06c11bdda435b5ae5cb06efa080f4a4fcadf23" - integrity sha512-XVB7X8jEogjJ+OY+a9JdE+VOk9i7znela0HP6WaDbpB4sUh8ghrG0Ccluu2MA2tcJbFAViBC9aVXus2UvkEr8A== + version "0.16.7" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.7.tgz#36be1d4ed96e8afdc5863407e70f8fb250aeafd5" + integrity sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA== dependencies: commander "^8.3.0" @@ -3200,6 +3341,14 @@ mdast-util-math@^2.0.0: longest-streak "^3.0.0" mdast-util-to-markdown "^1.3.0" +mdast-util-newline-to-break@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-newline-to-break/-/mdast-util-newline-to-break-1.0.0.tgz#a42699874a9240dc89bfcf256d3ff3036856abba" + integrity sha512-491LcYv3gbGhhCrLoeALncQmega2xPh+m3gbsIhVsOX4sw85+ShLFPvPyibxc1Swx/6GtzxgVodq+cGa/47ULg== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-find-and-replace "^2.0.0" + mdast-util-phrasing@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz#c7c21d0d435d7fb90956038f02e8702781f95463" @@ -3299,15 +3448,14 @@ micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: uvu "^0.5.0" micromark-extension-gfm-autolink-literal@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.3.tgz#dc589f9c37eaff31a175bab49f12290edcf96058" - integrity sha512-i3dmvU0htawfWED8aHMMAzAVp/F0Z+0bPh3YrbTPPL1v4YAlCZpy5rBO5p0LPYiZo0zFVkoYh7vDU7yQSiCMjg== + version "1.0.4" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.4.tgz#3a8af48264be47138654ab0b8700a8e22785ef07" + integrity sha512-WCssN+M9rUyfHN5zPBn3/f0mIA7tqArHL/EKbv3CZK+LT2rG77FEikIQEqBkv46fOqXQK4NEW/Pc7Z27gshpeg== dependencies: micromark-util-character "^1.0.0" micromark-util-sanitize-uri "^1.0.0" micromark-util-symbol "^1.0.0" micromark-util-types "^1.0.0" - uvu "^0.5.0" micromark-extension-gfm-footnote@^1.0.0: version "1.1.0" @@ -3336,9 +3484,9 @@ micromark-extension-gfm-strikethrough@^1.0.0: uvu "^0.5.0" micromark-extension-gfm-table@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.5.tgz#7b708b728f8dc4d95d486b9e7a2262f9cddbcbb4" - integrity sha512-xAZ8J1X9W9K3JTJTUL7G6wSKhp2ZYHrFk5qJgY/4B33scJzE2kpfRL6oiw/veJTbt7jiM/1rngLlOKPWr1G+vg== + version "1.0.6" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.6.tgz#22b2b18dff9db39bdb29d6017e53bdd370672c8e" + integrity sha512-92pq7Q+T+4kXH4M6kL+pc8WU23Z9iuhcqmtYFWdFWjm73ZscFpH2xE28+XFpGWlvgq3LUwcN0XC0PGCicYFpgA== dependencies: micromark-factory-space "^1.0.0" micromark-util-character "^1.0.0" @@ -3365,9 +3513,9 @@ micromark-extension-gfm-task-list-item@^1.0.0: uvu "^0.5.0" micromark-extension-gfm@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-2.0.1.tgz#40f3209216127a96297c54c67f5edc7ef2d1a2a2" - integrity sha512-p2sGjajLa0iYiGQdT0oelahRYtMWvLjy8J9LOCxzIQsllMCGLbsLW+Nc+N4vi02jcRJvedVJ68cjelKIO6bpDA== + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz#e517e8579949a5024a493e49204e884aa74f5acf" + integrity sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ== dependencies: micromark-extension-gfm-autolink-literal "^1.0.0" micromark-extension-gfm-footnote "^1.0.0" @@ -3379,9 +3527,9 @@ micromark-extension-gfm@^2.0.0: micromark-util-types "^1.0.0" micromark-extension-math@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-extension-math/-/micromark-extension-math-2.1.0.tgz#15ec67f8a3d6de64428d6eb456b0b00a77402f3b" - integrity sha512-WH+fJkveMvM3ZN+deb/jT3UW623x8xO9ycfJNDC+UQXX+V72RO6hT9KqxA7c8XFwozAFJ7tufOeG+x/CVSXHUw== + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-extension-math/-/micromark-extension-math-2.1.1.tgz#9a2901d32a275a433e66e452ea48f4996fabf18b" + integrity sha512-4rTUTTwHuXNL/sHy/LpmTEku+YOJIK4VYdILxv8bRI4unSpfdd/UzOv/DBV2KqgBeGQiyA3vmsARrKS7WQWwxw== dependencies: "@types/katex" "^0.16.0" katex "^0.16.0" @@ -3589,6 +3737,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3645,27 +3798,28 @@ next-seo@^6.0.0: resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-6.0.0.tgz#4568dc61a44dbdf5fe5ff44156cd0ff8804889a2" integrity sha512-jKKt1p1z4otMA28AyeoAONixVjdYmgFCWwpEFtu+DwRHQDllVX3RjtyXbuCQiUZEfQ9rFPBpAI90vDeLZlMBdg== -next@13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1" - integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA== +next@^13.4.3: + version "13.4.3" + resolved "https://registry.yarnpkg.com/next/-/next-13.4.3.tgz#7f417dec9fa2731d8c1d1819a1c7d0919ad6fc75" + integrity sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA== dependencies: - "@next/env" "13.3.0" - "@swc/helpers" "0.4.14" + "@next/env" "13.4.3" + "@swc/helpers" "0.5.1" busboy "1.6.0" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" + zod "3.21.4" optionalDependencies: - "@next/swc-darwin-arm64" "13.3.0" - "@next/swc-darwin-x64" "13.3.0" - "@next/swc-linux-arm64-gnu" "13.3.0" - "@next/swc-linux-arm64-musl" "13.3.0" - "@next/swc-linux-x64-gnu" "13.3.0" - "@next/swc-linux-x64-musl" "13.3.0" - "@next/swc-win32-arm64-msvc" "13.3.0" - "@next/swc-win32-ia32-msvc" "13.3.0" - "@next/swc-win32-x64-msvc" "13.3.0" + "@next/swc-darwin-arm64" "13.4.3" + "@next/swc-darwin-x64" "13.4.3" + "@next/swc-linux-arm64-gnu" "13.4.3" + "@next/swc-linux-arm64-musl" "13.4.3" + "@next/swc-linux-x64-gnu" "13.4.3" + "@next/swc-linux-x64-musl" "13.4.3" + "@next/swc-win32-arm64-msvc" "13.4.3" + "@next/swc-win32-ia32-msvc" "13.4.3" + "@next/swc-win32-x64-msvc" "13.4.3" nextjs-progressbar@^0.0.16: version "0.0.16" @@ -3677,9 +3831,9 @@ nextjs-progressbar@^0.0.16: prop-types "^15.8.1" node-fetch@^2.6.1, node-fetch@^2.6.7: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + version "2.6.11" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" + integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== dependencies: whatwg-url "^5.0.0" @@ -3703,13 +3857,20 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + nprogress@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" @@ -3795,20 +3956,28 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -open@^8.4.0: - version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" + mimic-fn "^4.0.0" + +open@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== + dependencies: + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" is-wsl "^2.2.0" opentype.js@^1.3.3: @@ -3901,6 +4070,13 @@ parse5@^6.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parse5@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -3916,6 +4092,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -3996,9 +4177,9 @@ postcss-selector-parser@6.0.10: util-deprecate "^1.0.2" postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11: - version "6.0.11" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" - integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + version "6.0.13" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -4017,7 +4198,7 @@ postcss@8.4.14: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@8.4.22, postcss@^8.0.9: +postcss@8.4.22: version "8.4.22" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.22.tgz#c29e6776b60ab3af602d4b513d5bd2ff9aa85dc1" integrity sha512-XseknLAfRHzVWjCEtdviapiBtfLdgyzExD50Rg2ePaucEesyh8Wv4VPdW0nbyDa1ydbrAxV19jvMT4+LFmcNUA== @@ -4026,6 +4207,15 @@ postcss@8.4.22, postcss@^8.0.9: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.0.9: + version "8.4.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" + integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + potpack@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14" @@ -4037,14 +4227,14 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier-plugin-tailwindcss@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.7.tgz#2314d728cce9c9699ced41a01253eb48b4218da5" - integrity sha512-jQopIOgjLpX+y8HeD56XZw7onupRTC0cw7eKKUimI7vhjkPF5/1ltW5LyqaPtSyc8HvEpvNZsvvsGFa2qpa59w== + version "0.2.8" + resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.8.tgz#e9c0356680331f909a86fefe8fc2b247c21e23a2" + integrity sha512-KgPcEnJeIijlMjsA6WwYgRs5rh3/q76oInqtMXBA/EMcamrcYJpyhtRhyX1ayT9hnHlHTuO8sIifHF10WuSDKg== prettier@^2.8.7: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-bytes@^5.6.0: version "5.6.0" @@ -4094,9 +4284,9 @@ punycode@^2.1.0, punycode@^2.1.1: integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== qs@^6.7.0: - version "6.11.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" - integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== dependencies: side-channel "^1.0.4" @@ -4124,7 +4314,7 @@ react-composer@^5.0.3: dependencies: prop-types "^15.6.0" -react-dom@18.2.0: +react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== @@ -4133,9 +4323,9 @@ react-dom@18.2.0: scheduler "^0.23.0" react-fast-compare@^3.0.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.1.tgz#53933d9e14f364281d6cba24bfed7a4afb808b5f" - integrity sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg== + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== react-h5-audio-player@^3.8.6: version "3.8.6" @@ -4212,9 +4402,9 @@ react-reconciler@^0.27.0: scheduler "^0.21.0" react-toastify@^9.1.2: - version "9.1.2" - resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.1.2.tgz#293aa1f952240129fe485ae5cb2f8d09c652cf3f" - integrity sha512-PBfzXO5jMGEtdYR5jxrORlNZZe/EuOkwvwKijMatsZZm8IZwLj01YvobeJYNjFcA6uy6CVrx2fzL9GWbhWPTDA== + version "9.1.3" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.1.3.tgz#1e798d260d606f50e0fab5ee31daaae1d628c5ff" + integrity sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg== dependencies: clsx "^1.1.1" @@ -4225,7 +4415,7 @@ react-use-measure@^2.1.1: dependencies: debounce "^1.2.1" -react@18.2.0: +react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== @@ -4266,30 +4456,28 @@ regexp-to-ast@0.5.0: resolved "https://registry.yarnpkg.com/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz#56c73856bee5e1fef7f73a00f1473452ab712a24" integrity sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw== -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== +regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" rehype-katex@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/rehype-katex/-/rehype-katex-6.0.2.tgz#20197bbc10bdf79f6b999bffa6689d7f17226c35" - integrity sha512-C4gDAlS1+l0hJqctyiU64f9CvT00S03qV1T6HiMzbSuLBgWUtcqydWHY9OpKrm0SpkK16FNd62CDKyWLwV2ppg== + version "6.0.3" + resolved "https://registry.yarnpkg.com/rehype-katex/-/rehype-katex-6.0.3.tgz#83e5b929b0967978e9491c02117f55be3594d7e1" + integrity sha512-ByZlRwRUcWegNbF70CVRm2h/7xy7jQ3R9LaY4VVSvjnoVWwWVhNL60DiZsBpC5tSzYQOCvDbzncIpIjPZWodZA== dependencies: "@types/hast" "^2.0.0" - "@types/katex" "^0.11.0" + "@types/katex" "^0.14.0" + hast-util-from-html-isomorphic "^1.0.0" hast-util-to-text "^3.1.0" - katex "^0.15.0" - rehype-parse "^8.0.0" - unified "^10.0.0" - unist-util-remove-position "^4.0.0" + katex "^0.16.0" unist-util-visit "^4.0.0" -rehype-parse@^8.0.0, rehype-parse@^8.0.2: +rehype-parse@^8.0.2: version "8.0.4" resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.4.tgz#3d17c9ff16ddfef6bbcc8e6a25a99467b482d688" integrity sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg== @@ -4330,13 +4518,13 @@ rehype-sanitize@^5.0.1: unified "^10.0.0" remark-breaks@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/remark-breaks/-/remark-breaks-3.0.2.tgz#f466b9d3474d7323146c0149fc1496dabadd908e" - integrity sha512-x96YDJ9X+Ry0/JNZFKfr1hpcAKvGYWfUTszxY9RbxKEqq6uzPPoLCuHdZsLPZZUdAv3nCROyc7FPrQLWr2rxyw== + version "3.0.3" + resolved "https://registry.yarnpkg.com/remark-breaks/-/remark-breaks-3.0.3.tgz#660e6c995e954e5abdd95bf77df6f1402cb911ef" + integrity sha512-C7VkvcUp1TPUc2eAYzsPdaUh8Xj4FSbQnYA5A9f80diApLZscTDeG7efiWP65W8hV2sEy3JuGVU0i6qr5D8Hug== dependencies: "@types/mdast" "^3.0.0" + mdast-util-newline-to-break "^1.0.0" unified "^10.0.0" - unist-util-visit "^4.0.0" remark-gfm@^3.0.1: version "3.0.1" @@ -4359,9 +4547,9 @@ remark-math@^5.1.1: unified "^10.0.0" remark-parse@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.1.tgz#6f60ae53edbf0cf38ea223fe643db64d112e0775" - integrity sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw== + version "10.0.2" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.2.tgz#ca241fde8751c2158933f031a4e3efbaeb8bc262" + integrity sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw== dependencies: "@types/mdast" "^3.0.0" mdast-util-from-markdown "^1.0.0" @@ -4416,11 +4604,11 @@ resolve-from@^4.0.0: integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve@^1.1.7, resolve@^1.22.1: - version "1.22.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283" - integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw== + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.12.0" + is-core-module "^2.11.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -4458,6 +4646,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -4466,9 +4661,9 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" rxjs@^7.5.1: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -4493,7 +4688,7 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -4532,17 +4727,10 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.8: - version "7.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0" - integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.7: - version "7.4.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" - integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== +semver@^7.3.2, semver@^7.3.7, semver@^7.3.8: + version "7.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" + integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== dependencies: lru-cache "^6.0.0" @@ -4567,7 +4755,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -4714,6 +4902,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -4771,9 +4964,9 @@ suspend-react@^0.0.8: integrity sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg== swr@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/swr/-/swr-2.1.3.tgz#ae3608d4dc19f75121e0b51d1982735fbf02f52e" - integrity sha512-g3ApxIM4Fjbd6vvEAlW60hJlKcYxHb+wtehogTygrh6Jsw7wNagv9m4Oj5Gq6zvvZw0tcyhVGL9L0oISvl3sUw== + version "2.1.5" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.1.5.tgz#688effa719c03f6d35c66decbb0f8e79c7190399" + integrity sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw== dependencies: use-sync-external-store "^1.2.0" @@ -4840,14 +5033,14 @@ thenify-all@^1.0.0: any-promise "^1.0.0" three-mesh-bvh@^0.5.23: - version "0.5.23" - resolved "https://registry.yarnpkg.com/three-mesh-bvh/-/three-mesh-bvh-0.5.23.tgz#08e5b629144b48b11acbd433519680e457d398ed" - integrity sha512-nyk+MskdyDgECqkxdv57UjazqqhrMi+Al9PxJN6yFtx1CTW4r0eCQ27FtyYKY5gCIWhxjtNfWYDPVy8lzx6LkA== + version "0.5.24" + resolved "https://registry.yarnpkg.com/three-mesh-bvh/-/three-mesh-bvh-0.5.24.tgz#56dd27ad35bfbafacedaba4a4d567723bbdd686c" + integrity sha512-VTIgfjz8aFoPKTQoMIQQv9jJD4ybFRZuKKE1/kqy78FQcuHQ0+iIWv7C5cSb2inlvs7bNMVY3yRx3RXGZfrvzQ== three-stdlib@^2.21.8: - version "2.21.10" - resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.21.10.tgz#cfebcd4ff8bee7313c3a82f0fb7c5dd8c01e2805" - integrity sha512-YGkR3stmgmXSHL27+1A5dWAKKB+e63PLVkRWV/LLZ7f5+ExsqjLPm4YfBmJgkoSLgILYpQs6O7baY9gnIZlmNA== + version "2.21.11" + resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.21.11.tgz#ee058f1522edf17f994b4a80307568dd553bd52e" + integrity sha512-fFeaMOc3lpup4A/FrSKHmp/PNYmyxyV8dzMorJpYyZBdIGAn5BmokGSN9ei8t7Wv9thxCW+VLaxA6csxTl5MYg== dependencies: "@babel/runtime" "^7.16.7" "@types/offscreencanvas" "^2019.6.4" @@ -4862,9 +5055,9 @@ three-stdlib@^2.21.8: zstddec "^0.0.2" three@^0.152.0: - version "0.152.0" - resolved "https://registry.yarnpkg.com/three/-/three-0.152.0.tgz#a50034af675c636322135b6bb829d6e7e3a6213d" - integrity sha512-uvKoYo4b2bnqzsR4RJFuWecxwMKcgT1nFNmiWooCNr6AxZLCtfkj/xcfFgoi5mFopSVorh7bnvTHPfeW8DINGg== + version "0.152.2" + resolved "https://registry.yarnpkg.com/three/-/three-0.152.2.tgz#2ee0f2c504d31a4bc29b45495c12bded9fda7bfc" + integrity sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw== throttleit@^1.0.0: version "1.0.0" @@ -4876,19 +5069,16 @@ through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tiny-glob@^0.2.9: - version "0.2.9" - resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" - integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== - dependencies: - globalyzer "0.1.0" - globrex "^0.1.2" - tiny-inflate@^1.0.0, tiny-inflate@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" @@ -4922,24 +5112,24 @@ trim-lines@^3.0.0: integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== troika-three-text@^0.47.1: - version "0.47.1" - resolved "https://registry.yarnpkg.com/troika-three-text/-/troika-three-text-0.47.1.tgz#4a239b122242d7ea93dbbf66a9f1863eca83d979" - integrity sha512-/fPRUmxCkXxyUT8k6REC/aWeFzKbNr37ivrkrplSJNb3JcBUXvVt8MT0Ac5wTUvFsYTviYWprYS4/8Laen08WA== + version "0.47.2" + resolved "https://registry.yarnpkg.com/troika-three-text/-/troika-three-text-0.47.2.tgz#fdf89059c010563bb829262b20c41f69ca79b712" + integrity sha512-qylT0F+U7xGs+/PEf3ujBdJMYWbn0Qci0kLqI5BJG2kW1wdg4T1XSxneypnF05DxFqJhEzuaOR9S2SjiyknMng== dependencies: bidi-js "^1.0.2" - troika-three-utils "^0.47.0" - troika-worker-utils "^0.47.0" + troika-three-utils "^0.47.2" + troika-worker-utils "^0.47.2" webgl-sdf-generator "1.1.1" -troika-three-utils@^0.47.0: - version "0.47.0" - resolved "https://registry.yarnpkg.com/troika-three-utils/-/troika-three-utils-0.47.0.tgz#4749334ad2ae043767916f79bcc365cbb5ea64b5" - integrity sha512-yoVTQxVbpQX3a55giIwqwq6hyJA6oYvq7kaNGwFTeicoWmTZCqqTbytafx1gcuL5umrtw5MYgsxYUSOha+xp5w== +troika-three-utils@^0.47.2: + version "0.47.2" + resolved "https://registry.yarnpkg.com/troika-three-utils/-/troika-three-utils-0.47.2.tgz#af49ca694245dce631963d5fefe4e8e1b8af9044" + integrity sha512-/28plhCxfKtH7MSxEGx8e3b/OXU5A0xlwl+Sbdp0H8FXUHKZDoksduEKmjQayXYtxAyuUiCRunYIv/8Vi7aiyg== -troika-worker-utils@^0.47.0: - version "0.47.0" - resolved "https://registry.yarnpkg.com/troika-worker-utils/-/troika-worker-utils-0.47.0.tgz#bea382fee55333e177955e7ba6497d64b9d9388d" - integrity sha512-PSUc9vunDEkbE23jpgXD3PcF96jQHKjgMjS+4o5g6DEK/ZAPTnldb+FNddhppawfUcuraMFrslo0GmIC8UpEmA== +troika-worker-utils@^0.47.2: + version "0.47.2" + resolved "https://registry.yarnpkg.com/troika-worker-utils/-/troika-worker-utils-0.47.2.tgz#e7c5de5f37d56c072b13fa8112bb844e048ff46c" + integrity sha512-mzss4MeyzUkYBppn4x5cdAqrhBHFEuVmMMgLMTyFV23x6GvQMyo+/R5E5Lsbrt7WSt5RfvewjcwD1DChRTA9lA== trough@^2.0.0: version "2.1.0" @@ -4967,9 +5157,9 @@ tslib@^1.8.1: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + version "2.5.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" + integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA== tsutils@^3.21.0: version "3.21.0" @@ -5354,6 +5544,11 @@ yoga-wasm-web@0.3.3, yoga-wasm-web@^0.3.3: resolved "https://registry.yarnpkg.com/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz#eb8e9fcb18e5e651994732f19a220cb885d932ba" integrity sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA== +zod@3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" + integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw== + zstddec@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.0.2.tgz#57e2f28dd1ff56b750e07d158a43f0611ad9eeb4"