[WEB-5093] improvement: adds content-based MIME type detection for uploads #7925

- Refactored file upload utilities to use async functions for better handling of file metadata.
- Introduced MIME type detection using the file-type library.
- Updated file service methods to await metadata retrieval.
- Added new dependencies for file-type and updated package.json accordingly.
- Removed deprecated file handling code from utils and adjusted imports across services.
This commit is contained in:
Prateek Shourya 2025-10-09 12:22:43 +05:30 committed by GitHub
parent f2539c5051
commit 0b257c8693
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 352 additions and 82 deletions

View file

@ -1,18 +1,5 @@
// plane imports
import { API_BASE_URL } from "@plane/constants";
import { TFileMetaDataLite, TFileSignedURLResponse } from "@plane/types";
/**
* @description from the provided signed URL response, generate a payload to be used to upload the file
* @param {TFileSignedURLResponse} signedURLResponse
* @param {File} file
* @returns {FormData} file upload request payload
*/
export const generateFileUploadPayload = (signedURLResponse: TFileSignedURLResponse, file: File): FormData => {
const formData = new FormData();
Object.entries(signedURLResponse.upload_data.fields).forEach(([key, value]) => formData.append(key, value));
formData.append("file", file);
return formData;
};
/**
* @description combine the file path with the base URL
@ -26,17 +13,6 @@ export const getFileURL = (path: string): string | undefined => {
return `${API_BASE_URL}${path}`;
};
/**
* @description returns the necessary file meta data to upload a file
* @param {File} file
* @returns {TFileMetaDataLite} payload with file info
*/
export const getFileMetaDataForUpload = (file: File): TFileMetaDataLite => ({
name: file.name,
size: file.size,
type: file.type,
});
/**
* @description this function returns the assetId from the asset source
* @param {string} src

View file

@ -1,9 +1,10 @@
import { AxiosRequestConfig } from "axios";
// plane types
import { API_BASE_URL } from "@plane/constants";
import { getFileMetaDataForUpload, generateFileUploadPayload } from "@plane/services";
import { TFileEntityInfo, TFileSignedURLResponse } from "@plane/types";
import { getAssetIdFromUrl } from "@plane/utils";
// helpers
import { generateFileUploadPayload, getAssetIdFromUrl, getFileMetaDataForUpload } from "@plane/utils";
// services
import { APIService } from "@/services/api.service";
import { FileUploadService } from "@/services/file-upload.service";
@ -68,7 +69,7 @@ export class FileService extends APIService {
file: File,
uploadProgressHandler?: AxiosRequestConfig["onUploadProgress"]
): Promise<TFileSignedURLResponse> {
const fileMetaData = getFileMetaDataForUpload(file);
const fileMetaData = await getFileMetaDataForUpload(file);
return this.post(`/api/assets/v2/workspaces/${workspaceSlug}/`, {
...data,
...fileMetaData,
@ -145,7 +146,7 @@ export class FileService extends APIService {
file: File,
uploadProgressHandler?: AxiosRequestConfig["onUploadProgress"]
): Promise<TFileSignedURLResponse> {
const fileMetaData = getFileMetaDataForUpload(file);
const fileMetaData = await getFileMetaDataForUpload(file);
return this.post(`/api/assets/v2/workspaces/${workspaceSlug}/projects/${projectId}/`, {
...data,
...fileMetaData,
@ -175,7 +176,7 @@ export class FileService extends APIService {
}
async uploadUserAsset(data: TFileEntityInfo, file: File): Promise<TFileSignedURLResponse> {
const fileMetaData = getFileMetaDataForUpload(file);
const fileMetaData = await getFileMetaDataForUpload(file);
return this.post(`/api/assets/v2/user-assets/`, {
...data,
...fileMetaData,

View file

@ -1,9 +1,8 @@
import { AxiosRequestConfig } from "axios";
import { API_BASE_URL } from "@plane/constants";
// plane types
import { getFileMetaDataForUpload, generateFileUploadPayload } from "@plane/services";
import { EIssueServiceType, TIssueAttachment, TIssueAttachmentUploadResponse, TIssueServiceType } from "@plane/types";
// helpers
import { generateFileUploadPayload, getFileMetaDataForUpload } from "@plane/utils";
// services
import { APIService } from "@/services/api.service";
import { FileUploadService } from "@/services/file-upload.service";
@ -41,7 +40,7 @@ export class IssueAttachmentService extends APIService {
file: File,
uploadProgressHandler?: AxiosRequestConfig["onUploadProgress"]
): Promise<TIssueAttachment> {
const fileMetaData = getFileMetaDataForUpload(file);
const fileMetaData = await getFileMetaDataForUpload(file);
return this.post(
`/api/assets/v2/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/attachments/`,
fileMetaData

View file

@ -23,7 +23,8 @@
"dependencies": {
"@plane/constants": "workspace:*",
"@plane/types": "workspace:*",
"axios": "catalog:"
"axios": "catalog:",
"file-type": "^21.0.0"
},
"devDependencies": {
"@plane/eslint-config": "workspace:*",

View file

@ -1,3 +1,6 @@
// external imports
import { fileTypeFromBuffer } from "file-type";
// plane imports
import { TFileMetaDataLite, TFileSignedURLResponse } from "@plane/types";
/**
@ -13,16 +16,63 @@ export const generateFileUploadPayload = (signedURLResponse: TFileSignedURLRespo
return formData;
};
/**
* @description Detect MIME type from file signature using file-type library
* @param {File} file
* @returns {Promise<string>} detected MIME type or empty string if unknown
*/
const detectMimeTypeFromSignature = async (file: File): Promise<string> => {
try {
// Read first 4KB which is usually sufficient for most file type detection
const chunk = file.slice(0, 4096);
const buffer = await chunk.arrayBuffer();
const uint8Array = new Uint8Array(buffer);
const fileType = await fileTypeFromBuffer(uint8Array);
return fileType?.mime || "";
} catch (_error) {
return "";
}
};
/**
* @description Determine the MIME type of a file using multiple detection methods
* @param {File} file
* @returns {Promise<string>} detected MIME type
*/
const detectFileType = async (file: File): Promise<string> => {
// check if the file has a MIME type
if (file.type && file.type.trim() !== "") {
return file.type;
}
// detect from file signature using file-type library
try {
const signatureType = await detectMimeTypeFromSignature(file);
if (signatureType) {
return signatureType;
}
} catch (_error) {
console.error("Error detecting file type from signature:", _error);
}
// fallback for unknown files
return "application/octet-stream";
};
/**
* @description returns the necessary file meta data to upload a file
* @param {File} file
* @returns {TFileMetaDataLite} payload with file info
* @returns {Promise<TFileMetaDataLite>} payload with file info
*/
export const getFileMetaDataForUpload = (file: File): TFileMetaDataLite => ({
name: file.name,
size: file.size,
type: file.type,
});
export const getFileMetaDataForUpload = async (file: File): Promise<TFileMetaDataLite> => {
const fileType = await detectFileType(file);
return {
name: file.name,
size: file.size,
type: fileType,
};
};
/**
* @description this function returns the assetId from the asset source

View file

@ -1,3 +1,4 @@
export * from "./file-upload.service";
export * from "./sites-file.service";
export * from "./file.service";
export * from "./helper";

View file

@ -74,7 +74,7 @@ export class SitesFileService extends FileService {
* @throws {Error} If the request fails
*/
async uploadAsset(anchor: string, data: TFileEntityInfo, file: File): Promise<TFileSignedURLResponse> {
const fileMetaData = getFileMetaDataForUpload(file);
const fileMetaData = await getFileMetaDataForUpload(file);
return this.post(`/api/public/assets/v2/anchor/${anchor}/`, {
...data,
...fileMetaData,

View file

@ -1,6 +1,5 @@
// plane imports
import { API_BASE_URL } from "@plane/constants";
import { TFileMetaDataLite, TFileSignedURLResponse } from "@plane/types";
/**
* @description combine the file path with the base URL
@ -14,30 +13,6 @@ export const getFileURL = (path: string): string | undefined => {
return `${API_BASE_URL}${path}`;
};
/**
* @description from the provided signed URL response, generate a payload to be used to upload the file
* @param {TFileSignedURLResponse} signedURLResponse
* @param {File} file
* @returns {FormData} file upload request payload
*/
export const generateFileUploadPayload = (signedURLResponse: TFileSignedURLResponse, file: File): FormData => {
const formData = new FormData();
Object.entries(signedURLResponse.upload_data.fields).forEach(([key, value]) => formData.append(key, value));
formData.append("file", file);
return formData;
};
/**
* @description returns the necessary file meta data to upload a file
* @param {File} file
* @returns {TFileMetaDataLite} payload with file info
*/
export const getFileMetaDataForUpload = (file: File): TFileMetaDataLite => ({
name: file.name,
size: file.size,
type: file.type,
});
/**
* @description this function returns the assetId from the asset source
* @param {string} src

301
pnpm-lock.yaml generated
View file

@ -1088,6 +1088,9 @@ importers:
axios:
specifier: 'catalog:'
version: 1.12.0
file-type:
specifier: ^21.0.0
version: 21.0.0
devDependencies:
'@plane/eslint-config':
specifier: workspace:*
@ -1546,6 +1549,9 @@ packages:
'@types/react':
optional: true
'@borewit/text-codec@0.1.1':
resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==}
'@bprogress/core@1.3.4':
resolution: {integrity: sha512-q/AqpurI/1uJzOrQROuZWixn/+ARekh+uvJGwLCP6HQ/EqAX4SkvNf618tSBxL4NysC0MwqAppb/mRw6Tzi61w==}
@ -2203,8 +2209,8 @@ packages:
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
'@napi-rs/wasm-runtime@1.0.5':
resolution: {integrity: sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==}
'@napi-rs/wasm-runtime@1.0.3':
resolution: {integrity: sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==}
'@next/env@14.2.32':
resolution: {integrity: sha512-n9mQdigI6iZ/DF6pCTwMKeWgF2e8lg7qgt5M7HXMLtyhZYMnf/u905M18sSpPmHL9MKp9JHo56C6jrD2EvWxng==}
@ -2648,51 +2654,101 @@ packages:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.50.0':
resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm-eabi@4.52.4':
resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.50.0':
resolution: {integrity: sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==}
cpu: [arm64]
os: [android]
'@rollup/rollup-android-arm64@4.52.4':
resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.50.0':
resolution: {integrity: sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-arm64@4.52.4':
resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.50.0':
resolution: {integrity: sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.52.4':
resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.50.0':
resolution: {integrity: sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-arm64@4.52.4':
resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.50.0':
resolution: {integrity: sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.52.4':
resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.50.0':
resolution: {integrity: sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-gnueabihf@4.52.4':
resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.50.0':
resolution: {integrity: sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.52.4':
resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.50.0':
resolution: {integrity: sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.52.4':
resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.50.0':
resolution: {integrity: sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.52.4':
resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==}
cpu: [arm64]
@ -2703,46 +2759,96 @@ packages:
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-loongarch64-gnu@4.50.0':
resolution: {integrity: sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.50.0':
resolution: {integrity: sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.52.4':
resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.50.0':
resolution: {integrity: sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.52.4':
resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.50.0':
resolution: {integrity: sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.52.4':
resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.50.0':
resolution: {integrity: sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.52.4':
resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.50.0':
resolution: {integrity: sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.52.4':
resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.50.0':
resolution: {integrity: sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.52.4':
resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==}
cpu: [x64]
os: [linux]
'@rollup/rollup-openharmony-arm64@4.50.0':
resolution: {integrity: sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-openharmony-arm64@4.52.4':
resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.50.0':
resolution: {integrity: sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-arm64-msvc@4.52.4':
resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.50.0':
resolution: {integrity: sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.52.4':
resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==}
cpu: [ia32]
@ -2753,6 +2859,11 @@ packages:
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.50.0':
resolution: {integrity: sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.52.4':
resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==}
cpu: [x64]
@ -3391,6 +3502,13 @@ packages:
y-protocols: ^1.0.1
yjs: ^13.5.38
'@tokenizer/inflate@0.2.7':
resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==}
engines: {node: '>=18'}
'@tokenizer/token@0.3.0':
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@ -5158,6 +5276,9 @@ packages:
fflate@0.4.8:
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
@ -5166,6 +5287,10 @@ packages:
resolution: {integrity: sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==}
engines: {node: '>= 12'}
file-type@21.0.0:
resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==}
engines: {node: '>=20'}
filesize@10.1.6:
resolution: {integrity: sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==}
engines: {node: '>= 10.4.0'}
@ -7145,6 +7270,11 @@ packages:
resolution: {integrity: sha512-Wwh7EwalMzzX3Yy3VN58VEajeR2Si8+HDNMf706jPLIqU7CxneRW+dQVfznf5O0TWTnJyu4npelwg2bzTXB1Nw==}
hasBin: true
rollup@4.50.0:
resolution: {integrity: sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
rollup@4.52.4:
resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@ -7433,6 +7563,10 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
strtok3@10.3.4:
resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==}
engines: {node: '>=18'}
style-loader@3.3.4:
resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==}
engines: {node: '>= 12.13.0'}
@ -7614,6 +7748,10 @@ packages:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
token-types@6.1.1:
resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==}
engines: {node: '>=14.16'}
tough-cookie@5.1.2:
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
engines: {node: '>=16'}
@ -7777,6 +7915,10 @@ packages:
uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
uint8array-extras@1.5.0:
resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
engines: {node: '>=18'}
unbox-primitive@1.1.0:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
@ -8401,6 +8543,8 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.11
'@borewit/text-codec@0.1.1': {}
'@bprogress/core@1.3.4': {}
'@bprogress/next@3.2.12(next@14.2.32(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
@ -9063,7 +9207,7 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
'@napi-rs/wasm-runtime@1.0.5':
'@napi-rs/wasm-runtime@1.0.3':
dependencies:
'@emnapi/core': 1.5.0
'@emnapi/runtime': 1.5.0
@ -9486,7 +9630,7 @@ snapshots:
'@rolldown/binding-wasm32-wasi@1.0.0-beta.34':
dependencies:
'@napi-rs/wasm-runtime': 1.0.5
'@napi-rs/wasm-runtime': 1.0.3
optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.34':
@ -9508,69 +9652,132 @@ snapshots:
optionalDependencies:
rollup: 4.52.4
'@rollup/rollup-android-arm-eabi@4.50.0':
optional: true
'@rollup/rollup-android-arm-eabi@4.52.4':
optional: true
'@rollup/rollup-android-arm64@4.50.0':
optional: true
'@rollup/rollup-android-arm64@4.52.4':
optional: true
'@rollup/rollup-darwin-arm64@4.50.0':
optional: true
'@rollup/rollup-darwin-arm64@4.52.4':
optional: true
'@rollup/rollup-darwin-x64@4.50.0':
optional: true
'@rollup/rollup-darwin-x64@4.52.4':
optional: true
'@rollup/rollup-freebsd-arm64@4.50.0':
optional: true
'@rollup/rollup-freebsd-arm64@4.52.4':
optional: true
'@rollup/rollup-freebsd-x64@4.50.0':
optional: true
'@rollup/rollup-freebsd-x64@4.52.4':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.50.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.52.4':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.50.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.52.4':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.50.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.52.4':
optional: true
'@rollup/rollup-linux-arm64-musl@4.50.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.52.4':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.52.4':
optional: true
'@rollup/rollup-linux-loongarch64-gnu@4.50.0':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.50.0':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.52.4':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.50.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.52.4':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.50.0':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.52.4':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.50.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.52.4':
optional: true
'@rollup/rollup-linux-x64-gnu@4.50.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.52.4':
optional: true
'@rollup/rollup-linux-x64-musl@4.50.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.52.4':
optional: true
'@rollup/rollup-openharmony-arm64@4.50.0':
optional: true
'@rollup/rollup-openharmony-arm64@4.52.4':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.50.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.52.4':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.50.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.52.4':
optional: true
'@rollup/rollup-win32-x64-gnu@4.52.4':
optional: true
'@rollup/rollup-win32-x64-msvc@4.50.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.52.4':
optional: true
@ -10331,6 +10538,16 @@ snapshots:
y-protocols: 1.0.6(yjs@13.6.27)
yjs: 13.6.27
'@tokenizer/inflate@0.2.7':
dependencies:
debug: 4.4.3
fflate: 0.8.2
token-types: 6.1.1
transitivePeerDependencies:
- supports-color
'@tokenizer/token@0.3.0': {}
'@tsconfig/node10@1.0.11':
optional: true
@ -10374,7 +10591,7 @@ snapshots:
'@types/body-parser@1.19.6':
dependencies:
'@types/connect': 3.4.38
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/chai@5.2.2':
dependencies:
@ -10383,15 +10600,15 @@ snapshots:
'@types/compression@1.8.1':
dependencies:
'@types/express': 4.17.23
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/connect@3.4.38':
dependencies:
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/cors@2.8.19':
dependencies:
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/d3-array@3.2.1': {}
@ -10441,14 +10658,14 @@ snapshots:
'@types/express-serve-static-core@4.19.6':
dependencies:
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/qs': 6.14.0
'@types/range-parser': 1.2.7
'@types/send': 0.17.5
'@types/express-serve-static-core@5.0.7':
dependencies:
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/qs': 6.14.0
'@types/range-parser': 1.2.7
'@types/send': 0.17.5
@ -10527,7 +10744,6 @@ snapshots:
'@types/node@22.17.2':
dependencies:
undici-types: 6.21.0
optional: true
'@types/nprogress@0.2.3': {}
@ -10547,7 +10763,7 @@ snapshots:
'@types/pino@6.3.12':
dependencies:
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/pino-pretty': 5.0.0
'@types/pino-std-serializers': 4.0.0
sonic-boom: 2.8.0
@ -10587,12 +10803,12 @@ snapshots:
'@types/send@0.17.5':
dependencies:
'@types/mime': 1.3.5
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/serve-static@1.15.8':
dependencies:
'@types/http-errors': 2.0.5
'@types/node': 22.12.0
'@types/node': 22.17.2
'@types/send': 0.17.5
'@types/triple-beam@1.3.5': {}
@ -10611,7 +10827,7 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
'@types/node': 22.12.0
'@types/node': 22.17.2
'@typescript-eslint/eslint-plugin@8.40.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)':
dependencies:
@ -12352,6 +12568,8 @@ snapshots:
fflate@0.4.8: {}
fflate@0.8.2: {}
file-entry-cache@6.0.1:
dependencies:
flat-cache: 3.2.0
@ -12360,6 +12578,15 @@ snapshots:
dependencies:
tslib: 2.8.1
file-type@21.0.0:
dependencies:
'@tokenizer/inflate': 0.2.7
strtok3: 10.3.4
token-types: 6.1.1
uint8array-extras: 1.5.0
transitivePeerDependencies:
- supports-color
filesize@10.1.6: {}
fill-range@7.1.1:
@ -12985,7 +13212,7 @@ snapshots:
jest-worker@27.5.1:
dependencies:
'@types/node': 22.12.0
'@types/node': 22.17.2
merge-stream: 2.0.0
supports-color: 8.1.1
@ -14529,6 +14756,33 @@ snapshots:
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.34
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.34
rollup@4.50.0:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.50.0
'@rollup/rollup-android-arm64': 4.50.0
'@rollup/rollup-darwin-arm64': 4.50.0
'@rollup/rollup-darwin-x64': 4.50.0
'@rollup/rollup-freebsd-arm64': 4.50.0
'@rollup/rollup-freebsd-x64': 4.50.0
'@rollup/rollup-linux-arm-gnueabihf': 4.50.0
'@rollup/rollup-linux-arm-musleabihf': 4.50.0
'@rollup/rollup-linux-arm64-gnu': 4.50.0
'@rollup/rollup-linux-arm64-musl': 4.50.0
'@rollup/rollup-linux-loongarch64-gnu': 4.50.0
'@rollup/rollup-linux-ppc64-gnu': 4.50.0
'@rollup/rollup-linux-riscv64-gnu': 4.50.0
'@rollup/rollup-linux-riscv64-musl': 4.50.0
'@rollup/rollup-linux-s390x-gnu': 4.50.0
'@rollup/rollup-linux-x64-gnu': 4.50.0
'@rollup/rollup-linux-x64-musl': 4.50.0
'@rollup/rollup-openharmony-arm64': 4.50.0
'@rollup/rollup-win32-arm64-msvc': 4.50.0
'@rollup/rollup-win32-ia32-msvc': 4.50.0
'@rollup/rollup-win32-x64-msvc': 4.50.0
fsevents: 2.3.3
rollup@4.52.4:
dependencies:
'@types/estree': 1.0.8
@ -14556,6 +14810,7 @@ snapshots:
'@rollup/rollup-win32-x64-gnu': 4.52.4
'@rollup/rollup-win32-x64-msvc': 4.52.4
fsevents: 2.3.3
optional: true
rope-sequence@1.3.4: {}
@ -14937,6 +15192,10 @@ snapshots:
strip-json-comments@3.1.1: {}
strtok3@10.3.4:
dependencies:
'@tokenizer/token': 0.3.0
style-loader@3.3.4(webpack@5.101.3(@swc/core@1.13.5(@swc/helpers@0.5.17))(esbuild@0.25.0)):
dependencies:
webpack: 5.101.3(@swc/core@1.13.5(@swc/helpers@0.5.17))(esbuild@0.25.0)
@ -15113,6 +15372,12 @@ snapshots:
toidentifier@1.0.1: {}
token-types@6.1.1:
dependencies:
'@borewit/text-codec': 0.1.1
'@tokenizer/token': 0.3.0
ieee754: 1.2.1
tough-cookie@5.1.2:
dependencies:
tldts: 6.1.86
@ -15281,6 +15546,8 @@ snapshots:
uc.micro@2.1.0: {}
uint8array-extras@1.5.0: {}
unbox-primitive@1.1.0:
dependencies:
call-bound: 1.0.4
@ -15499,7 +15766,7 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.52.4
rollup: 4.50.0
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 22.17.2