perf: decouple constants from zod and add bundle analyzer (#7101)

This commit is contained in:
Theodór Tómas
2026-01-14 16:50:05 +07:00
committed by GitHub
parent db752cee15
commit 48cded1646
18 changed files with 278 additions and 64 deletions
+1
View File
@@ -62,3 +62,4 @@ branch.json
packages/ios/FormbricksSDK/FormbricksSDK.xcodeproj/project.xcworkspace/xcuserdata
.cursorrules
i18n.cache
stats.html
+2
View File
@@ -28,6 +28,7 @@
"scripts": {
"dev": "vite build --watch --mode dev",
"build": "tsc && vite build",
"build:analyze": "tsc && ANALYZE=true vite build",
"build:dev": "tsc && vite build --mode dev",
"go": "vite build --watch --mode dev",
"lint": "eslint src --fix --ext .ts,.js,.tsx,.jsx",
@@ -58,6 +59,7 @@
"autoprefixer": "10.4.21",
"concurrently": "9.1.2",
"postcss": "8.5.3",
"rollup-plugin-visualizer": "6.0.5",
"tailwindcss": "4.1.17",
"terser": "5.39.1",
"vite": "6.4.1",
@@ -1,11 +1,11 @@
import { useState } from "preact/hooks";
import { useTranslation } from "react-i18next";
import { OpenText } from "@formbricks/survey-ui";
import { ZEmail, ZUrl } from "@formbricks/types/common";
import { type TResponseData, type TResponseTtc } from "@formbricks/types/responses";
import type { TSurveyOpenTextElement } from "@formbricks/types/surveys/elements";
import { getLocalizedValue } from "@/lib/i18n";
import { getUpdatedTtc, useTtc } from "@/lib/ttc";
import { validateEmail, validatePhone, validateUrl } from "@/lib/validation";
interface OpenTextElementProps {
element: TSurveyOpenTextElement;
@@ -50,27 +50,24 @@ export function OpenTextElement({
return true;
};
const validateEmail = (): boolean => {
if (!ZEmail.safeParse(value).success) {
const checkEmail = (): boolean => {
if (!validateEmail(value)) {
setErrorMessage(t("errors.please_enter_a_valid_email_address"));
return false;
}
return true;
};
const validateUrl = (): boolean => {
if (!ZUrl.safeParse(value).success) {
const checkUrl = (): boolean => {
if (!validateUrl(value)) {
setErrorMessage(t("errors.please_enter_a_valid_url"));
return false;
}
return true;
};
const validatePhone = (): boolean => {
// Match the same pattern: must start with digit or +, end with digit
// Allows digits, +, -, and spaces in between
const phoneRegex = /^[0-9+][0-9+\- ]*[0-9]$/;
if (!phoneRegex.test(value)) {
const checkPhone = (): boolean => {
if (!validatePhone(value)) {
setErrorMessage(t("errors.please_enter_a_valid_phone_number"));
return false;
}
@@ -81,13 +78,13 @@ export function OpenTextElement({
if (!value || value.trim() === "") return true;
if (element.inputType === "email") {
return validateEmail();
return checkEmail();
}
if (element.inputType === "url") {
return validateUrl();
return checkUrl();
}
if (element.inputType === "phone") {
return validatePhone();
return checkPhone();
}
return true;
};
@@ -2,12 +2,12 @@ import { useEffect, useRef, useState } from "preact/hooks";
import { type TJsFileUploadParams } from "@formbricks/types/js";
import { type TResponseData, TResponseDataValue, type TResponseTtc } from "@formbricks/types/responses";
import { type TUploadFileConfig } from "@formbricks/types/storage";
import { TSurveyBlock } from "@formbricks/types/surveys/blocks";
import { type TSurveyBlock } from "@formbricks/types/surveys/blocks";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
import {
TSurveyElement,
TSurveyElementTypeEnum,
TSurveyMatrixElement,
TSurveyRankingElement,
type TSurveyElement,
type TSurveyMatrixElement,
type TSurveyRankingElement,
} from "@formbricks/types/surveys/elements";
import { BackButton } from "@/components/buttons/back-button";
import { SubmitButton } from "@/components/buttons/submit-button";
@@ -2,11 +2,8 @@ import { useEffect, useRef } from "preact/hooks";
import { type TJsFileUploadParams } from "@formbricks/types/js";
import { type TResponseData, type TResponseDataValue, type TResponseTtc } from "@formbricks/types/responses";
import { type TUploadFileConfig } from "@formbricks/types/storage";
import {
TSurveyElement,
TSurveyElementChoice,
TSurveyElementTypeEnum,
} from "@formbricks/types/surveys/elements";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
import { type TSurveyElement, type TSurveyElementChoice } from "@formbricks/types/surveys/elements";
import { AddressElement } from "@/components/elements/address-element";
import { CalElement } from "@/components/elements/cal-element";
import { ConsentElement } from "@/components/elements/consent-element";
+6 -6
View File
@@ -1,10 +1,10 @@
import { describe, expect, test, vi } from "vitest";
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
import { TResponseData, TResponseVariables } from "@formbricks/types/responses";
import { TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements";
import { TConditionGroup, TSingleCondition } from "@formbricks/types/surveys/logic";
import { TSurveyVariable } from "@formbricks/types/surveys/types";
import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js";
import { type TResponseData, type TResponseVariables } from "@formbricks/types/responses";
import { type TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
import { type TConditionGroup, type TSingleCondition } from "@formbricks/types/surveys/logic";
import { type TSurveyVariable } from "@formbricks/types/surveys/types";
import { evaluateLogic, isConditionGroup, performActions } from "./logic";
// Mock the imported function
+7 -6
View File
@@ -1,9 +1,10 @@
import { TJsEnvironmentStateSurvey } from "@formbricks/types/js";
import { TResponseData, TResponseVariables } from "@formbricks/types/responses";
import { TActionCalculate, TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks";
import { TSurveyElement, TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements";
import { TConditionGroup, TSingleCondition } from "@formbricks/types/surveys/logic";
import { TSurveyVariable } from "@formbricks/types/surveys/types";
import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js";
import { type TResponseData, type TResponseVariables } from "@formbricks/types/responses";
import { type TActionCalculate, type TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
import type { TSurveyElement } from "@formbricks/types/surveys/elements";
import { type TConditionGroup, type TSingleCondition } from "@formbricks/types/surveys/logic";
import { type TSurveyVariable } from "@formbricks/types/surveys/types";
import { getLocalizedValue } from "@/lib/i18n";
import { getElementsFromSurveyBlocks } from "./utils";
+2 -1
View File
@@ -1,6 +1,7 @@
import { describe, expect, test, vi } from "vitest";
import { type TResponseData, type TResponseVariables } from "@formbricks/types/responses";
import { TSurveyElementTypeEnum, type TSurveyOpenTextElement } from "@formbricks/types/surveys/elements";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
import { type TSurveyOpenTextElement } from "@formbricks/types/surveys/elements";
import { parseRecallInformation, replaceRecallInfo } from "./recall";
// Mock getLocalizedValue (assuming path and simple behavior)
+2 -1
View File
@@ -1,5 +1,6 @@
import { type TResponseData, type TResponseVariables } from "@formbricks/types/responses";
import { TSurveyElement, TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
import { type TSurveyElement } from "@formbricks/types/surveys/elements";
import { formatDateWithOrdinal, isValidDateString } from "@/lib/date-time";
import { getLocalizedValue } from "@/lib/i18n";
+1 -1
View File
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, test, vi } from "vitest";
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
import type { TJsEnvironmentStateSurvey } from "../../../types/js";
import { type TAllowedFileExtension, mimeTypes } from "../../../types/storage";
import { TSurveyElementTypeEnum } from "../../../types/surveys/elements";
import type { TSurveyLanguage } from "../../../types/surveys/types";
import {
findBlockByElementId,
+37 -4
View File
@@ -1,9 +1,13 @@
import { type Result, err, ok, wrapThrowsAsync } from "@formbricks/types/error-handlers";
import { type ApiErrorResponse } from "@formbricks/types/errors";
import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js";
import { TAllowedFileExtension, mimeTypes } from "@formbricks/types/storage";
import { TSurveyBlock, TSurveyBlockLogic, TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks";
import { type TSurveyElement, TSurveyElementChoice } from "@formbricks/types/surveys/elements";
import { type TAllowedFileExtension } from "@formbricks/types/storage";
import {
type TSurveyBlock,
type TSurveyBlockLogic,
type TSurveyBlockLogicAction,
} from "@formbricks/types/surveys/blocks";
import { type TSurveyElement, type TSurveyElementChoice } from "@formbricks/types/surveys/elements";
import { type TShuffleOption } from "@formbricks/types/surveys/types";
import { ApiResponse, ApiSuccessResponse } from "@/types/api";
@@ -177,7 +181,36 @@ export const getDefaultLanguageCode = (survey: TJsEnvironmentStateSurvey): strin
if (defaultSurveyLanguage) return defaultSurveyLanguage.language.code;
};
// Function to convert file extension to its MIME type
// Inlined from @formbricks/types/storage.ts to avoid Zod dependency
const mimeTypes: Record<string, string> = {
heic: "image/heic",
png: "image/png",
jpeg: "image/jpeg",
jpg: "image/jpeg",
webp: "image/webp",
ico: "image/x-icon",
pdf: "application/pdf",
eml: "message/rfc822",
doc: "application/msword",
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
xls: "application/vnd.ms-excel",
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
ppt: "application/vnd.ms-powerpoint",
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
txt: "text/plain",
csv: "text/csv",
mp4: "video/mp4",
mov: "video/quicktime",
avi: "video/x-msvideo",
mkv: "video/x-matroska",
webm: "video/webm",
zip: "application/zip",
rar: "application/vnd.rar",
"7z": "application/x-7z-compressed",
tar: "application/x-tar",
mp3: "audio/mpeg",
};
export const getMimeType = (extension: TAllowedFileExtension): string => mimeTypes[extension];
/**
@@ -0,0 +1,79 @@
import { describe, expect, it } from "vitest";
import { z } from "zod";
// Used for parity check in tests only
import { validateEmail, validatePhone, validateUrl } from "./validation";
describe("Validation Logic Parity", () => {
describe("Email Validation", () => {
const zodEmail = z.string().email();
const testCases = [
"test@example.com",
"user.name+tag@example.co.uk",
"invalid-email",
"no-at-sign.com",
"@domain.com",
"user@",
"user@domain", // Zod might accept this or not depending on TLD requirement. Our regex requires TLD {2,}.
"user@domain.c", // Our regex requires 2 chars TLD.
];
testCases.forEach((email) => {
it(`should match Zod behavior for email: "${email}"`, () => {
const zodResult = zodEmail.safeParse(email).success;
const myResult = validateEmail(email);
// We aim for parity with Zod's default email validator.
// Our custom ReDoS-safe regex handles standard email formats correctly.
expect(myResult).toBe(zodResult);
});
});
});
describe("URL Validation", () => {
const zodUrl = z.string().url();
const testCases = [
"https://example.com",
"http://localhost:3000",
"ftp://files.com",
"invalid-url",
"examples",
"https://",
];
testCases.forEach((url) => {
it(`should match Zod behavior for URL: "${url}"`, () => {
const zodResult = zodUrl.safeParse(url).success;
const myResult = validateUrl(url);
if (zodResult) {
expect(myResult).toBe(true);
} else {
expect(myResult).toBe(false);
}
});
});
});
// Note: Phone validation in the component used a custom regex, not Zod's default.
// The original component code had: const phoneRegex = /^[0-9+][0-9+\- ]*[0-9]$/;
// So we just test that function directly.
describe("Phone Validation", () => {
const testCases = [
{ input: "+1234567890", expected: true },
{ input: "123-456-7890", expected: true },
{ input: "123 456 7890", expected: true },
{ input: "invalid", expected: false },
{ input: "123-", expected: false }, // ends with separator
{ input: "-123", expected: false }, // starts with separator
{ input: "+", expected: false },
];
testCases.forEach(({ input, expected }) => {
it(`should validate phone "${input}" as ${expected}`, () => {
expect(validatePhone(input)).toBe(expected);
});
});
});
});
+24
View File
@@ -0,0 +1,24 @@
export const validateEmail = (email: string): boolean => {
// ReDoS-safe email validation regex that closely matches Zod's/HTML5's permissive behavior.
// This avoids "super-linear runtime" warnings by sticking to specific allowed characters
// rather than negated character classes where possible, and avoiding nested quantifiers.
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(email);
};
export const validateUrl = (url: string): boolean => {
try {
// Use URL constructor for validation
new URL(url);
return true;
} catch {
return false;
}
};
export const validatePhone = (phone: string): boolean => {
// Match the same pattern: must start with digit or +, end with digit
// ReDoS safe: Avoids nested repetition.
const phoneRegex = /^[0-9+][0-9+\- ]*[0-9]$/;
return phoneRegex.test(phone);
};
+2
View File
@@ -6,6 +6,7 @@ import dts from "vite-plugin-dts";
import tsconfigPaths from "vite-tsconfig-paths";
import { defineConfig } from "vitest/config";
import { copyCompiledAssetsPlugin } from "../vite-plugins/copy-compiled-assets";
import { visualizer } from "rollup-plugin-visualizer";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -74,6 +75,7 @@ const config = ({ mode }) => {
dts({ rollupTypes: true }),
tsconfigPaths(),
copyCompiledAssetsPlugin({ filename: "surveys", distDir: resolve(__dirname, "dist") }),
process.env.ANALYZE === "true" && visualizer({ filename: resolve(__dirname, "stats.html"), open: false, gzipSize: true, brotliSize: true }),
],
});
};
+18
View File
@@ -0,0 +1,18 @@
// Element Type Enum (same as question types)
export enum TSurveyElementTypeEnum {
FileUpload = "fileUpload",
OpenText = "openText",
MultipleChoiceSingle = "multipleChoiceSingle",
MultipleChoiceMulti = "multipleChoiceMulti",
NPS = "nps",
CTA = "cta",
Rating = "rating",
Consent = "consent",
PictureSelection = "pictureSelection",
Cal = "cal",
Date = "date",
Matrix = "matrix",
Address = "address",
Ranking = "ranking",
ContactInfo = "contactInfo",
}
+11 -18
View File
@@ -2,26 +2,19 @@ import { z } from "zod";
import { ZUrl } from "../common";
import { ZI18nString } from "../i18n";
import { ZAllowedFileExtension } from "../storage";
import { TSurveyElementTypeEnum } from "./constants";
import { FORBIDDEN_IDS } from "./validation";
// Element Type Enum (same as question types)
export enum TSurveyElementTypeEnum {
FileUpload = "fileUpload",
OpenText = "openText",
MultipleChoiceSingle = "multipleChoiceSingle",
MultipleChoiceMulti = "multipleChoiceMulti",
NPS = "nps",
CTA = "cta",
Rating = "rating",
Consent = "consent",
PictureSelection = "pictureSelection",
Cal = "cal",
Date = "date",
Matrix = "matrix",
Address = "address",
Ranking = "ranking",
ContactInfo = "contactInfo",
}
/**
* RE-EXPORTING FOR BACKWARDS COMPATIBILITY AND CONVENIENCE
*
* TSurveyElementTypeEnum is defined in `constants.ts` to avoid circular dependencies
* and ensure that the Zod library is not included in bundles that only need the Enum value.
*
* However, we re-export it here so that most consumers (who also need the Zod schemas)
* can import everything from a single file (`elements.ts`).
*/
export { TSurveyElementTypeEnum };
// Element ID validation (same rules as questions - USER EDITABLE)
export const ZSurveyElementId = z.string().superRefine((id, ctx) => {
+10 -4
View File
@@ -3190,7 +3190,7 @@ const validateBlockConditions = (
path: ["blocks", blockIndex, "logic", logicIndex, "conditions"],
});
} else {
const validElementTypes = [TSurveyElementTypeEnum.OpenText];
const validElementTypes: TSurveyElementTypeEnum[] = [TSurveyElementTypeEnum.OpenText];
if (element.inputType === "number") {
validElementTypes.push(...[TSurveyElementTypeEnum.Rating, TSurveyElementTypeEnum.NPS]);
@@ -3397,7 +3397,10 @@ const validateBlockConditions = (
path: ["blocks", blockIndex, "logic", logicIndex, "conditions"],
});
} else {
const validElementTypes = [TSurveyElementTypeEnum.OpenText, TSurveyElementTypeEnum.Date];
const validElementTypes: TSurveyElementTypeEnum[] = [
TSurveyElementTypeEnum.OpenText,
TSurveyElementTypeEnum.Date,
];
if (!validElementTypes.includes(elem.data.type)) {
issues.push({
code: z.ZodIssueCode.custom,
@@ -3587,7 +3590,7 @@ const validateBlockActions = (
if (variable.type === "text") {
if (action.value.type === "element") {
const allowedElements = [
const allowedElements: TSurveyElementTypeEnum[] = [
TSurveyElementTypeEnum.OpenText,
TSurveyElementTypeEnum.MultipleChoiceSingle,
TSurveyElementTypeEnum.Rating,
@@ -3610,7 +3613,10 @@ const validateBlockActions = (
}
if (action.value.type === "element") {
const allowedElements = [TSurveyElementTypeEnum.Rating, TSurveyElementTypeEnum.NPS];
const allowedElements: TSurveyElementTypeEnum[] = [
TSurveyElementTypeEnum.Rating,
TSurveyElementTypeEnum.NPS,
];
const selectedElement = allElements.get(action.value.value);
+59
View File
@@ -1011,6 +1011,9 @@ importers:
postcss:
specifier: 8.5.3
version: 8.5.3
rollup-plugin-visualizer:
specifier: 6.0.5
version: 6.0.5(rollup@4.54.0)
tailwindcss:
specifier: 4.1.17
version: 4.1.17
@@ -3231,12 +3234,15 @@ packages:
'@otplib/plugin-crypto@12.0.1':
resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==}
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
'@otplib/plugin-thirty-two@12.0.1':
resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==}
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
'@otplib/preset-default@12.0.1':
resolution: {integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==}
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
'@otplib/preset-v11@12.0.1':
resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==}
@@ -6648,6 +6654,10 @@ packages:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
define-lazy-prop@2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
engines: {node: '>=8'}
define-lazy-prop@3.0.0:
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
engines: {node: '>=12'}
@@ -7777,6 +7787,11 @@ packages:
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
hasBin: true
is-docker@3.0.0:
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -7914,6 +7929,10 @@ packages:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
engines: {node: '>=12.13'}
is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
is-wsl@3.1.0:
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
engines: {node: '>=16'}
@@ -8839,6 +8858,10 @@ packages:
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
engines: {node: '>=18'}
open@8.4.2:
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
openid-client@5.7.1:
resolution: {integrity: sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==}
@@ -9638,6 +9661,19 @@ packages:
ripemd160@2.0.2:
resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
rollup-plugin-visualizer@6.0.5:
resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
rolldown: 1.x || ^1.0.0-beta
rollup: 2.x || 3.x || 4.x
peerDependenciesMeta:
rolldown:
optional: true
rollup:
optional: true
rollup@4.54.0:
resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -17957,6 +17993,8 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
define-lazy-prop@2.0.0: {}
define-lazy-prop@3.0.0: {}
define-properties@1.2.1:
@@ -19381,6 +19419,8 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-docker@2.2.1: {}
is-docker@3.0.0: {}
is-extglob@2.1.1: {}
@@ -19492,6 +19532,10 @@ snapshots:
is-what@4.1.16: {}
is-wsl@2.2.0:
dependencies:
is-docker: 2.2.1
is-wsl@3.1.0:
dependencies:
is-inside-container: 1.0.0
@@ -20441,6 +20485,12 @@ snapshots:
is-inside-container: 1.0.0
wsl-utils: 0.1.0
open@8.4.2:
dependencies:
define-lazy-prop: 2.0.0
is-docker: 2.2.1
is-wsl: 2.2.0
openid-client@5.7.1:
dependencies:
jose: 4.15.9
@@ -21283,6 +21333,15 @@ snapshots:
hash-base: 3.1.2
inherits: 2.0.4
rollup-plugin-visualizer@6.0.5(rollup@4.54.0):
dependencies:
open: 8.4.2
picomatch: 4.0.3
source-map: 0.7.6
yargs: 17.7.2
optionalDependencies:
rollup: 4.54.0
rollup@4.54.0:
dependencies:
'@types/estree': 1.0.8