fix: Restore uuid package on frontend (#10491)

* fix: Restore uuid package on frontend

* Remove legacy moduleNameMapper

* Add lint rule

* lint - getRandomValues can be used without SSL

* Update Comment.ts
This commit is contained in:
Tom Moor
2025-10-28 13:13:48 +01:00
committed by GitHub
parent cc6d2dc471
commit fa8d82d82a
20 changed files with 64 additions and 62 deletions
+2 -4
View File
@@ -20,8 +20,7 @@
"moduleNameMapper": { "moduleNameMapper": {
"^~/(.*)$": "<rootDir>/app/$1", "^~/(.*)$": "<rootDir>/app/$1",
"^@shared/(.*)$": "<rootDir>/shared/$1", "^@shared/(.*)$": "<rootDir>/shared/$1",
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js", "^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js"
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js"
}, },
"modulePaths": ["<rootDir>/app"], "modulePaths": ["<rootDir>/app"],
"setupFiles": ["<rootDir>/__mocks__/window.js"], "setupFiles": ["<rootDir>/__mocks__/window.js"],
@@ -48,8 +47,7 @@
"moduleNameMapper": { "moduleNameMapper": {
"^~/(.*)$": "<rootDir>/app/$1", "^~/(.*)$": "<rootDir>/app/$1",
"^@shared/(.*)$": "<rootDir>/shared/$1", "^@shared/(.*)$": "<rootDir>/shared/$1",
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js", "^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js"
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js"
}, },
"setupFiles": ["<rootDir>/__mocks__/window.js"], "setupFiles": ["<rootDir>/__mocks__/window.js"],
"testEnvironment": "jsdom", "testEnvironment": "jsdom",
+7
View File
@@ -5,6 +5,13 @@
{ {
"files": ["**/*.{jsx,tsx}"], "files": ["**/*.{jsx,tsx}"],
"rules": { "rules": {
"no-restricted-globals": [
"error",
{
"name": "crypto",
"message": "Do not use, does not work in environments without SSL."
}
],
"no-restricted-imports": [ "no-restricted-imports": [
"error", "error",
{ {
+7 -6
View File
@@ -1,4 +1,5 @@
import { LocationDescriptor } from "history"; import { LocationDescriptor } from "history";
import { v4 as uuidv4 } from "uuid";
import flattenDeep from "lodash/flattenDeep"; import flattenDeep from "lodash/flattenDeep";
import { toast } from "sonner"; import { toast } from "sonner";
import { Optional } from "utility-types"; import { Optional } from "utility-types";
@@ -45,7 +46,7 @@ export function createAction(definition: Optional<Action, "id">): Action {
return definition.perform?.(context); return definition.perform?.(context);
} }
: undefined, : undefined,
id: definition.id ?? crypto.randomUUID(), id: definition.id ?? uuidv4(),
}; };
} }
@@ -201,7 +202,7 @@ export function createActionV2(
return definition.perform(context); return definition.perform(context);
} }
: () => {}, : () => {},
id: definition.id ?? crypto.randomUUID(), id: definition.id ?? uuidv4(),
}; };
} }
@@ -212,7 +213,7 @@ export function createInternalLinkActionV2(
...definition, ...definition,
type: "action", type: "action",
variant: "internal_link", variant: "internal_link",
id: definition.id ?? crypto.randomUUID(), id: definition.id ?? uuidv4(),
}; };
} }
@@ -223,7 +224,7 @@ export function createExternalLinkActionV2(
...definition, ...definition,
type: "action", type: "action",
variant: "external_link", variant: "external_link",
id: definition.id ?? crypto.randomUUID(), id: definition.id ?? uuidv4(),
}; };
} }
@@ -234,7 +235,7 @@ export function createActionV2WithChildren(
...definition, ...definition,
type: "action", type: "action",
variant: "action_with_children", variant: "action_with_children",
id: definition.id ?? crypto.randomUUID(), id: definition.id ?? uuidv4(),
}; };
} }
@@ -251,7 +252,7 @@ export function createRootMenuAction(
actions: (ActionV2Variant | ActionV2Group | TActionV2Separator)[] actions: (ActionV2Variant | ActionV2Group | TActionV2Separator)[]
): ActionV2WithChildren { ): ActionV2WithChildren {
return { return {
id: crypto.randomUUID(), id: uuidv4(),
type: "action", type: "action",
variant: "action_with_children", variant: "action_with_children",
name: "root_action", name: "root_action",
+7 -6
View File
@@ -1,5 +1,6 @@
import { isEmail } from "class-validator"; import { isEmail } from "class-validator";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { v4 as uuidv4 } from "uuid";
import { DocumentIcon, PlusIcon, CollectionIcon } from "outline-icons"; import { DocumentIcon, PlusIcon, CollectionIcon } from "outline-icons";
import { useState, useCallback, useEffect } from "react"; import { useState, useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -99,7 +100,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: UserSection, section: UserSection,
appendSpace: true, appendSpace: true,
attrs: { attrs: {
id: crypto.randomUUID(), id: uuidv4(),
type: MentionType.User, type: MentionType.User,
modelId: user.id, modelId: user.id,
actorId, actorId,
@@ -125,7 +126,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: GroupSection, section: GroupSection,
appendSpace: true, appendSpace: true,
attrs: { attrs: {
id: crypto.randomUUID(), id: uuidv4(),
type: MentionType.Group, type: MentionType.Group,
modelId: group.id, modelId: group.id,
actorId, actorId,
@@ -157,7 +158,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: DocumentsSection, section: DocumentsSection,
appendSpace: true, appendSpace: true,
attrs: { attrs: {
id: crypto.randomUUID(), id: uuidv4(),
type: MentionType.Document, type: MentionType.Document,
modelId: doc.id, modelId: doc.id,
actorId, actorId,
@@ -185,7 +186,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: CollectionsSection, section: CollectionsSection,
appendSpace: true, appendSpace: true,
attrs: { attrs: {
id: crypto.randomUUID(), id: uuidv4(),
type: MentionType.Collection, type: MentionType.Collection,
modelId: collection.id, modelId: collection.id,
actorId, actorId,
@@ -205,9 +206,9 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
priority: -1, priority: -1,
appendSpace: true, appendSpace: true,
attrs: { attrs: {
id: crypto.randomUUID(), id: uuidv4(),
type: MentionType.Document, type: MentionType.Document,
modelId: crypto.randomUUID(), modelId: uuidv4(),
actorId, actorId,
label: search, label: search,
}, },
+3 -2
View File
@@ -1,4 +1,5 @@
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { v4 as uuidv4 } from "uuid";
import { EmailIcon, LinkIcon } from "outline-icons"; import { EmailIcon, LinkIcon } from "outline-icons";
import React, { useCallback } from "react"; import React, { useCallback } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -101,11 +102,11 @@ function useItems({
icon: <EmailIcon />, icon: <EmailIcon />,
visible: !!mentionType, visible: !!mentionType,
attrs: { attrs: {
id: crypto.randomUUID(), id: uuidv4(),
type: mentionType, type: mentionType,
label: pastedText, label: pastedText,
href: pastedText, href: pastedText,
modelId: crypto.randomUUID(), modelId: uuidv4(),
actorId: user?.id, actorId: user?.id,
}, },
appendSpace: true, appendSpace: true,
+3 -2
View File
@@ -1,4 +1,5 @@
import { action, observable } from "mobx"; import { action, observable } from "mobx";
import { v4 as uuidv4 } from "uuid";
import { toggleMark } from "prosemirror-commands"; import { toggleMark } from "prosemirror-commands";
import { Node, Slice } from "prosemirror-model"; import { Node, Slice } from "prosemirror-model";
import { import {
@@ -143,7 +144,7 @@ export default class PasteHandler extends Extension {
type: MentionType.Document, type: MentionType.Document,
modelId: document.id, modelId: document.id,
label: document.titleWithDefault, label: document.titleWithDefault,
id: crypto.randomUUID(), id: uuidv4(),
}) })
) )
); );
@@ -188,7 +189,7 @@ export default class PasteHandler extends Extension {
type: MentionType.Collection, type: MentionType.Collection,
modelId: collection.id, modelId: collection.id,
label: collection.name, label: collection.name,
id: crypto.randomUUID(), id: uuidv4(),
}) })
) )
); );
@@ -1,4 +1,5 @@
import * as VisuallyHidden from "@radix-ui/react-visually-hidden"; import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
import { v4 as uuidv4 } from "uuid";
import { m } from "framer-motion"; import { m } from "framer-motion";
import { action } from "mobx"; import { action } from "mobx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
@@ -160,7 +161,7 @@ function CommentForm({
comments comments
); );
comment.id = crypto.randomUUID(); comment.id = uuidv4();
comments.add(comment); comments.add(comment);
comment comment
+2 -1
View File
@@ -1,4 +1,5 @@
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { v4 as uuidv4 } from "uuid";
import queryString from "query-string"; import queryString from "query-string";
import * as React from "react"; import * as React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -104,7 +105,7 @@ function Search() {
// without a flash of loading. // without a flash of loading.
if (query) { if (query) {
searches.add({ searches.add({
id: crypto.randomUUID(), id: uuidv4(),
query, query,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}); });
+2 -1
View File
@@ -1,4 +1,5 @@
import { observable, action } from "mobx"; import { observable, action } from "mobx";
import { v4 as uuidv4 } from "uuid";
import * as React from "react"; import * as React from "react";
type DialogDefinition = { type DialogDefinition = {
@@ -65,7 +66,7 @@ export default class DialogsStore {
this.modalStack.clear(); this.modalStack.clear();
} }
this.modalStack.set(id ?? replaceId ?? crypto.randomUUID(), { this.modalStack.set(id ?? replaceId ?? uuidv4(), {
title, title,
content, content,
style, style,
+1
View File
@@ -262,6 +262,7 @@
"ukkonen": "^2.2.0", "ukkonen": "^2.2.0",
"umzug": "^3.8.2", "umzug": "^3.8.2",
"utility-types": "^3.11.0", "utility-types": "^3.11.0",
"uuid": "^11.1.0",
"validator": "13.15.20", "validator": "13.15.20",
"vaul": "^1.1.2", "vaul": "^1.1.2",
"vite": "npm:rolldown-vite@latest", "vite": "npm:rolldown-vite@latest",
+7
View File
@@ -5,6 +5,13 @@
{ {
"files": ["**/*.{js,jsx,ts,tsx}"], "files": ["**/*.{js,jsx,ts,tsx}"],
"rules": { "rules": {
"no-restricted-globals": [
"error",
{
"name": "crypto",
"message": "Do not use, does not work in environments without SSL."
}
],
"no-restricted-imports": [ "no-restricted-imports": [
"error", "error",
{ {
+3 -2
View File
@@ -1,5 +1,6 @@
import { Attrs } from "prosemirror-model"; import { Attrs } from "prosemirror-model";
import { Command, NodeSelection, TextSelection } from "prosemirror-state"; import { Command, NodeSelection, TextSelection } from "prosemirror-state";
import { v4 as uuidv4 } from "uuid";
import { isMarkActive } from "../queries/isMarkActive"; import { isMarkActive } from "../queries/isMarkActive";
import { chainTransactions } from "../lib/chainTransactions"; import { chainTransactions } from "../lib/chainTransactions";
import { addMark } from "./addMark"; import { addMark } from "./addMark";
@@ -20,7 +21,7 @@ const addCommentNodeSelection =
const newMark = { const newMark = {
type: "comment", type: "comment",
attrs: { attrs: {
id: crypto.randomUUID(), id: uuidv4(),
userId: attrs.userId, userId: attrs.userId,
draft: true, draft: true,
}, },
@@ -54,7 +55,7 @@ const addCommentTextSelection =
chainTransactions( chainTransactions(
addMark(state.schema.marks.comment, { addMark(state.schema.marks.comment, {
id: crypto.randomUUID(), id: uuidv4(),
userId: attrs.userId, userId: attrs.userId,
draft: true, draft: true,
}), }),
+2 -1
View File
@@ -1,4 +1,5 @@
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import { v4 as uuidv4 } from "uuid";
import { EditorView } from "prosemirror-view"; import { EditorView } from "prosemirror-view";
import { toast } from "sonner"; import { toast } from "sonner";
import type { Dictionary } from "~/hooks/useDictionary"; import type { Dictionary } from "~/hooks/useDictionary";
@@ -71,7 +72,7 @@ const insertFiles = async function (
: undefined; : undefined;
return { return {
id: `upload-${crypto.randomUUID()}`, id: `upload-${uuidv4()}`,
dimensions: await getDimensions?.(file), dimensions: await getDimensions?.(file),
isImage, isImage,
isVideo, isVideo,
+2 -1
View File
@@ -1,6 +1,7 @@
import debounce from "lodash/debounce"; import debounce from "lodash/debounce";
import last from "lodash/last"; import last from "lodash/last";
import sortBy from "lodash/sortBy"; import sortBy from "lodash/sortBy";
import { v4 as uuidv4 } from "uuid";
import type MermaidUnsafe from "mermaid"; import type MermaidUnsafe from "mermaid";
import { Node } from "prosemirror-model"; import { Node } from "prosemirror-model";
import { import {
@@ -53,7 +54,7 @@ class MermaidRenderer {
readonly editor: Editor; readonly editor: Editor;
constructor(editor: Editor) { constructor(editor: Editor) {
this.diagramId = crypto.randomUUID(); this.diagramId = uuidv4();
this.elementId = `mermaid-diagram-wrapper-${this.diagramId}`; this.elementId = `mermaid-diagram-wrapper-${this.diagramId}`;
this.element = this.element =
document.getElementById(this.elementId) || document.createElement("div"); document.getElementById(this.elementId) || document.createElement("div");
+3 -2
View File
@@ -1,5 +1,6 @@
import { Node, Schema } from "prosemirror-model"; import { Node, Schema } from "prosemirror-model";
import { Primitive } from "utility-types"; import { Primitive } from "utility-types";
import { v4 as uuidv4 } from "uuid";
import { isList } from "../queries/isList"; import { isList } from "../queries/isList";
export function transformListToMentions( export function transformListToMentions(
@@ -33,11 +34,11 @@ function transformListItemToMentions(
node.type.create( node.type.create(
node.attrs, node.attrs,
schema.nodes.mention.create({ schema.nodes.mention.create({
id: crypto.randomUUID(), id: uuidv4(),
type: mentionType, type: mentionType,
label: link, label: link,
href: link, href: link,
modelId: crypto.randomUUID(), modelId: uuidv4(),
actorId: attrs.actorId, actorId: attrs.actorId,
}) })
) )
+3 -2
View File
@@ -1,12 +1,13 @@
import { toggleMark } from "prosemirror-commands"; import { toggleMark } from "prosemirror-commands";
import { MarkSpec, MarkType, Mark as PMMark } from "prosemirror-model"; import { MarkSpec, MarkType, Mark as PMMark } from "prosemirror-model";
import { Command, Plugin } from "prosemirror-state"; import { Command, Plugin } from "prosemirror-state";
import { v4 as uuidv4 } from "uuid";
import { collapseSelection } from "../commands/collapseSelection"; import { collapseSelection } from "../commands/collapseSelection";
import { addComment } from "../commands/comment";
import { chainTransactions } from "../lib/chainTransactions"; import { chainTransactions } from "../lib/chainTransactions";
import { isMarkActive } from "../queries/isMarkActive"; import { isMarkActive } from "../queries/isMarkActive";
import { EditorStyleHelper } from "../styles/EditorStyleHelper"; import { EditorStyleHelper } from "../styles/EditorStyleHelper";
import Mark from "./Mark"; import Mark from "./Mark";
import { addComment } from "../commands/comment";
export default class Comment extends Mark { export default class Comment extends Mark {
get name() { get name() {
@@ -81,7 +82,7 @@ export default class Comment extends Mark {
chainTransactions( chainTransactions(
toggleMark(type, { toggleMark(type, {
id: crypto.randomUUID(), id: uuidv4(),
userId: this.options.userId, userId: this.options.userId,
draft: true, draft: true,
}), }),
+2 -1
View File
@@ -5,6 +5,7 @@ import {
sinkListItem, sinkListItem,
liftListItem, liftListItem,
} from "prosemirror-schema-list"; } from "prosemirror-schema-list";
import { v4 as uuidv4 } from "uuid";
import toggleCheckboxItem from "../commands/toggleCheckboxItem"; import toggleCheckboxItem from "../commands/toggleCheckboxItem";
import { MarkdownSerializerState } from "../lib/markdown/serializer"; import { MarkdownSerializerState } from "../lib/markdown/serializer";
import checkboxRule from "../rules/checkboxes"; import checkboxRule from "../rules/checkboxes";
@@ -34,7 +35,7 @@ export default class CheckboxItem extends Node {
}, },
], ],
toDOM: (node) => { toDOM: (node) => {
const id = `checkbox-${crypto.randomUUID()}`; const id = `checkbox-${uuidv4()}`;
const checked = node.attrs.checked.toString(); const checked = node.attrs.checked.toString();
let input; let input;
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
+2 -1
View File
@@ -13,6 +13,7 @@ import {
TextSelection, TextSelection,
} from "prosemirror-state"; } from "prosemirror-state";
import { Primitive } from "utility-types"; import { Primitive } from "utility-types";
import { v4 as uuidv4 } from "uuid";
import env from "../../env"; import env from "../../env";
import { MentionType, UnfurlResourceType, UnfurlResponse } from "../../types"; import { MentionType, UnfurlResourceType, UnfurlResponse } from "../../types";
import { import {
@@ -178,7 +179,7 @@ export default class Mention extends Node {
node.type.name === this.name && node.type.name === this.name &&
(!nodeId || existingIds.has(nodeId)) (!nodeId || existingIds.has(nodeId))
) { ) {
nodeId = crypto.randomUUID(); nodeId = uuidv4();
modified = true; modified = true;
tr.setNodeAttribute(pos, "id", nodeId); tr.setNodeAttribute(pos, "id", nodeId);
} }
+1
View File
@@ -61,6 +61,7 @@ export const randomString = (options: number | RandomStringOptions) => {
: lowercase + uppercase + numeric; : lowercase + uppercase + numeric;
const array = new Uint8Array(length); const array = new Uint8Array(length);
// oxlint-disable-next-line no-restricted-globals
crypto.getRandomValues(array); crypto.getRandomValues(array);
return Array.from(array, (x) => chars[x % chars.length]).join(""); return Array.from(array, (x) => chars[x % chars.length]).join("");
}; };
+3 -29
View File
@@ -753,20 +753,7 @@
lru-cache "^5.1.1" lru-cache "^5.1.1"
semver "^6.3.1" semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3": "@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3", "@babel/helper-create-class-features-plugin@^7.28.5":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz#3e747434ea007910c320c4d39a6b46f20f371d46"
integrity sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.27.3"
"@babel/helper-member-expression-to-functions" "^7.27.1"
"@babel/helper-optimise-call-expression" "^7.27.1"
"@babel/helper-replace-supers" "^7.27.1"
"@babel/helper-skip-transparent-expression-wrappers" "^7.27.1"
"@babel/traverse" "^7.28.3"
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.28.5":
version "7.28.5" version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46"
integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==
@@ -818,15 +805,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
"@babel/helper-member-expression-to-functions@^7.27.1": "@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44"
integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==
dependencies:
"@babel/traverse" "^7.27.1"
"@babel/types" "^7.27.1"
"@babel/helper-member-expression-to-functions@^7.28.5":
version "7.28.5" version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150"
integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==
@@ -894,12 +873,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.27.1": "@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
"@babel/helper-validator-identifier@^7.28.5":
version "7.28.5" version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==