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

View File

@@ -20,8 +20,7 @@
"moduleNameMapper": {
"^~/(.*)$": "<rootDir>/app/$1",
"^@shared/(.*)$": "<rootDir>/shared/$1",
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js",
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js"
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js"
},
"modulePaths": ["<rootDir>/app"],
"setupFiles": ["<rootDir>/__mocks__/window.js"],
@@ -48,8 +47,7 @@
"moduleNameMapper": {
"^~/(.*)$": "<rootDir>/app/$1",
"^@shared/(.*)$": "<rootDir>/shared/$1",
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js",
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js"
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js"
},
"setupFiles": ["<rootDir>/__mocks__/window.js"],
"testEnvironment": "jsdom",

View File

@@ -5,6 +5,13 @@
{
"files": ["**/*.{jsx,tsx}"],
"rules": {
"no-restricted-globals": [
"error",
{
"name": "crypto",
"message": "Do not use, does not work in environments without SSL."
}
],
"no-restricted-imports": [
"error",
{

View File

@@ -1,4 +1,5 @@
import { LocationDescriptor } from "history";
import { v4 as uuidv4 } from "uuid";
import flattenDeep from "lodash/flattenDeep";
import { toast } from "sonner";
import { Optional } from "utility-types";
@@ -45,7 +46,7 @@ export function createAction(definition: Optional<Action, "id">): Action {
return definition.perform?.(context);
}
: undefined,
id: definition.id ?? crypto.randomUUID(),
id: definition.id ?? uuidv4(),
};
}
@@ -201,7 +202,7 @@ export function createActionV2(
return definition.perform(context);
}
: () => {},
id: definition.id ?? crypto.randomUUID(),
id: definition.id ?? uuidv4(),
};
}
@@ -212,7 +213,7 @@ export function createInternalLinkActionV2(
...definition,
type: "action",
variant: "internal_link",
id: definition.id ?? crypto.randomUUID(),
id: definition.id ?? uuidv4(),
};
}
@@ -223,7 +224,7 @@ export function createExternalLinkActionV2(
...definition,
type: "action",
variant: "external_link",
id: definition.id ?? crypto.randomUUID(),
id: definition.id ?? uuidv4(),
};
}
@@ -234,7 +235,7 @@ export function createActionV2WithChildren(
...definition,
type: "action",
variant: "action_with_children",
id: definition.id ?? crypto.randomUUID(),
id: definition.id ?? uuidv4(),
};
}
@@ -251,7 +252,7 @@ export function createRootMenuAction(
actions: (ActionV2Variant | ActionV2Group | TActionV2Separator)[]
): ActionV2WithChildren {
return {
id: crypto.randomUUID(),
id: uuidv4(),
type: "action",
variant: "action_with_children",
name: "root_action",

View File

@@ -1,5 +1,6 @@
import { isEmail } from "class-validator";
import { observer } from "mobx-react";
import { v4 as uuidv4 } from "uuid";
import { DocumentIcon, PlusIcon, CollectionIcon } from "outline-icons";
import { useState, useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
@@ -99,7 +100,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: UserSection,
appendSpace: true,
attrs: {
id: crypto.randomUUID(),
id: uuidv4(),
type: MentionType.User,
modelId: user.id,
actorId,
@@ -125,7 +126,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: GroupSection,
appendSpace: true,
attrs: {
id: crypto.randomUUID(),
id: uuidv4(),
type: MentionType.Group,
modelId: group.id,
actorId,
@@ -157,7 +158,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: DocumentsSection,
appendSpace: true,
attrs: {
id: crypto.randomUUID(),
id: uuidv4(),
type: MentionType.Document,
modelId: doc.id,
actorId,
@@ -185,7 +186,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
section: CollectionsSection,
appendSpace: true,
attrs: {
id: crypto.randomUUID(),
id: uuidv4(),
type: MentionType.Collection,
modelId: collection.id,
actorId,
@@ -205,9 +206,9 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
priority: -1,
appendSpace: true,
attrs: {
id: crypto.randomUUID(),
id: uuidv4(),
type: MentionType.Document,
modelId: crypto.randomUUID(),
modelId: uuidv4(),
actorId,
label: search,
},

View File

@@ -1,4 +1,5 @@
import { observer } from "mobx-react";
import { v4 as uuidv4 } from "uuid";
import { EmailIcon, LinkIcon } from "outline-icons";
import React, { useCallback } from "react";
import { useTranslation } from "react-i18next";
@@ -101,11 +102,11 @@ function useItems({
icon: <EmailIcon />,
visible: !!mentionType,
attrs: {
id: crypto.randomUUID(),
id: uuidv4(),
type: mentionType,
label: pastedText,
href: pastedText,
modelId: crypto.randomUUID(),
modelId: uuidv4(),
actorId: user?.id,
},
appendSpace: true,

View File

@@ -1,4 +1,5 @@
import { action, observable } from "mobx";
import { v4 as uuidv4 } from "uuid";
import { toggleMark } from "prosemirror-commands";
import { Node, Slice } from "prosemirror-model";
import {
@@ -143,7 +144,7 @@ export default class PasteHandler extends Extension {
type: MentionType.Document,
modelId: document.id,
label: document.titleWithDefault,
id: crypto.randomUUID(),
id: uuidv4(),
})
)
);
@@ -188,7 +189,7 @@ export default class PasteHandler extends Extension {
type: MentionType.Collection,
modelId: collection.id,
label: collection.name,
id: crypto.randomUUID(),
id: uuidv4(),
})
)
);

View File

@@ -1,4 +1,5 @@
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
import { v4 as uuidv4 } from "uuid";
import { m } from "framer-motion";
import { action } from "mobx";
import { observer } from "mobx-react";
@@ -160,7 +161,7 @@ function CommentForm({
comments
);
comment.id = crypto.randomUUID();
comment.id = uuidv4();
comments.add(comment);
comment

View File

@@ -1,4 +1,5 @@
import { observer } from "mobx-react";
import { v4 as uuidv4 } from "uuid";
import queryString from "query-string";
import * as React from "react";
import { useTranslation } from "react-i18next";
@@ -104,7 +105,7 @@ function Search() {
// without a flash of loading.
if (query) {
searches.add({
id: crypto.randomUUID(),
id: uuidv4(),
query,
createdAt: new Date().toISOString(),
});

View File

@@ -1,4 +1,5 @@
import { observable, action } from "mobx";
import { v4 as uuidv4 } from "uuid";
import * as React from "react";
type DialogDefinition = {
@@ -65,7 +66,7 @@ export default class DialogsStore {
this.modalStack.clear();
}
this.modalStack.set(id ?? replaceId ?? crypto.randomUUID(), {
this.modalStack.set(id ?? replaceId ?? uuidv4(), {
title,
content,
style,

View File

@@ -262,6 +262,7 @@
"ukkonen": "^2.2.0",
"umzug": "^3.8.2",
"utility-types": "^3.11.0",
"uuid": "^11.1.0",
"validator": "13.15.20",
"vaul": "^1.1.2",
"vite": "npm:rolldown-vite@latest",

View File

@@ -5,6 +5,13 @@
{
"files": ["**/*.{js,jsx,ts,tsx}"],
"rules": {
"no-restricted-globals": [
"error",
{
"name": "crypto",
"message": "Do not use, does not work in environments without SSL."
}
],
"no-restricted-imports": [
"error",
{

View File

@@ -1,5 +1,6 @@
import { Attrs } from "prosemirror-model";
import { Command, NodeSelection, TextSelection } from "prosemirror-state";
import { v4 as uuidv4 } from "uuid";
import { isMarkActive } from "../queries/isMarkActive";
import { chainTransactions } from "../lib/chainTransactions";
import { addMark } from "./addMark";
@@ -20,7 +21,7 @@ const addCommentNodeSelection =
const newMark = {
type: "comment",
attrs: {
id: crypto.randomUUID(),
id: uuidv4(),
userId: attrs.userId,
draft: true,
},
@@ -54,7 +55,7 @@ const addCommentTextSelection =
chainTransactions(
addMark(state.schema.marks.comment, {
id: crypto.randomUUID(),
id: uuidv4(),
userId: attrs.userId,
draft: true,
}),

View File

@@ -1,4 +1,5 @@
import * as Sentry from "@sentry/react";
import { v4 as uuidv4 } from "uuid";
import { EditorView } from "prosemirror-view";
import { toast } from "sonner";
import type { Dictionary } from "~/hooks/useDictionary";
@@ -71,7 +72,7 @@ const insertFiles = async function (
: undefined;
return {
id: `upload-${crypto.randomUUID()}`,
id: `upload-${uuidv4()}`,
dimensions: await getDimensions?.(file),
isImage,
isVideo,

View File

@@ -1,6 +1,7 @@
import debounce from "lodash/debounce";
import last from "lodash/last";
import sortBy from "lodash/sortBy";
import { v4 as uuidv4 } from "uuid";
import type MermaidUnsafe from "mermaid";
import { Node } from "prosemirror-model";
import {
@@ -53,7 +54,7 @@ class MermaidRenderer {
readonly editor: Editor;
constructor(editor: Editor) {
this.diagramId = crypto.randomUUID();
this.diagramId = uuidv4();
this.elementId = `mermaid-diagram-wrapper-${this.diagramId}`;
this.element =
document.getElementById(this.elementId) || document.createElement("div");

View File

@@ -1,5 +1,6 @@
import { Node, Schema } from "prosemirror-model";
import { Primitive } from "utility-types";
import { v4 as uuidv4 } from "uuid";
import { isList } from "../queries/isList";
export function transformListToMentions(
@@ -33,11 +34,11 @@ function transformListItemToMentions(
node.type.create(
node.attrs,
schema.nodes.mention.create({
id: crypto.randomUUID(),
id: uuidv4(),
type: mentionType,
label: link,
href: link,
modelId: crypto.randomUUID(),
modelId: uuidv4(),
actorId: attrs.actorId,
})
)

View File

@@ -1,12 +1,13 @@
import { toggleMark } from "prosemirror-commands";
import { MarkSpec, MarkType, Mark as PMMark } from "prosemirror-model";
import { Command, Plugin } from "prosemirror-state";
import { v4 as uuidv4 } from "uuid";
import { collapseSelection } from "../commands/collapseSelection";
import { addComment } from "../commands/comment";
import { chainTransactions } from "../lib/chainTransactions";
import { isMarkActive } from "../queries/isMarkActive";
import { EditorStyleHelper } from "../styles/EditorStyleHelper";
import Mark from "./Mark";
import { addComment } from "../commands/comment";
export default class Comment extends Mark {
get name() {
@@ -81,7 +82,7 @@ export default class Comment extends Mark {
chainTransactions(
toggleMark(type, {
id: crypto.randomUUID(),
id: uuidv4(),
userId: this.options.userId,
draft: true,
}),

View File

@@ -5,6 +5,7 @@ import {
sinkListItem,
liftListItem,
} from "prosemirror-schema-list";
import { v4 as uuidv4 } from "uuid";
import toggleCheckboxItem from "../commands/toggleCheckboxItem";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import checkboxRule from "../rules/checkboxes";
@@ -34,7 +35,7 @@ export default class CheckboxItem extends Node {
},
],
toDOM: (node) => {
const id = `checkbox-${crypto.randomUUID()}`;
const id = `checkbox-${uuidv4()}`;
const checked = node.attrs.checked.toString();
let input;
if (typeof document !== "undefined") {

View File

@@ -13,6 +13,7 @@ import {
TextSelection,
} from "prosemirror-state";
import { Primitive } from "utility-types";
import { v4 as uuidv4 } from "uuid";
import env from "../../env";
import { MentionType, UnfurlResourceType, UnfurlResponse } from "../../types";
import {
@@ -178,7 +179,7 @@ export default class Mention extends Node {
node.type.name === this.name &&
(!nodeId || existingIds.has(nodeId))
) {
nodeId = crypto.randomUUID();
nodeId = uuidv4();
modified = true;
tr.setNodeAttribute(pos, "id", nodeId);
}

View File

@@ -61,6 +61,7 @@ export const randomString = (options: number | RandomStringOptions) => {
: lowercase + uppercase + numeric;
const array = new Uint8Array(length);
// oxlint-disable-next-line no-restricted-globals
crypto.getRandomValues(array);
return Array.from(array, (x) => chars[x % chars.length]).join("");
};

View File

@@ -753,20 +753,7 @@
lru-cache "^5.1.1"
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3":
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":
"@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.5"
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==
@@ -818,15 +805,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
"@babel/helper-member-expression-to-functions@^7.27.1":
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":
"@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^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"
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"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.27.1":
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":
"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5":
version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==