From cfaee93af904db1488a31b2cdfc701269d648a49 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:28:22 +0000 Subject: [PATCH 01/44] fix --- apps/server/src/core/search/search.service.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/server/src/core/search/search.service.ts b/apps/server/src/core/search/search.service.ts index 01d7fbb1..9883b265 100644 --- a/apps/server/src/core/search/search.service.ts +++ b/apps/server/src/core/search/search.service.ts @@ -91,9 +91,15 @@ export class SearchService { return { items: [] }; } + const isRestricted = + await this.pagePermissionRepo.hasRestrictedAncestor(share.pageId); + if (isRestricted) { + return { items: [] }; + } + const pageIdsToSearch = []; if (share.includeSubPages) { - const pageList = await this.pageRepo.getPageAndDescendants( + const pageList = await this.pageRepo.getPageAndDescendantsExcludingRestricted( share.pageId, { includeContent: false, From cc5c8002382cf7deb46eebae5d73194e6148914c Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:29:09 +0000 Subject: [PATCH 02/44] 0.70.3 --- apps/client/package.json | 2 +- apps/server/package.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/client/package.json b/apps/client/package.json index 82155b40..e54d65f7 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -1,7 +1,7 @@ { "name": "client", "private": true, - "version": "0.70.2", + "version": "0.70.3", "scripts": { "dev": "vite", "build": "tsc && vite build", diff --git a/apps/server/package.json b/apps/server/package.json index 8586b7f0..0a5fe355 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "0.70.2", + "version": "0.70.3", "description": "", "author": "", "private": true, diff --git a/package.json b/package.json index c1c0c1f5..6b51c8b1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "docmost", "homepage": "https://docmost.com", - "version": "0.70.2", + "version": "0.70.3", "private": true, "scripts": { "build": "nx run-many -t build", From 6683c515cf395851f38d48e83808f2d96896579c Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:40:22 +0000 Subject: [PATCH 03/44] fix: make codeblock language detection performant (#2032) * fix: make codeblock language detection performant * lint --- .../lib/custom-code-block/lowlight-plugin.ts | 151 +++++++++++------- 1 file changed, 97 insertions(+), 54 deletions(-) diff --git a/packages/editor-ext/src/lib/custom-code-block/lowlight-plugin.ts b/packages/editor-ext/src/lib/custom-code-block/lowlight-plugin.ts index 505b8f20..ad80cee9 100644 --- a/packages/editor-ext/src/lib/custom-code-block/lowlight-plugin.ts +++ b/packages/editor-ext/src/lib/custom-code-block/lowlight-plugin.ts @@ -1,80 +1,111 @@ -import { findChildren } from '@tiptap/core' -import type { Node as ProsemirrorNode } from '@tiptap/pm/model' -import { Plugin, PluginKey } from '@tiptap/pm/state' -import { Decoration, DecorationSet } from '@tiptap/pm/view' +import { findChildren } from '@tiptap/core'; +import type { Node as ProsemirrorNode } from '@tiptap/pm/model'; +import { Plugin, PluginKey } from '@tiptap/pm/state'; +import { Decoration, DecorationSet } from '@tiptap/pm/view'; // @ts-ignore -import highlight from 'highlight.js/lib/core' +import highlight from 'highlight.js/lib/core'; -function parseNodes(nodes: any[], className: string[] = []): { text: string; classes: string[] }[] { +function parseNodes( + nodes: any[], + className: string[] = [], +): { text: string; classes: string[] }[] { return nodes - .map(node => { - const classes = [...className, ...(node.properties ? node.properties.className : [])] + .map((node) => { + const classes = [ + ...className, + ...(node.properties ? node.properties.className : []), + ]; if (node.children) { - return parseNodes(node.children, classes) + return parseNodes(node.children, classes); } return { text: node.value, classes, - } + }; }) - .flat() + .flat(); } function getHighlightNodes(result: any) { // `.value` for lowlight v1, `.children` for lowlight v2 - return result.value || result.children || [] + return result.value || result.children || []; } function registered(aliasOrLanguage: string) { - return Boolean(highlight.getLanguage(aliasOrLanguage)) + return Boolean(highlight.getLanguage(aliasOrLanguage)); } +// Max characters to sample for auto-detection to avoid performance issues with large code blocks +const AUTO_DETECT_SAMPLE_SIZE = 3000; + function getDecorations({ doc, name, lowlight, defaultLanguage, }: { - doc: ProsemirrorNode - name: string - lowlight: any - defaultLanguage: string | null | undefined + doc: ProsemirrorNode; + name: string; + lowlight: any; + defaultLanguage: string | null | undefined; }) { - const decorations: Decoration[] = [] + const decorations: Decoration[] = []; - findChildren(doc, node => node.type.name === name).forEach(block => { - let from = block.pos + 1 - const language = block.node.attrs.language || defaultLanguage - const languages = lowlight.listLanguages() + findChildren(doc, (node) => node.type.name === name).forEach((block) => { + let from = block.pos + 1; + const language = block.node.attrs.language || defaultLanguage; + const languages = lowlight.listLanguages(); + const textContent = block.node.textContent; - const nodes = - language && (languages.includes(language) || registered(language) || lowlight.registered?.(language)) - ? getHighlightNodes(lowlight.highlight(language, block.node.textContent)) - : getHighlightNodes(lowlight.highlightAuto(block.node.textContent)) + let nodes; + if ( + language && + (languages.includes(language) || + registered(language) || + lowlight.registered?.(language)) + ) { + nodes = getHighlightNodes(lowlight.highlight(language, textContent)); + } else { + // For auto-detection, sample a limited portion to detect the language, + // then highlight the full content with the detected language + const sample = + textContent.length > AUTO_DETECT_SAMPLE_SIZE + ? textContent.slice(0, AUTO_DETECT_SAMPLE_SIZE) + : textContent; + const autoResult = lowlight.highlightAuto(sample); + const detectedLanguage = autoResult.data?.language; + if (detectedLanguage && textContent.length > AUTO_DETECT_SAMPLE_SIZE) { + nodes = getHighlightNodes( + lowlight.highlight(detectedLanguage, textContent), + ); + } else { + nodes = getHighlightNodes(autoResult); + } + } - parseNodes(nodes).forEach(node => { - const to = from + node.text.length + parseNodes(nodes).forEach((node) => { + const to = from + node.text.length; if (node.classes.length) { const decoration = Decoration.inline(from, to, { class: node.classes.join(' '), - }) + }); - decorations.push(decoration) + decorations.push(decoration); } - from = to - }) - }) + from = to; + }); + }); - return DecorationSet.create(doc, decorations) + return DecorationSet.create(doc, decorations); } // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type function isFunction(param: any): param is Function { - return typeof param === 'function' + return typeof param === 'function'; } export function LowlightPlugin({ @@ -82,12 +113,18 @@ export function LowlightPlugin({ lowlight, defaultLanguage, }: { - name: string - lowlight: any - defaultLanguage: string | null | undefined + name: string; + lowlight: any; + defaultLanguage: string | null | undefined; }) { - if (!['highlight', 'highlightAuto', 'listLanguages'].every(api => isFunction(lowlight[api]))) { - throw Error('You should provide an instance of lowlight to use the code-block-lowlight extension') + if ( + !['highlight', 'highlightAuto', 'listLanguages'].every((api) => + isFunction(lowlight[api]), + ) + ) { + throw Error( + 'You should provide an instance of lowlight to use the code-block-lowlight extension', + ); } const lowlightPlugin: Plugin = new Plugin({ @@ -102,10 +139,16 @@ export function LowlightPlugin({ defaultLanguage, }), apply: (transaction, decorationSet, oldState, newState) => { - const oldNodeName = oldState.selection.$head.parent.type.name - const newNodeName = newState.selection.$head.parent.type.name - const oldNodes = findChildren(oldState.doc, node => node.type.name === name) - const newNodes = findChildren(newState.doc, node => node.type.name === name) + const oldNodeName = oldState.selection.$head.parent.type.name; + const newNodeName = newState.selection.$head.parent.type.name; + const oldNodes = findChildren( + oldState.doc, + (node) => node.type.name === name, + ); + const newNodes = findChildren( + newState.doc, + (node) => node.type.name === name, + ); if ( transaction.docChanged && @@ -117,23 +160,23 @@ export function LowlightPlugin({ // OR transaction has changes that completely encapsulte a node // (for example, a transaction that affects the entire document). // Such transactions can happen during collab syncing via y-prosemirror, for example. - transaction.steps.some(step => { + transaction.steps.some((step) => { // @ts-ignore return ( // @ts-ignore step.from !== undefined && // @ts-ignore step.to !== undefined && - oldNodes.some(node => { + oldNodes.some((node) => { // @ts-ignore return ( // @ts-ignore node.pos >= step.from && // @ts-ignore node.pos + node.node.nodeSize <= step.to - ) + ); }) - ) + ); })) ) { return getDecorations({ @@ -141,19 +184,19 @@ export function LowlightPlugin({ name, lowlight, defaultLanguage, - }) + }); } - return decorationSet.map(transaction.mapping, transaction.doc) + return decorationSet.map(transaction.mapping, transaction.doc); }, }, props: { decorations(state) { - return lowlightPlugin.getState(state) + return lowlightPlugin.getState(state); }, }, - }) + }); - return lowlightPlugin -} \ No newline at end of file + return lowlightPlugin; +} From 975b4dcaab9292a77e8db157d934f18054999e73 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sun, 22 Mar 2026 16:40:50 +0000 Subject: [PATCH 04/44] feat: auth pages layout (#2042) * auth pages layout * exclude home route from redirect * fix margin --- .../src/ee/components/cloud-login-form.tsx | 7 +- .../src/ee/mfa/components/mfa-challenge.tsx | 3 + .../ee/mfa/components/mfa-setup-required.tsx | 3 + apps/client/src/ee/pages/verify-email.tsx | 25 +++--- .../features/auth/components/auth-layout.tsx | 26 ++++++ .../features/auth/components/auth.module.css | 12 ++- .../auth/components/forgot-password-form.tsx | 3 + .../auth/components/invite-sign-up-form.tsx | 3 + .../features/auth/components/login-form.tsx | 87 ++++++++++--------- .../auth/components/password-reset-form.tsx | 3 + .../auth/components/setup-workspace-form.tsx | 5 +- apps/client/src/lib/api-client.ts | 8 +- 12 files changed, 124 insertions(+), 61 deletions(-) create mode 100644 apps/client/src/features/auth/components/auth-layout.tsx diff --git a/apps/client/src/ee/components/cloud-login-form.tsx b/apps/client/src/ee/components/cloud-login-form.tsx index 6ab7ebd9..2357f9bb 100644 --- a/apps/client/src/ee/components/cloud-login-form.tsx +++ b/apps/client/src/ee/components/cloud-login-form.tsx @@ -21,6 +21,7 @@ import { useTranslation } from "react-i18next"; import JoinedWorkspaces from "@/ee/components/joined-workspaces.tsx"; import { useJoinedWorkspacesQuery } from "@/ee/cloud/query/cloud-query.ts"; import { findWorkspacesByEmail } from "@/ee/cloud/service/cloud-service.ts"; +import { AuthLayout } from "@/features/auth/components/auth-layout.tsx"; const formSchema = z.object({ hostname: z.string().min(1, { message: "subdomain is required" }), @@ -82,7 +83,7 @@ export function CloudLoginForm() { } return ( -
+ @@ -145,12 +146,12 @@ export function CloudLoginForm() { </Box> </Container> - <Text ta="center"> + <Text ta="center" mb="xl"> {t("Don't have a workspace?")}{" "} <Anchor component={Link} to={APP_ROUTE.AUTH.CREATE_WORKSPACE} fw={500}> {t("Create new workspace")} </Anchor> </Text> - </div> + </AuthLayout> ); } diff --git a/apps/client/src/ee/mfa/components/mfa-challenge.tsx b/apps/client/src/ee/mfa/components/mfa-challenge.tsx index e1d15965..bfa16b22 100644 --- a/apps/client/src/ee/mfa/components/mfa-challenge.tsx +++ b/apps/client/src/ee/mfa/components/mfa-challenge.tsx @@ -22,6 +22,7 @@ import APP_ROUTE, { getPostLoginRedirect } from "@/lib/app-route"; import { useTranslation } from "react-i18next"; import { z } from "zod/v4"; import { MfaBackupCodeInput } from "./mfa-backup-code-input"; +import { AuthLayout } from "@/features/auth/components/auth-layout.tsx"; const formSchema = z.object({ code: z @@ -66,6 +67,7 @@ export function MfaChallenge() { }; return ( + <AuthLayout> <Container size={420} className={classes.container}> <Paper radius="lg" p={40} className={classes.paper}> <Stack align="center" gap="xl"> @@ -157,5 +159,6 @@ export function MfaChallenge() { </Stack> </Paper> </Container> + </AuthLayout> ); } diff --git a/apps/client/src/ee/mfa/components/mfa-setup-required.tsx b/apps/client/src/ee/mfa/components/mfa-setup-required.tsx index ab327c4d..880228a9 100644 --- a/apps/client/src/ee/mfa/components/mfa-setup-required.tsx +++ b/apps/client/src/ee/mfa/components/mfa-setup-required.tsx @@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next"; import { MfaSetupModal } from "@/ee/mfa"; import APP_ROUTE, { getPostLoginRedirect } from "@/lib/app-route.ts"; import { useNavigate } from "react-router-dom"; +import { AuthLayout } from "@/features/auth/components/auth-layout.tsx"; export default function MfaSetupRequired() { const { t } = useTranslation(); @@ -15,6 +16,7 @@ export default function MfaSetupRequired() { }; return ( + <AuthLayout> <Container size="sm" py="xl"> <Paper shadow="sm" p="xl" radius="md" withBorder> <Stack> @@ -44,5 +46,6 @@ export default function MfaSetupRequired() { </Stack> </Paper> </Container> + </AuthLayout> ); } diff --git a/apps/client/src/ee/pages/verify-email.tsx b/apps/client/src/ee/pages/verify-email.tsx index 623c3202..aef5711d 100644 --- a/apps/client/src/ee/pages/verify-email.tsx +++ b/apps/client/src/ee/pages/verify-email.tsx @@ -9,6 +9,7 @@ import { import { notifications } from "@mantine/notifications"; import APP_ROUTE from "@/lib/app-route.ts"; import { useTranslation } from "react-i18next"; +import { AuthLayout } from "@/features/auth/components/auth-layout.tsx"; export default function VerifyEmail() { const { t } = useTranslation(); @@ -59,20 +60,23 @@ export default function VerifyEmail() { if (token) { return ( - <Container size={420} className={classes.container}> - <Box p="xl" className={classes.containerBox}> - <Title order={2} ta="center" fw={500} mb="md"> - {t("Verifying your email")} - - - {t("Please wait...")} - - - + + + + + {t("Verifying your email")} + + + {t("Please wait...")} + + + + ); } return ( + @@ -103,5 +107,6 @@ export default function VerifyEmail() { )} </Box> </Container> + </AuthLayout> ); } diff --git a/apps/client/src/features/auth/components/auth-layout.tsx b/apps/client/src/features/auth/components/auth-layout.tsx new file mode 100644 index 00000000..d05ad7ec --- /dev/null +++ b/apps/client/src/features/auth/components/auth-layout.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { Group, Text } from "@mantine/core"; +import classes from "./auth.module.css"; + +type AuthLayoutProps = { + children: React.ReactNode; +}; + +export function AuthLayout({ children }: AuthLayoutProps) { + return ( + <> + <Group justify="center" gap={8} className={classes.logo}> + <img + src="/icons/favicon-32x32.png" + alt="Docmost" + width={22} + height={22} + /> + <Text size="28px" fw={700} style={{ userSelect: "none" }}> + Docmost + </Text> + </Group> + {children} + </> + ); +} diff --git a/apps/client/src/features/auth/components/auth.module.css b/apps/client/src/features/auth/components/auth.module.css index 1e7d09d6..9b99200d 100644 --- a/apps/client/src/features/auth/components/auth.module.css +++ b/apps/client/src/features/auth/components/auth.module.css @@ -1,12 +1,20 @@ +.logo { + margin-top: 80px; + + @media (max-width: $mantine-breakpoint-sm) { + margin-top: 30px; + } +} + .container { box-shadow: rgba(0, 0, 0, 0.07) 0px 2px 45px 4px; border-radius: 4px; background: light-dark(var(--mantine-color-body), rgba(0, 0, 0, 0.1)); - margin-top: 150px; + margin-top: 40px; margin-bottom: 20px; @media (max-width: $mantine-breakpoint-sm) { - margin-top: 50px; + margin-top: 20px; margin-bottom: 20px; } } diff --git a/apps/client/src/features/auth/components/forgot-password-form.tsx b/apps/client/src/features/auth/components/forgot-password-form.tsx index b5987b49..cdb249c9 100644 --- a/apps/client/src/features/auth/components/forgot-password-form.tsx +++ b/apps/client/src/features/auth/components/forgot-password-form.tsx @@ -7,6 +7,7 @@ import { Box, Button, Container, Text, TextInput, Title } from "@mantine/core"; import classes from "./auth.module.css"; import { useRedirectIfAuthenticated } from "@/features/auth/hooks/use-redirect-if-authenticated.ts"; import { useTranslation } from "react-i18next"; +import { AuthLayout } from "./auth-layout.tsx"; const formSchema = z.object({ email: z @@ -35,6 +36,7 @@ export function ForgotPasswordForm() { } return ( + <AuthLayout> <Container size={420} className={classes.container}> <Box p="xl" className={classes.containerBox}> <Title order={2} ta="center" fw={500} mb="md"> @@ -69,5 +71,6 @@ export function ForgotPasswordForm() { </form> </Box> </Container> + </AuthLayout> ); } diff --git a/apps/client/src/features/auth/components/invite-sign-up-form.tsx b/apps/client/src/features/auth/components/invite-sign-up-form.tsx index d06ef307..91d8e167 100644 --- a/apps/client/src/features/auth/components/invite-sign-up-form.tsx +++ b/apps/client/src/features/auth/components/invite-sign-up-form.tsx @@ -19,6 +19,7 @@ import { useGetInvitationQuery } from "@/features/workspace/queries/workspace-qu import { useRedirectIfAuthenticated } from "@/features/auth/hooks/use-redirect-if-authenticated.ts"; import { useTranslation } from "react-i18next"; import SsoLogin from "@/ee/components/sso-login.tsx"; +import { AuthLayout } from "./auth-layout.tsx"; const formSchema = z.object({ name: z.string().trim().min(1), @@ -66,6 +67,7 @@ export function InviteSignUpForm() { } return ( + <AuthLayout> <Container size={420} className={classes.container}> <Box p="xl" className={classes.containerBox}> <Title order={2} ta="center" fw={500} mb="md"> @@ -111,5 +113,6 @@ export function InviteSignUpForm() { )} </Box> </Container> + </AuthLayout> ); } diff --git a/apps/client/src/features/auth/components/login-form.tsx b/apps/client/src/features/auth/components/login-form.tsx index c07ebe02..78aaa94b 100644 --- a/apps/client/src/features/auth/components/login-form.tsx +++ b/apps/client/src/features/auth/components/login-form.tsx @@ -21,6 +21,7 @@ import SsoLogin from "@/ee/components/sso-login.tsx"; import { useWorkspacePublicDataQuery } from "@/features/workspace/queries/workspace-query.ts"; import { Error404 } from "@/components/ui/error-404.tsx"; import React from "react"; +import { AuthLayout } from "./auth-layout.tsx"; const formSchema = z.object({ email: z @@ -62,52 +63,54 @@ export function LoginForm() { } return ( - <Container size={420} className={classes.container}> - <Box p="xl" className={classes.containerBox}> - <Title order={2} ta="center" fw={500} mb="md"> - {t("Login")} - + + + + + {t("Login")} + - + - {!data?.enforceSso && ( - <> -
- + {!data?.enforceSso && ( + <> + + - + - - - {t("Forgot your password?")} - - + + + {t("Forgot your password?")} + + - - - - )} -
-
+ + + + )} +
+
+
); } diff --git a/apps/client/src/features/auth/components/password-reset-form.tsx b/apps/client/src/features/auth/components/password-reset-form.tsx index 4fe836ec..d603264f 100644 --- a/apps/client/src/features/auth/components/password-reset-form.tsx +++ b/apps/client/src/features/auth/components/password-reset-form.tsx @@ -6,6 +6,7 @@ import { Box, Button, Container, PasswordInput, Title } from "@mantine/core"; import classes from "./auth.module.css"; import { useRedirectIfAuthenticated } from "@/features/auth/hooks/use-redirect-if-authenticated.ts"; import { useTranslation } from "react-i18next"; +import { AuthLayout } from "./auth-layout.tsx"; const formSchema = z.object({ newPassword: z @@ -38,6 +39,7 @@ export function PasswordResetForm({ resetToken }: PasswordResetFormProps) { } return ( + @@ -59,5 +61,6 @@ export function PasswordResetForm({ resetToken }: PasswordResetFormProps) { </form> </Box> </Container> + </AuthLayout> ); } diff --git a/apps/client/src/features/auth/components/setup-workspace-form.tsx b/apps/client/src/features/auth/components/setup-workspace-form.tsx index 6eaf3d12..8aa55e99 100644 --- a/apps/client/src/features/auth/components/setup-workspace-form.tsx +++ b/apps/client/src/features/auth/components/setup-workspace-form.tsx @@ -19,6 +19,7 @@ import SsoCloudSignup from "@/ee/components/sso-cloud-signup.tsx"; import { isCloud } from "@/lib/config.ts"; import { Link } from "react-router-dom"; import APP_ROUTE from "@/lib/app-route.ts"; +import { AuthLayout } from "./auth-layout.tsx"; const formSchema = z.object({ workspaceName: z.string().trim().max(50).optional(), @@ -50,7 +51,7 @@ export function SetupWorkspaceForm() { } return ( - <div> + <AuthLayout> <Container size={420} className={classes.container}> <Box p="xl" className={classes.containerBox}> <Title order={2} ta="center" fw={500} mb="md"> @@ -117,6 +118,6 @@ export function SetupWorkspaceForm() { </Anchor> </Text> )} - </div> + </AuthLayout> ); } diff --git a/apps/client/src/lib/api-client.ts b/apps/client/src/lib/api-client.ts index 632811db..17f7e102 100644 --- a/apps/client/src/lib/api-client.ts +++ b/apps/client/src/lib/api-client.ts @@ -74,8 +74,12 @@ function redirectToLogin() { ]; if (!exemptPaths.some((path) => window.location.pathname.startsWith(path))) { const redirectTo = window.location.pathname; - const params = new URLSearchParams({ redirect: redirectTo }); - window.location.href = `${APP_ROUTE.AUTH.LOGIN}?${params.toString()}`; + if (redirectTo === APP_ROUTE.HOME) { + window.location.href = APP_ROUTE.AUTH.LOGIN; + } else { + const params = new URLSearchParams({ redirect: redirectTo }); + window.location.href = `${APP_ROUTE.AUTH.LOGIN}?${params.toString()}`; + } } } From 6d6f3a8a8ed493424b13a776facc134e076148e8 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 24 Mar 2026 10:52:09 +0000 Subject: [PATCH 05/44] merge commit --- apps/client/src/lib/config.ts | 3 ++- .../src/core/attachment/services/attachment.service.ts | 4 ++-- packages/editor-ext/src/lib/attachment/attachment.ts | 10 +++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/client/src/lib/config.ts b/apps/client/src/lib/config.ts index bc40020b..67bbe100 100644 --- a/apps/client/src/lib/config.ts +++ b/apps/client/src/lib/config.ts @@ -1,6 +1,7 @@ import bytes from "bytes"; import { castToBoolean } from "@/lib/utils.tsx"; import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts"; +import { sanitizeUrl } from "@docmost/editor-ext"; declare global { interface Window { @@ -66,7 +67,7 @@ export function getFileUrl(src: string) { if (src.startsWith("/files/")) { return getBackendUrl() + src; } - return src; + return sanitizeUrl(src); } export function getFileUploadSizeLimit() { diff --git a/apps/server/src/core/attachment/services/attachment.service.ts b/apps/server/src/core/attachment/services/attachment.service.ts index bc6a1e36..6419ed58 100644 --- a/apps/server/src/core/attachment/services/attachment.service.ts +++ b/apps/server/src/core/attachment/services/attachment.service.ts @@ -70,8 +70,8 @@ export class AttachmentService { } if ( - existingAttachment.pageId !== pageId && - existingAttachment.fileExt !== preparedFile.fileExtension && + existingAttachment.pageId !== pageId || + existingAttachment.fileExt !== preparedFile.fileExtension || existingAttachment.workspaceId !== workspaceId ) { throw new BadRequestException('File attachment does not match'); diff --git a/packages/editor-ext/src/lib/attachment/attachment.ts b/packages/editor-ext/src/lib/attachment/attachment.ts index a1e851a4..cc346a52 100644 --- a/packages/editor-ext/src/lib/attachment/attachment.ts +++ b/packages/editor-ext/src/lib/attachment/attachment.ts @@ -1,5 +1,6 @@ import { Node, mergeAttributes } from "@tiptap/core"; import { ReactNodeViewRenderer } from "@tiptap/react"; +import { sanitizeUrl } from "../utils"; export interface AttachmentOptions { HTMLAttributes: Record<string, any>; @@ -42,9 +43,12 @@ export const Attachment = Node.create<AttachmentOptions>({ return { url: { default: "", - parseHTML: (element) => element.getAttribute("data-attachment-url"), + parseHTML: (element) => { + const url = element.getAttribute("data-attachment-url"); + return sanitizeUrl(url); + }, renderHTML: (attributes) => ({ - "data-attachment-url": attributes.url, + "data-attachment-url": sanitizeUrl(attributes.url), }), }, name: { @@ -101,7 +105,7 @@ export const Attachment = Node.create<AttachmentOptions>({ [ "a", { - href: HTMLAttributes["data-attachment-url"], + href: sanitizeUrl(HTMLAttributes["data-attachment-url"]), class: "attachment", target: "blank", }, From fa4872e89e2d02765e0500f5f9e4bccead682826 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:07:01 +0000 Subject: [PATCH 06/44] fix(deps): package updates (#2041) * update * overrides * override * fix page update mutation * fix * cleanup * loader * fix excalidraw package * override * fix regex --- apps/client/package.json | 76 +- .../editor/components/drawio/drawio-menu.tsx | 11 +- .../editor/components/drawio/drawio-view.tsx | 7 +- .../components/excalidraw/excalidraw-menu.tsx | 9 +- .../components/excalidraw/excalidraw-view.tsx | 5 +- .../editor/components/link/link-menu.tsx | 2 +- .../editor/components/link/link-view.tsx | 7 +- .../src/features/page/queries/page-query.ts | 10 +- apps/client/src/lib/utils.tsx | 6 + apps/client/vite.config.ts | 16 +- apps/server/package.json | 108 +- .../collaboration/collaboration.gateway.ts | 2 +- .../src/core/auth/services/token.service.ts | 3 +- apps/server/src/core/auth/token.module.ts | 3 +- apps/server/src/ee | 2 +- .../src/integrations/export/export.service.ts | 3 +- .../import/utils/import-formatter.ts | 3 +- nx.json | 5 +- package.json | 120 +- pnpm-lock.yaml | 8024 +++++++++-------- 20 files changed, 4470 insertions(+), 3952 deletions(-) diff --git a/apps/client/package.json b/apps/client/package.json index e54d65f7..67b25406 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -10,76 +10,76 @@ "format": "prettier --write \"src/**/*.tsx\" \"src/**/*.ts\"" }, "dependencies": { - "@casl/react": "^4.0.0", + "@casl/react": "^5.0.1", "@docmost/editor-ext": "workspace:*", "@emoji-mart/data": "^1.2.1", "@emoji-mart/react": "^1.1.1", "@excalidraw/excalidraw": "0.18.0-3a5ef40", - "@mantine/core": "^8.3.14", - "@mantine/dates": "^8.3.14", - "@mantine/form": "^8.3.14", - "@mantine/hooks": "^8.3.14", - "@mantine/modals": "^8.3.14", - "@mantine/notifications": "^8.3.14", - "@mantine/spotlight": "^8.3.14", - "@tabler/icons-react": "^3.36.1", - "@tanstack/react-query": "^5.90.17", + "@mantine/core": "^8.3.18", + "@mantine/dates": "^8.3.18", + "@mantine/form": "^8.3.18", + "@mantine/hooks": "^8.3.18", + "@mantine/modals": "^8.3.18", + "@mantine/notifications": "^8.3.18", + "@mantine/spotlight": "^8.3.18", + "@tabler/icons-react": "^3.40.0", + "@tanstack/react-query": "5.90.17", "alfaaz": "^1.1.0", - "axios": "^1.13.5", + "axios": "^1.13.6", "blueimp-load-image": "^5.16.0", "clsx": "^2.1.1", "emoji-mart": "^5.6.0", "file-saver": "^2.0.5", "highlightjs-sap-abap": "^0.3.0", - "i18next": "^23.16.8", - "i18next-http-backend": "^2.7.3", - "jotai": "^2.16.2", + "i18next": "^25.10.1", + "i18next-http-backend": "^3.0.2", + "jotai": "^2.18.1", "jotai-optics": "^0.4.0", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", - "katex": "0.16.27", + "katex": "0.16.40", "lowlight": "^3.3.0", "mantine-form-zod-resolver": "^1.3.0", - "mermaid": "^11.12.2", + "mermaid": "^11.13.0", "mitt": "^3.0.1", - "posthog-js": "1.345.5", + "posthog-js": "1.363.1", "react": "^18.3.1", "react-arborist": "3.4.0", - "react-clear-modal": "^2.0.17", + "react-clear-modal": "^2.0.18", "react-dom": "^18.3.1", "react-drawio": "^1.0.7", - "react-error-boundary": "^4.1.2", - "react-helmet-async": "^2.0.5", - "react-i18next": "^15.0.1", - "react-router-dom": "^7.12.0", - "semver": "^7.7.3", + "react-error-boundary": "^6.1.1", + "react-helmet-async": "^3.0.0", + "react-i18next": "^16.5.8", + "react-router-dom": "^7.13.1", + "semver": "^7.7.4", "socket.io-client": "^4.8.3", "tiptap-extension-global-drag-handle": "^0.1.18", "zod": "^4.3.6" }, "devDependencies": { - "@eslint/js": "^9.16.0", - "@tanstack/eslint-plugin-query": "^5.62.1", - "@types/blueimp-load-image": "^5.16.0", + "@eslint/js": "^9.28.0", + "@tanstack/eslint-plugin-query": "^5.94.4", + "@types/blueimp-load-image": "^5.16.6", "@types/file-saver": "^2.0.7", "@types/js-cookie": "^3.0.6", - "@types/katex": "^0.16.7", + "@types/katex": "^0.16.8", "@types/node": "22.19.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "@vitejs/plugin-react": "^5.1.1", - "eslint": "^9.39.2", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.16", + "@vitejs/plugin-react": "^6.0.0", + "eslint": "^9.28.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", "globals": "^15.13.0", "optics-ts": "^2.4.1", - "postcss": "^8.4.49", - "postcss-preset-mantine": "^1.17.0", + "postcss": "^8.5.8", + "postcss-preset-mantine": "^1.18.0", "postcss-simple-vars": "^7.0.1", - "prettier": "^3.4.1", - "typescript": "^5.7.2", - "typescript-eslint": "^8.17.0", - "vite": "^7.2.4" + "prettier": "^3.8.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.57.1", + "vite": "^8.0.1" } } diff --git a/apps/client/src/features/editor/components/drawio/drawio-menu.tsx b/apps/client/src/features/editor/components/drawio/drawio-menu.tsx index 8cda0348..869decd7 100644 --- a/apps/client/src/features/editor/components/drawio/drawio-menu.tsx +++ b/apps/client/src/features/editor/components/drawio/drawio-menu.tsx @@ -8,6 +8,7 @@ import { } from "@/features/editor/components/table/types/types.ts"; import { ActionIcon, + LoadingOverlay, Modal, Text, Tooltip, @@ -46,6 +47,8 @@ export function DrawioMenu({ editor }: EditorMenuProps) { const computedColorScheme = useComputedColorScheme(); const isDirtyRef = useRef(false); const isSavingRef = useRef(false); + const [isSaving, setIsSaving] = useState(false); + const [isLoading, setIsLoading] = useState(false); const editorState = useEditorState({ editor, @@ -140,6 +143,7 @@ export function DrawioMenu({ editor }: EditorMenuProps) { if (isSavingRef.current) return; isSavingRef.current = true; + setIsSaving(true); try { const svgString = decodeBase64ToSvgString(svgXml); @@ -167,6 +171,7 @@ export function DrawioMenu({ editor }: EditorMenuProps) { isDirtyRef.current = false; } finally { isSavingRef.current = false; + setIsSaving(false); } }, [editor, editorState?.attachmentId]); @@ -196,6 +201,7 @@ export function DrawioMenu({ editor }: EditorMenuProps) { const handleOpen = useCallback(async () => { if (!editorState?.src) return; + setIsLoading(true); try { const url = getFileUrl(editorState.src); const request = await fetch(url, { @@ -213,6 +219,7 @@ export function DrawioMenu({ editor }: EditorMenuProps) { } catch (err) { console.error(err); } finally { + setIsLoading(false); isDirtyRef.current = false; open(); } @@ -307,6 +314,7 @@ export function DrawioMenu({ editor }: EditorMenuProps) { size="lg" aria-label={t("Edit")} variant="subtle" + loading={isLoading} > <IconEdit size={18} /> </ActionIcon> @@ -339,7 +347,8 @@ export function DrawioMenu({ editor }: EditorMenuProps) { <Modal.Root opened={opened} onClose={handleClose} fullScreen closeOnEscape={false}> <Modal.Overlay /> <Modal.Content style={{ overflow: "hidden" }}> - <Modal.Body> + <Modal.Body pos="relative"> + <LoadingOverlay visible={isSaving} /> <div style={{ height: "100vh" }}> <DrawIoEmbed ref={drawioRef} diff --git a/apps/client/src/features/editor/components/drawio/drawio-view.tsx b/apps/client/src/features/editor/components/drawio/drawio-view.tsx index 1b1ad95c..357057bf 100644 --- a/apps/client/src/features/editor/components/drawio/drawio-view.tsx +++ b/apps/client/src/features/editor/components/drawio/drawio-view.tsx @@ -2,6 +2,7 @@ import { NodeViewProps, NodeViewWrapper } from "@tiptap/react"; import { ActionIcon, Card, + LoadingOverlay, Modal, Text, useComputedColorScheme, @@ -34,6 +35,7 @@ export default function DrawioView(props: NodeViewProps) { const computedColorScheme = useComputedColorScheme(); const isDirtyRef = useRef(false); const isSavingRef = useRef(false); + const [isSaving, setIsSaving] = useState(false); const handleOpen = async () => { if (!editor.isEditable) { @@ -47,6 +49,7 @@ export default function DrawioView(props: NodeViewProps) { if (isSavingRef.current) return; isSavingRef.current = true; + setIsSaving(true); try { const svgString = decodeBase64ToSvgString(svgXml); @@ -79,6 +82,7 @@ export default function DrawioView(props: NodeViewProps) { isDirtyRef.current = false; } finally { isSavingRef.current = false; + setIsSaving(false); } }; @@ -136,7 +140,8 @@ export default function DrawioView(props: NodeViewProps) { <Modal.Root opened={opened} onClose={handleClose} fullScreen closeOnEscape={false}> <Modal.Overlay /> <Modal.Content style={{ overflow: "hidden" }}> - <Modal.Body> + <Modal.Body pos="relative"> + <LoadingOverlay visible={isSaving} /> <div style={{ height: "100vh" }}> <DrawIoEmbed ref={drawioRef} diff --git a/apps/client/src/features/editor/components/excalidraw/excalidraw-menu.tsx b/apps/client/src/features/editor/components/excalidraw/excalidraw-menu.tsx index c9ae7c08..fd312806 100644 --- a/apps/client/src/features/editor/components/excalidraw/excalidraw-menu.tsx +++ b/apps/client/src/features/editor/components/excalidraw/excalidraw-menu.tsx @@ -56,6 +56,8 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) { const computedColorScheme = useComputedColorScheme(); const isDirtyRef = useRef(false); const isSavingRef = useRef(false); + const [isSaving, setIsSaving] = useState(false); + const [isLoading, setIsLoading] = useState(false); const isInitialLoadRef = useRef(true); const lastFingerprintRef = useRef(""); @@ -153,6 +155,7 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) { const handleOpen = useCallback(async () => { if (!editorState?.src) return; + setIsLoading(true); try { const url = getFileUrl(editorState.src); const request = await fetch(url, { @@ -166,6 +169,7 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) { } catch (err) { console.error(err); } finally { + setIsLoading(false); isDirtyRef.current = false; isInitialLoadRef.current = true; open(); @@ -178,6 +182,7 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) { } isSavingRef.current = true; + setIsSaving(true); try { const { exportToSvg } = await import("@excalidraw/excalidraw"); @@ -223,6 +228,7 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) { isDirtyRef.current = false; } finally { isSavingRef.current = false; + setIsSaving(false); } }, [editor, excalidrawAPI, editorState?.attachmentId]); @@ -339,6 +345,7 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) { size="lg" aria-label={t("Edit")} variant="subtle" + loading={isLoading} > <IconEdit size={18} /> </ActionIcon> @@ -390,7 +397,7 @@ export function ExcalidrawMenu({ editor }: EditorMenuProps) { bg="var(--mantine-color-body)" p="xs" > - <Button onClick={handleSaveAndExit} size={"compact-sm"}> + <Button onClick={handleSaveAndExit} size={"compact-sm"} loading={isSaving}> {t("Save & Exit")} </Button> <Button onClick={handleClose} color="red" size={"compact-sm"}> diff --git a/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx b/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx index 0673f853..658743a3 100644 --- a/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx +++ b/apps/client/src/features/editor/components/excalidraw/excalidraw-view.tsx @@ -52,6 +52,7 @@ export default function ExcalidrawView(props: NodeViewProps) { const isDirtyRef = useRef(false); const isSavingRef = useRef(false); + const [isSaving, setIsSaving] = useState(false); const isInitialLoadRef = useRef(true); const lastFingerprintRef = useRef(""); @@ -70,6 +71,7 @@ export default function ExcalidrawView(props: NodeViewProps) { } isSavingRef.current = true; + setIsSaving(true); try { const { exportToSvg } = await import("@excalidraw/excalidraw"); @@ -120,6 +122,7 @@ export default function ExcalidrawView(props: NodeViewProps) { isDirtyRef.current = false; } finally { isSavingRef.current = false; + setIsSaving(false); } }, [excalidrawAPI, editor, attachmentId, updateAttributes]); @@ -191,7 +194,7 @@ export default function ExcalidrawView(props: NodeViewProps) { bg="var(--mantine-color-body)" p="xs" > - <Button onClick={handleSaveAndExit} size={"compact-sm"}> + <Button onClick={handleSaveAndExit} size={"compact-sm"} loading={isSaving}> {t("Save & Exit")} </Button> <Button onClick={handleClose} color="red" size={"compact-sm"}> diff --git a/apps/client/src/features/editor/components/link/link-menu.tsx b/apps/client/src/features/editor/components/link/link-menu.tsx index e61a586c..4517a79d 100644 --- a/apps/client/src/features/editor/components/link/link-menu.tsx +++ b/apps/client/src/features/editor/components/link/link-menu.tsx @@ -5,7 +5,7 @@ import { useAtom } from "jotai"; import { isTextSelected } from "@docmost/editor-ext"; import { showLinkMenuAtom } from "@/features/editor/atoms/editor-atoms"; import { LinkEditorPanel } from "@/features/editor/components/link/link-editor-panel"; -import { normalizeUrl } from "@/features/editor/components/link/link-view"; +import { normalizeUrl } from "@/lib/utils"; import { TextSelection } from "@tiptap/pm/state"; import { Paper } from "@mantine/core"; diff --git a/apps/client/src/features/editor/components/link/link-view.tsx b/apps/client/src/features/editor/components/link/link-view.tsx index 90b89e82..46227ef9 100644 --- a/apps/client/src/features/editor/components/link/link-view.tsx +++ b/apps/client/src/features/editor/components/link/link-view.tsx @@ -29,12 +29,7 @@ import { useSharePageQuery } from "@/features/share/queries/share-query.ts"; import { buildSharedPageUrl } from "@/features/page/page.utils.ts"; import { extractPageSlugId } from "@/lib"; import { sanitizeUrl, copyToClipboard } from "@docmost/editor-ext"; - -export const normalizeUrl = (url: string): string => { - if (!url) return url; - if (url.startsWith("/") || /^(\S+):(\/\/)?\S+$/.test(url)) return url; - return `https://${url}`; -}; +import { normalizeUrl } from "@/lib/utils"; const parseInternalLink = ( href: string, diff --git a/apps/client/src/features/page/queries/page-query.ts b/apps/client/src/features/page/queries/page-query.ts index cf074ee3..175f7d8d 100644 --- a/apps/client/src/features/page/queries/page-query.ts +++ b/apps/client/src/features/page/queries/page-query.ts @@ -110,15 +110,7 @@ export function useUpdatePageMutation() { return useMutation<IPage, Error, Partial<IPageInput>>({ mutationFn: (data) => updatePage(data), onSuccess: (data) => { - updatePage(data); - - invalidateOnUpdatePage( - data.spaceId, - data.parentPageId, - data.id, - data.title, - data.icon, - ); + updatePageData(data); }, }); } diff --git a/apps/client/src/lib/utils.tsx b/apps/client/src/lib/utils.tsx index e9c39267..5f640d45 100644 --- a/apps/client/src/lib/utils.tsx +++ b/apps/client/src/lib/utils.tsx @@ -94,6 +94,12 @@ export function getPageIcon(icon: string, size = 18): string | ReactNode { ); } +export const normalizeUrl = (url: string): string => { + if (!url) return url; + if (url.startsWith("/") || /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) return url; + return `https://${url}`; +}; + export function castToBoolean(value: unknown): boolean { if (value == null) { return false; diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts index a5fafce3..e6f9de48 100644 --- a/apps/client/vite.config.ts +++ b/apps/client/vite.config.ts @@ -2,7 +2,7 @@ import { defineConfig, loadEnv } from "vite"; import react from "@vitejs/plugin-react"; import * as path from "path"; -export const envPath = path.resolve(process.cwd(), "..", ".."); +const envPath = path.resolve(process.cwd(), "..", ".."); export default defineConfig(({ mode }) => { const { @@ -35,6 +35,20 @@ export default defineConfig(({ mode }) => { APP_VERSION: JSON.stringify(process.env.npm_package_version), }, plugins: [react()], + build: { + rolldownOptions: { + output: { + codeSplitting: { + groups: [ + { name: "vendor-mantine", test: /@mantine/ }, + { name: "vendor-mermaid", test: /mermaid|cytoscape|elkjs/ }, + { name: "vendor-excalidraw", test: /excalidraw/ }, + { name: "vendor-katex", test: /katex/ }, + ], + }, + }, + }, + }, resolve: { alias: { "@": "/src", diff --git a/apps/server/package.json b/apps/server/package.json index 0a5fe355..53fd6468 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -30,123 +30,123 @@ "test:e2e": "jest --config test/jest-e2e.json" }, "dependencies": { - "@ai-sdk/google": "^3.0.29", - "@ai-sdk/openai": "^3.0.29", - "@ai-sdk/openai-compatible": "^2.0.30", - "@aws-sdk/client-s3": "3.1000.0", - "@aws-sdk/lib-storage": "3.1000.0", - "@aws-sdk/s3-request-presigner": "3.1000.0", - "@clickhouse/client": "^1.17.0", + "@ai-sdk/google": "^3.0.52", + "@ai-sdk/openai": "^3.0.47", + "@ai-sdk/openai-compatible": "^2.0.37", + "@aws-sdk/client-s3": "3.1014.0", + "@aws-sdk/lib-storage": "3.1014.0", + "@aws-sdk/s3-request-presigner": "3.1014.0", + "@clickhouse/client": "^1.18.2", "@fastify/cookie": "^11.0.2", "@fastify/multipart": "^9.4.0", "@fastify/static": "^9.0.0", "@keyv/redis": "^5.1.6", - "@langchain/core": "1.1.29", + "@langchain/core": "1.1.34", "@langchain/textsplitters": "1.0.1", "@modelcontextprotocol/sdk": "^1.27.1", "@nestjs-labs/nestjs-ioredis": "^11.0.4", "@nestjs/bullmq": "^11.0.4", "@nestjs/cache-manager": "^3.1.0", - "@nestjs/common": "^11.1.14", + "@nestjs/common": "^11.1.17", "@nestjs/config": "^4.0.3", - "@nestjs/core": "^11.1.14", + "@nestjs/core": "^11.1.17", "@nestjs/event-emitter": "^3.0.1", - "@nestjs/jwt": "11.0.0", + "@nestjs/jwt": "11.0.2", "@nestjs/mapped-types": "^2.1.0", "@nestjs/passport": "^11.0.5", - "@nestjs/platform-fastify": "^11.1.14", - "@nestjs/platform-socket.io": "^11.1.14", + "@nestjs/platform-fastify": "^11.1.17", + "@nestjs/platform-socket.io": "^11.1.17", "@nestjs/schedule": "^6.1.1", "@nestjs/terminus": "^11.1.1", - "@nestjs/websockets": "^11.1.14", + "@nestjs/websockets": "^11.1.17", "@node-saml/passport-saml": "^5.1.0", - "@react-email/components": "1.0.7", + "@react-email/components": "1.0.10", "@react-email/render": "2.0.4", "@socket.io/redis-adapter": "^8.3.0", - "ai": "^6.0.86", - "ai-sdk-ollama": "^3.7.0", + "ai": "^6.0.134", + "ai-sdk-ollama": "^3.8.1", "bcrypt": "^6.0.0", - "bullmq": "^5.70.1", + "bullmq": "^5.71.0", "cache-manager": "^7.2.8", "cheerio": "^1.2.0", "class-transformer": "^0.5.1", "class-validator": "^0.15.1", "cookie": "^1.1.1", - "fs-extra": "^11.3.3", - "happy-dom": "20.1.0", - "ioredis": "^5.4.1", + "fs-extra": "^11.3.4", + "happy-dom": "20.8.4", + "ioredis": "^5.10.1", "jsonwebtoken": "^9.0.3", - "kysely": "^0.28.2", + "kysely": "^0.28.14", "kysely-migration-cli": "^0.4.2", "kysely-postgres-js": "^3.0.0", - "ldapts": "^7.4.0", + "ldapts": "^8.1.7", "lib0": "^0.2.117", - "mammoth": "^1.11.0", - "mime-types": "^2.1.35", - "msgpackr": "^1.11.8", - "nanoid": "3.3.11", + "mammoth": "^1.12.0", + "mime-types": "^3.0.2", + "msgpackr": "^1.11.9", + "nanoid": "5.1.7", "nestjs-cls": "^6.2.0", - "nestjs-kysely": "^1.2.0", - "nestjs-pino": "^4.5.0", - "nodemailer": "^7.0.12", - "openid-client": "^5.7.1", - "otpauth": "^9.4.1", - "p-limit": "^6.2.0", + "nestjs-kysely": "^3.1.2", + "nestjs-pino": "^4.6.1", + "nodemailer": "^8.0.3", + "openid-client": "^6.8.2", + "otpauth": "^9.5.0", + "p-limit": "^7.3.0", "passport-google-oauth20": "^2.0.0", "passport-jwt": "^4.0.1", - "pdfjs-dist": "^5.4.394", + "pdfjs-dist": "^5.5.207", "pg-tsquery": "^8.4.2", "pgvector": "^0.2.1", "pino-http": "^11.0.0", "pino-pretty": "^13.1.3", "postgres": "^3.4.8", - "postmark": "^4.0.5", + "postmark": "^4.0.7", "react": "^18.3.1", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.2", "sanitize-filename-ts": "1.0.2", "socket.io": "^4.8.3", - "stripe": "^17.5.0", + "stripe": "^17.7.0", "tlds": "^1.261.0", "tmp-promise": "^3.0.3", "tseep": "^1.3.1", - "typesense": "^2.1.0", + "typesense": "^3.0.3", "ws": "^8.19.0", - "yauzl": "^3.2.0", + "yauzl": "^3.2.1", "zod": "^4.3.6" }, "devDependencies": { - "@eslint/js": "^9.20.0", + "@eslint/js": "^9.28.0", "@nestjs/cli": "^11.0.16", - "@nestjs/schematics": "^11.0.1", - "@nestjs/testing": "^11.0.10", - "@types/bcrypt": "^5.0.2", + "@nestjs/schematics": "^11.0.9", + "@nestjs/testing": "^11.1.17", + "@types/bcrypt": "^6.0.0", "@types/debounce": "^1.2.4", "@types/fs-extra": "^11.0.4", "@types/jest": "^30.0.0", - "@types/mime-types": "^2.1.4", - "@types/node": "^22.13.4", - "@types/nodemailer": "^6.4.17", - "@types/passport-google-oauth20": "^2.0.16", + "@types/mime-types": "^3.0.1", + "@types/node": "^25.5.0", + "@types/nodemailer": "^7.0.11", + "@types/passport-google-oauth20": "^2.0.17", "@types/passport-jwt": "^4.0.1", "@types/supertest": "^6.0.3", "@types/ws": "^8.18.1", "@types/yauzl": "^2.10.3", - "eslint": "^9.39.2", - "eslint-config-prettier": "^10.0.1", - "globals": "^15.15.0", - "jest": "^30.2.0", + "eslint": "^9.28.0", + "eslint-config-prettier": "^10.1.8", + "globals": "^17.4.0", + "jest": "^30.3.0", "kysely-codegen": "^0.20.0", - "prettier": "^3.5.1", - "react-email": "5.2.8", + "prettier": "^3.8.1", + "react-email": "5.2.10", "source-map-support": "^0.5.21", "supertest": "^7.2.2", "ts-jest": "^29.4.6", "ts-loader": "^9.5.4", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", - "typescript": "^5.7.3", - "typescript-eslint": "^8.24.1" + "typescript": "^5.9.3", + "typescript-eslint": "^8.57.1" }, "jest": { "moduleFileExtensions": [ diff --git a/apps/server/src/collaboration/collaboration.gateway.ts b/apps/server/src/collaboration/collaboration.gateway.ts index b296c520..b46c13c8 100644 --- a/apps/server/src/collaboration/collaboration.gateway.ts +++ b/apps/server/src/collaboration/collaboration.gateway.ts @@ -116,7 +116,7 @@ export class CollaborationGateway { // Forward close events client.on('close', (code: number, reason: Buffer) => { - this.redisSync!.onSocketClose(socketId, code, reason); + this.redisSync!.onSocketClose(socketId, code, reason.buffer as ArrayBuffer); }); // Forward pong events for keepalive diff --git a/apps/server/src/core/auth/services/token.service.ts b/apps/server/src/core/auth/services/token.service.ts index 3929e992..182b6675 100644 --- a/apps/server/src/core/auth/services/token.service.ts +++ b/apps/server/src/core/auth/services/token.service.ts @@ -4,6 +4,7 @@ import { UnauthorizedException, } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; +import type { StringValue } from 'ms'; import { EnvironmentService } from '../../../integrations/environment/environment.service'; import { JwtApiKeyPayload, @@ -96,7 +97,7 @@ export class TokenService { apiKeyId: string; user: User; workspaceId: string; - expiresIn?: string | number; + expiresIn?: StringValue | number; }): Promise<string> { const { apiKeyId, user, workspaceId, expiresIn } = opts; if (isUserDisabled(user)) { diff --git a/apps/server/src/core/auth/token.module.ts b/apps/server/src/core/auth/token.module.ts index d8cd98eb..01ae8cd7 100644 --- a/apps/server/src/core/auth/token.module.ts +++ b/apps/server/src/core/auth/token.module.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; +import type { StringValue } from 'ms'; import { EnvironmentService } from '../../integrations/environment/environment.service'; import { TokenService } from './services/token.service'; @@ -10,7 +11,7 @@ import { TokenService } from './services/token.service'; return { secret: environmentService.getAppSecret(), signOptions: { - expiresIn: environmentService.getJwtTokenExpiresIn(), + expiresIn: environmentService.getJwtTokenExpiresIn() as StringValue, issuer: 'Docmost', }, }; diff --git a/apps/server/src/ee b/apps/server/src/ee index 0b5c8646..c2755be3 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 0b5c8646e6444244c22daaf5cca10bb9bcdbe77c +Subproject commit c2755be37c61e21f55c3974339388b6d18725ca2 diff --git a/apps/server/src/integrations/export/export.service.ts b/apps/server/src/integrations/export/export.service.ts index 64f226a2..4e1350f3 100644 --- a/apps/server/src/integrations/export/export.service.ts +++ b/apps/server/src/integrations/export/export.service.ts @@ -28,8 +28,7 @@ import { PageRepo } from '@docmost/db/repos/page/page.repo'; import { PagePermissionRepo } from '@docmost/db/repos/page/page-permission.repo'; import { Node } from '@tiptap/pm/model'; import { EditorState } from '@tiptap/pm/state'; -// eslint-disable-next-line @typescript-eslint/no-require-imports -import slugify = require('@sindresorhus/slugify'); +import slugify from '@sindresorhus/slugify'; // eslint-disable-next-line @typescript-eslint/no-require-imports const packageJson = require('../../../package.json'); import { EnvironmentService } from '../environment/environment.service'; diff --git a/apps/server/src/integrations/import/utils/import-formatter.ts b/apps/server/src/integrations/import/utils/import-formatter.ts index c46b3c02..3b0738a5 100644 --- a/apps/server/src/integrations/import/utils/import-formatter.ts +++ b/apps/server/src/integrations/import/utils/import-formatter.ts @@ -4,8 +4,7 @@ import * as path from 'path'; import { v7 } from 'uuid'; import { InsertableBacklink } from '@docmost/db/types/entity.types'; import { Cheerio, CheerioAPI, load } from 'cheerio'; -// eslint-disable-next-line @typescript-eslint/no-require-imports -import slugify = require('@sindresorhus/slugify'); +import slugify from '@sindresorhus/slugify'; // Check if text contains Unicode characters (for emojis/icons) function isUnicodeCharacter(text: string): boolean { diff --git a/nx.json b/nx.json index 9578f835..171d1146 100644 --- a/nx.json +++ b/nx.json @@ -17,5 +17,6 @@ }, "affected": { "defaultBase": "main" - } -} + }, + "analytics": false +} \ No newline at end of file diff --git a/package.json b/package.json index 6b51c8b1..3bc5e0c1 100644 --- a/package.json +++ b/package.json @@ -19,73 +19,71 @@ "clean": "rm -rf apps/*/dist packages/*/dist apps/client/node_modules/.vite" }, "dependencies": { - "@braintree/sanitize-url": "^7.1.0", + "@braintree/sanitize-url": "^7.1.2", "@casl/ability": "6.8.0", "@docmost/editor-ext": "workspace:*", "@floating-ui/dom": "^1.7.3", "@hocuspocus/provider": "3.4.4", "@hocuspocus/server": "3.4.4", "@hocuspocus/transformer": "3.4.4", - "@joplin/turndown": "^4.0.74", - "@joplin/turndown-plugin-gfm": "^1.0.56", - "@sindresorhus/slugify": "1.1.0", - "@tiptap/core": "3.17.1", - "@tiptap/extension-code-block": "3.17.1", - "@tiptap/extension-collaboration": "3.17.1", - "@tiptap/extension-collaboration-caret": "3.17.1", - "@tiptap/extension-color": "3.17.1", - "@tiptap/extension-document": "3.17.1", - "@tiptap/extension-heading": "3.17.1", - "@tiptap/extension-highlight": "3.17.1", - "@tiptap/extension-history": "3.17.1", - "@tiptap/extension-image": "3.17.1", - "@tiptap/extension-link": "3.17.1", - "@tiptap/extension-list": "3.17.1", - "@tiptap/extension-placeholder": "3.17.1", - "@tiptap/extension-subscript": "3.17.1", - "@tiptap/extension-superscript": "3.17.1", - "@tiptap/extension-table": "3.17.1", - "@tiptap/extension-text": "3.17.1", - "@tiptap/extension-text-align": "3.17.1", - "@tiptap/extension-text-style": "3.17.1", - "@tiptap/extension-typography": "3.17.1", - "@tiptap/extension-unique-id": "^3.17.1", - "@tiptap/extension-youtube": "3.17.1", - "@tiptap/html": "3.17.1", - "@tiptap/pm": "3.17.1", - "@tiptap/react": "3.17.1", - "@tiptap/starter-kit": "3.17.1", - "@tiptap/suggestion": "3.17.1", - "@tiptap/y-tiptap": "^3.0.2", - "@types/qrcode": "^1.5.5", + "@joplin/turndown": "^4.0.82", + "@joplin/turndown-plugin-gfm": "^1.0.64", + "@sindresorhus/slugify": "3.0.0", + "@tiptap/core": "3.20.4", + "@tiptap/extension-code-block": "3.20.4", + "@tiptap/extension-collaboration": "3.20.4", + "@tiptap/extension-collaboration-caret": "3.20.4", + "@tiptap/extension-color": "3.20.4", + "@tiptap/extension-document": "3.20.4", + "@tiptap/extension-heading": "3.20.4", + "@tiptap/extension-highlight": "3.20.4", + "@tiptap/extension-history": "3.20.4", + "@tiptap/extension-image": "3.20.4", + "@tiptap/extension-link": "3.20.4", + "@tiptap/extension-list": "3.20.4", + "@tiptap/extension-placeholder": "3.20.4", + "@tiptap/extension-subscript": "3.20.4", + "@tiptap/extension-superscript": "3.20.4", + "@tiptap/extension-table": "3.20.4", + "@tiptap/extension-text": "3.20.4", + "@tiptap/extension-text-align": "3.20.4", + "@tiptap/extension-text-style": "3.20.4", + "@tiptap/extension-typography": "3.20.4", + "@tiptap/extension-unique-id": "3.20.4", + "@tiptap/extension-youtube": "3.20.4", + "@tiptap/html": "3.20.4", + "@tiptap/pm": "3.20.4", + "@tiptap/react": "3.20.4", + "@tiptap/starter-kit": "3.20.4", + "@tiptap/suggestion": "3.20.4", + "@tiptap/y-tiptap": "3.0.2", "bytes": "^3.1.2", - "cross-env": "^7.0.3", + "cross-env": "^10.1.0", "date-fns": "^4.1.0", "diff": "8.0.3", "dompurify": "^3.3.3", "fractional-indexing-jittered": "^1.0.0", "highlight.js": "^11.11.1", "image-dimensions": "^2.5.0", - "ioredis": "^5.4.1", "jszip": "^3.10.1", "linkifyjs": "^4.3.2", - "marked": "13.0.3", + "marked": "17.0.5", "ms": "3.0.0-canary.1", "qrcode": "^1.5.4", - "rfc6902": "5.1.2", - "uuid": "^11.1.0", + "rfc6902": "5.2.0", + "uuid": "^13.0.0", "y-indexeddb": "^9.0.12", "y-prosemirror": "1.3.7", - "yjs": "^13.6.29" + "yjs": "^13.6.30" }, "devDependencies": { - "@nx/js": "22.5.3", + "@nx/js": "22.6.1", "@types/bytes": "^3.1.5", + "@types/qrcode": "^1.5.6", "@types/turndown": "^5.0.6", - "@types/uuid": "^10.0.0", - "concurrently": "^9.1.2", - "nx": "22.5.3", - "tsx": "^4.19.3" + "concurrently": "^9.2.1", + "nx": "22.6.1", + "tsx": "^4.21.0" }, "workspaces": { "packages": [ @@ -100,28 +98,28 @@ "@tiptap/core": "patches/@tiptap__core.patch" }, "overrides": { - "jsdom": "25.0.1", - "jsonwebtoken": "9.0.3", - "prosemirror-changeset": "2.3.1", + "prosemirror-changeset": "2.4.0", "y-prosemirror": "1.3.7", - "qs": "6.14.2", - "glob": "10.5.0", - "lodash": "4.17.23", + "glob": "13.0.6", "ws": "8.19.0", - "cross-spawn": "7.0.5", "dompurify": "3.3.3", "tmp": "0.2.5", + "hono": "4.12.8", + "mermaid": "11.13.0", + "nanoid@^3": "3.3.8", + "socket.io-parser": "4.2.6", + "serialize-javascript": "7.0.3", "lodash-es": "4.17.23", - "markdown-it": "14.1.1", - "@tiptap/core": "3.17.1", - "@tiptap/pm": "3.17.1", - "@tiptap/starter-kit": "3.17.1", - "@tiptap/extension-blockquote": "3.17.1", - "@tiptap/extension-bold": "3.17.0", - "@tiptap/extension-bubble-menu": "3.17.1", - "@tiptap/extension-bullet-list": "3.17.1", - "@tiptap/extension-list": "3.17.1", - "@tiptap/extension-code": "3.17.1" + "@hono/node-server": "1.19.10", + "undici": "7.24.0", + "ajv@^6": "6.14.0", + "ajv@^8": "8.18.0", + "underscore": "1.13.8", + "immutable": "4.3.8", + "express-rate-limit": "8.2.2", + "minimatch@^3": "3.1.5", + "minimatch@^5": "5.1.8", + "flatted": "3.4.2" }, "neverBuiltDependencies": [] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3953ddd7..b979f3c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,28 +5,28 @@ settings: excludeLinksFromLockfile: false overrides: - jsdom: 25.0.1 - jsonwebtoken: 9.0.3 - prosemirror-changeset: 2.3.1 + prosemirror-changeset: 2.4.0 y-prosemirror: 1.3.7 - qs: 6.14.2 - glob: 10.5.0 - lodash: 4.17.23 + glob: 13.0.6 ws: 8.19.0 - cross-spawn: 7.0.5 dompurify: 3.3.3 tmp: 0.2.5 + hono: 4.12.8 + mermaid: 11.13.0 + nanoid@^3: 3.3.8 + socket.io-parser: 4.2.6 + serialize-javascript: 7.0.3 lodash-es: 4.17.23 - markdown-it: 14.1.1 - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 - '@tiptap/starter-kit': 3.17.1 - '@tiptap/extension-blockquote': 3.17.1 - '@tiptap/extension-bold': 3.17.0 - '@tiptap/extension-bubble-menu': 3.17.1 - '@tiptap/extension-bullet-list': 3.17.1 - '@tiptap/extension-list': 3.17.1 - '@tiptap/extension-code': 3.17.1 + '@hono/node-server': 1.19.10 + undici: 7.24.0 + ajv@^6: 6.14.0 + ajv@^8: 8.18.0 + underscore: 1.13.8 + immutable: 4.3.8 + express-rate-limit: 8.2.2 + minimatch@^3: 3.1.5 + minimatch@^5: 5.1.8 + flatted: 3.4.2 patchedDependencies: '@tiptap/core': @@ -41,8 +41,8 @@ importers: .: dependencies: '@braintree/sanitize-url': - specifier: ^7.1.0 - version: 7.1.0 + specifier: ^7.1.2 + version: 7.1.2 '@casl/ability': specifier: 6.8.0 version: 6.8.0 @@ -54,115 +54,112 @@ importers: version: 1.7.3 '@hocuspocus/provider': specifier: 3.4.4 - version: 3.4.4(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29) + version: 3.4.4(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) '@hocuspocus/server': specifier: 3.4.4 - version: 3.4.4(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29) + version: 3.4.4(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) '@hocuspocus/transformer': specifier: 3.4.4 - version: 3.4.4(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29))(yjs@13.6.29) + version: 3.4.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30) '@joplin/turndown': - specifier: ^4.0.74 - version: 4.0.74 + specifier: ^4.0.82 + version: 4.0.82 '@joplin/turndown-plugin-gfm': - specifier: ^1.0.56 - version: 1.0.56 + specifier: ^1.0.64 + version: 1.0.64 '@sindresorhus/slugify': - specifier: 1.1.0 - version: 1.1.0 + specifier: 3.0.0 + version: 3.0.0 '@tiptap/core': - specifier: 3.17.1 - version: 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) '@tiptap/extension-code-block': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-collaboration': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29))(yjs@13.6.29) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30) '@tiptap/extension-collaboration-caret': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30)) '@tiptap/extension-color': - specifier: 3.17.1 - version: 3.17.1(@tiptap/extension-text-style@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))) + specifier: 3.20.4 + version: 3.20.4(@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))) '@tiptap/extension-document': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-heading': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-highlight': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-history': - specifier: 3.17.1 - version: 3.17.1(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) '@tiptap/extension-image': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-link': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-list': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-placeholder': - specifier: 3.17.1 - version: 3.17.1(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) '@tiptap/extension-subscript': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-superscript': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-table': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-text': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-text-align': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-text-style': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-typography': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/extension-unique-id': - specifier: ^3.17.1 - version: 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-youtube': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) '@tiptap/html': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(happy-dom@20.1.0) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4) '@tiptap/pm': - specifier: 3.17.1 - version: 3.17.1 + specifier: 3.20.4 + version: 3.20.4 '@tiptap/react': - specifier: 3.17.1 - version: 3.17.1(@floating-ui/dom@1.7.3)(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 3.20.4 + version: 3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tiptap/starter-kit': - specifier: 3.17.1 - version: 3.17.1 + specifier: 3.20.4 + version: 3.20.4 '@tiptap/suggestion': - specifier: 3.17.1 - version: 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/y-tiptap': - specifier: ^3.0.2 - version: 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29) - '@types/qrcode': - specifier: ^1.5.5 - version: 1.5.5 + specifier: 3.0.2 + version: 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) bytes: specifier: ^3.1.2 version: 3.1.2 cross-env: - specifier: ^7.0.3 - version: 7.0.3 + specifier: ^10.1.0 + version: 10.1.0 date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -181,9 +178,6 @@ importers: image-dimensions: specifier: ^2.5.0 version: 2.5.0 - ioredis: - specifier: ^5.4.1 - version: 5.4.1 jszip: specifier: ^3.10.1 version: 3.10.1 @@ -191,8 +185,8 @@ importers: specifier: ^4.3.2 version: 4.3.2 marked: - specifier: 13.0.3 - version: 13.0.3 + specifier: 17.0.5 + version: 17.0.5 ms: specifier: 3.0.0-canary.1 version: 3.0.0-canary.1 @@ -200,48 +194,48 @@ importers: specifier: ^1.5.4 version: 1.5.4 rfc6902: - specifier: 5.1.2 - version: 5.1.2 + specifier: 5.2.0 + version: 5.2.0 uuid: - specifier: ^11.1.0 - version: 11.1.0 + specifier: ^13.0.0 + version: 13.0.0 y-indexeddb: specifier: ^9.0.12 - version: 9.0.12(yjs@13.6.29) + version: 9.0.12(yjs@13.6.30) y-prosemirror: specifier: 1.3.7 - version: 1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29) + version: 1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) yjs: - specifier: ^13.6.29 - version: 13.6.29 + specifier: ^13.6.30 + version: 13.6.30 devDependencies: '@nx/js': - specifier: 22.5.3 - version: 22.5.3(@babel/traverse@7.28.5)(nx@22.5.3) + specifier: 22.6.1 + version: 22.6.1(@babel/traverse@7.28.5)(nx@22.6.1) '@types/bytes': specifier: ^3.1.5 version: 3.1.5 + '@types/qrcode': + specifier: ^1.5.6 + version: 1.5.6 '@types/turndown': specifier: ^5.0.6 version: 5.0.6 - '@types/uuid': - specifier: ^10.0.0 - version: 10.0.0 concurrently: - specifier: ^9.1.2 - version: 9.1.2 + specifier: ^9.2.1 + version: 9.2.1 nx: - specifier: 22.5.3 - version: 22.5.3 + specifier: 22.6.1 + version: 22.6.1 tsx: - specifier: ^4.19.3 - version: 4.19.3 + specifier: ^4.21.0 + version: 4.21.0 apps/client: dependencies: '@casl/react': - specifier: ^4.0.0 - version: 4.0.0(@casl/ability@6.8.0)(react@18.3.1) + specifier: ^5.0.1 + version: 5.0.1(@casl/ability@6.8.0)(react@18.3.1) '@docmost/editor-ext': specifier: workspace:* version: link:../../packages/editor-ext @@ -255,38 +249,38 @@ importers: specifier: 0.18.0-3a5ef40 version: 0.18.0-3a5ef40(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/core': - specifier: ^8.3.14 - version: 8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^8.3.18 + version: 8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/dates': - specifier: ^8.3.14 - version: 8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(dayjs@1.11.19)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^8.3.18 + version: 8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(dayjs@1.11.19)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/form': - specifier: ^8.3.14 - version: 8.3.14(react@18.3.1) + specifier: ^8.3.18 + version: 8.3.18(react@18.3.1) '@mantine/hooks': - specifier: ^8.3.14 - version: 8.3.14(react@18.3.1) + specifier: ^8.3.18 + version: 8.3.18(react@18.3.1) '@mantine/modals': - specifier: ^8.3.14 - version: 8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^8.3.18 + version: 8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/notifications': - specifier: ^8.3.14 - version: 8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^8.3.18 + version: 8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mantine/spotlight': - specifier: ^8.3.14 - version: 8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^8.3.18 + version: 8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tabler/icons-react': - specifier: ^3.36.1 - version: 3.36.1(react@18.3.1) + specifier: ^3.40.0 + version: 3.40.0(react@18.3.1) '@tanstack/react-query': - specifier: ^5.90.17 + specifier: 5.90.17 version: 5.90.17(react@18.3.1) alfaaz: specifier: ^1.1.0 version: 1.1.0 axios: - specifier: ^1.13.5 - version: 1.13.5 + specifier: ^1.13.6 + version: 1.13.6 blueimp-load-image: specifier: ^5.16.0 version: 5.16.0 @@ -303,17 +297,17 @@ importers: specifier: ^0.3.0 version: 0.3.0 i18next: - specifier: ^23.16.8 - version: 23.16.8 + specifier: ^25.10.1 + version: 25.10.1(typescript@5.9.3) i18next-http-backend: - specifier: ^2.7.3 - version: 2.7.3 + specifier: ^3.0.2 + version: 3.0.2 jotai: - specifier: ^2.16.2 - version: 2.16.2(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1) + specifier: ^2.18.1 + version: 2.18.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1) jotai-optics: specifier: ^0.4.0 - version: 0.4.0(jotai@2.16.2(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1))(optics-ts@2.4.1) + version: 0.4.0(jotai@2.18.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1))(optics-ts@2.4.1) js-cookie: specifier: ^3.0.5 version: 3.0.5 @@ -321,23 +315,23 @@ importers: specifier: ^4.0.0 version: 4.0.0 katex: - specifier: 0.16.27 - version: 0.16.27 + specifier: 0.16.40 + version: 0.16.40 lowlight: specifier: ^3.3.0 version: 3.3.0 mantine-form-zod-resolver: specifier: ^1.3.0 - version: 1.3.0(@mantine/form@8.3.14(react@18.3.1))(zod@4.3.6) + version: 1.3.0(@mantine/form@8.3.18(react@18.3.1))(zod@4.3.6) mermaid: - specifier: ^11.12.2 - version: 11.12.2 + specifier: 11.13.0 + version: 11.13.0 mitt: specifier: ^3.0.1 version: 3.0.1 posthog-js: - specifier: 1.345.5 - version: 1.345.5 + specifier: 1.363.1 + version: 1.363.1 react: specifier: ^18.3.1 version: 18.3.1 @@ -345,8 +339,8 @@ importers: specifier: 3.4.0 version: 3.4.0(patch_hash=419b3b02e24afe928cc006a006f6e906666aff19aa6fd7daaa788ccc2202678a)(@types/node@22.19.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-clear-modal: - specifier: ^2.0.17 - version: 2.0.17(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^2.0.18 + version: 2.0.18(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) @@ -354,20 +348,20 @@ importers: specifier: ^1.0.7 version: 1.0.7(react@18.3.1) react-error-boundary: - specifier: ^4.1.2 - version: 4.1.2(react@18.3.1) + specifier: ^6.1.1 + version: 6.1.1(react@18.3.1) react-helmet-async: - specifier: ^2.0.5 - version: 2.0.5(react@18.3.1) + specifier: ^3.0.0 + version: 3.0.0(react@18.3.1) react-i18next: - specifier: ^15.0.1 - version: 15.0.1(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^16.5.8 + version: 16.5.8(i18next@25.10.1(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) react-router-dom: - specifier: ^7.12.0 - version: 7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^7.13.1 + version: 7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) semver: - specifier: ^7.7.3 - version: 7.7.3 + specifier: ^7.7.4 + version: 7.7.4 socket.io-client: specifier: ^4.8.3 version: 4.8.3 @@ -379,13 +373,13 @@ importers: version: 4.3.6 devDependencies: '@eslint/js': - specifier: ^9.16.0 - version: 9.16.0 + specifier: ^9.28.0 + version: 9.39.4 '@tanstack/eslint-plugin-query': - specifier: ^5.62.1 - version: 5.62.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) + specifier: ^5.94.4 + version: 5.94.4(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) '@types/blueimp-load-image': - specifier: ^5.16.0 + specifier: ^5.16.6 version: 5.16.6 '@types/file-saver': specifier: ^2.0.7 @@ -394,8 +388,8 @@ importers: specifier: ^3.0.6 version: 3.0.6 '@types/katex': - specifier: ^0.16.7 - version: 0.16.7 + specifier: ^0.16.8 + version: 0.16.8 '@types/node': specifier: 22.19.1 version: 22.19.1 @@ -406,20 +400,20 @@ importers: specifier: ^18.3.1 version: 18.3.1 '@vitejs/plugin-react': - specifier: ^5.1.1 - version: 5.1.1(vite@7.2.4(@types/node@22.19.1)(jiti@2.4.2)(less@4.2.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0)) + specifier: ^6.0.0 + version: 6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0)) eslint: - specifier: ^9.39.2 - version: 9.39.2(jiti@2.4.2) + specifier: ^9.28.0 + version: 9.39.4(jiti@2.4.2) eslint-plugin-react: - specifier: ^7.37.2 - version: 7.37.2(eslint@9.39.2(jiti@2.4.2)) + specifier: ^7.37.5 + version: 7.37.5(eslint@9.39.4(jiti@2.4.2)) eslint-plugin-react-hooks: - specifier: ^5.1.0 - version: 5.1.0(eslint@9.39.2(jiti@2.4.2)) + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.4(jiti@2.4.2)) eslint-plugin-react-refresh: - specifier: ^0.4.16 - version: 0.4.16(eslint@9.39.2(jiti@2.4.2)) + specifier: ^0.5.2 + version: 0.5.2(eslint@9.39.4(jiti@2.4.2)) globals: specifier: ^15.13.0 version: 15.13.0 @@ -427,50 +421,50 @@ importers: specifier: ^2.4.1 version: 2.4.1 postcss: - specifier: ^8.4.49 - version: 8.4.49 + specifier: ^8.5.8 + version: 8.5.8 postcss-preset-mantine: - specifier: ^1.17.0 - version: 1.17.0(postcss@8.4.49) + specifier: ^1.18.0 + version: 1.18.0(postcss@8.5.8) postcss-simple-vars: specifier: ^7.0.1 - version: 7.0.1(postcss@8.4.49) + version: 7.0.1(postcss@8.5.8) prettier: - specifier: ^3.4.1 - version: 3.4.1 + specifier: ^3.8.1 + version: 3.8.1 typescript: - specifier: ^5.7.2 - version: 5.7.2 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.17.0 - version: 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) + specifier: ^8.57.1 + version: 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) vite: - specifier: ^7.2.4 - version: 7.2.4(@types/node@22.19.1)(jiti@2.4.2)(less@4.2.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) + specifier: ^8.0.1 + version: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0) apps/server: dependencies: '@ai-sdk/google': - specifier: ^3.0.29 - version: 3.0.29(zod@4.3.6) + specifier: ^3.0.52 + version: 3.0.52(zod@4.3.6) '@ai-sdk/openai': - specifier: ^3.0.29 - version: 3.0.29(zod@4.3.6) + specifier: ^3.0.47 + version: 3.0.47(zod@4.3.6) '@ai-sdk/openai-compatible': - specifier: ^2.0.30 - version: 2.0.30(zod@4.3.6) + specifier: ^2.0.37 + version: 2.0.37(zod@4.3.6) '@aws-sdk/client-s3': - specifier: 3.1000.0 - version: 3.1000.0 + specifier: 3.1014.0 + version: 3.1014.0 '@aws-sdk/lib-storage': - specifier: 3.1000.0 - version: 3.1000.0(@aws-sdk/client-s3@3.1000.0) + specifier: 3.1014.0 + version: 3.1014.0(@aws-sdk/client-s3@3.1014.0) '@aws-sdk/s3-request-presigner': - specifier: 3.1000.0 - version: 3.1000.0 + specifier: 3.1014.0 + version: 3.1014.0 '@clickhouse/client': - specifier: ^1.17.0 - version: 1.17.0 + specifier: ^1.18.2 + version: 1.18.2 '@fastify/cookie': specifier: ^11.0.2 version: 11.0.2 @@ -484,65 +478,65 @@ importers: specifier: ^5.1.6 version: 5.1.6(keyv@5.6.0) '@langchain/core': - specifier: 1.1.29 - version: 1.1.29(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6)) + specifier: 1.1.34 + version: 1.1.34(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6)) '@langchain/textsplitters': specifier: 1.0.1 - version: 1.0.1(@langchain/core@1.1.29(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6))) + version: 1.0.1(@langchain/core@1.1.34(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6))) '@modelcontextprotocol/sdk': specifier: ^1.27.1 version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) '@nestjs-labs/nestjs-ioredis': specifier: ^11.0.4 - version: 11.0.4(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(ioredis@5.4.1) + version: 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1) '@nestjs/bullmq': specifier: ^11.0.4 - version: 11.0.4(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(bullmq@5.70.1) + version: 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(bullmq@5.71.0) '@nestjs/cache-manager': specifier: ^3.1.0 - version: 3.1.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2) + version: 3.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2) '@nestjs/common': - specifier: ^11.1.14 - version: 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: ^11.1.17 + version: 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/config': specifier: ^4.0.3 - version: 4.0.3(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) + version: 4.0.3(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) '@nestjs/core': - specifier: ^11.1.14 - version: 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: ^11.1.17 + version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/event-emitter': specifier: ^3.0.1 - version: 3.0.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) + version: 3.0.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) '@nestjs/jwt': - specifier: 11.0.0 - version: 11.0.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) + specifier: 11.0.2 + version: 11.0.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) '@nestjs/mapped-types': specifier: ^2.1.0 - version: 2.1.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) + version: 2.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) '@nestjs/passport': specifier: ^11.0.5 - version: 11.0.5(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) + version: 11.0.5(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) '@nestjs/platform-fastify': - specifier: ^11.1.14 - version: 11.1.14(@fastify/static@9.0.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) + specifier: ^11.1.17 + version: 11.1.17(@fastify/static@9.0.0)(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) '@nestjs/platform-socket.io': - specifier: ^11.1.14 - version: 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(rxjs@7.8.2) + specifier: ^11.1.17 + version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(rxjs@7.8.2) '@nestjs/schedule': specifier: ^6.1.1 - version: 6.1.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) + version: 6.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) '@nestjs/terminus': specifier: ^11.1.1 - version: 11.1.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/websockets': - specifier: ^11.1.14 - version: 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-socket.io@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: ^11.1.17 + version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@node-saml/passport-saml': specifier: ^5.1.0 version: 5.1.0 '@react-email/components': - specifier: 1.0.7 - version: 1.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 1.0.10 + version: 1.0.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-email/render': specifier: 2.0.4 version: 2.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -550,17 +544,17 @@ importers: specifier: ^8.3.0 version: 8.3.0(socket.io-adapter@2.5.4) ai: - specifier: ^6.0.86 - version: 6.0.86(zod@4.3.6) + specifier: ^6.0.134 + version: 6.0.134(zod@4.3.6) ai-sdk-ollama: - specifier: ^3.7.0 - version: 3.7.0(ai@6.0.86(zod@4.3.6))(zod@4.3.6) + specifier: ^3.8.1 + version: 3.8.1(ai@6.0.134(zod@4.3.6))(zod@4.3.6) bcrypt: specifier: ^6.0.0 version: 6.0.0 bullmq: - specifier: ^5.70.1 - version: 5.70.1 + specifier: ^5.71.0 + version: 5.71.0 cache-manager: specifier: ^7.2.8 version: 7.2.8 @@ -577,65 +571,65 @@ importers: specifier: ^1.1.1 version: 1.1.1 fs-extra: - specifier: ^11.3.3 - version: 11.3.3 + specifier: ^11.3.4 + version: 11.3.4 happy-dom: - specifier: 20.1.0 - version: 20.1.0 + specifier: 20.8.4 + version: 20.8.4 ioredis: - specifier: ^5.4.1 - version: 5.4.1 + specifier: ^5.10.1 + version: 5.10.1 jsonwebtoken: - specifier: 9.0.3 + specifier: ^9.0.3 version: 9.0.3 kysely: - specifier: ^0.28.2 - version: 0.28.2 + specifier: ^0.28.14 + version: 0.28.14 kysely-migration-cli: specifier: ^0.4.2 version: 0.4.2 kysely-postgres-js: specifier: ^3.0.0 - version: 3.0.0(kysely@0.28.2)(postgres@3.4.8) + version: 3.0.0(kysely@0.28.14)(postgres@3.4.8) ldapts: - specifier: ^7.4.0 - version: 7.4.0 + specifier: ^8.1.7 + version: 8.1.7 lib0: specifier: ^0.2.117 version: 0.2.117 mammoth: - specifier: ^1.11.0 - version: 1.11.0 + specifier: ^1.12.0 + version: 1.12.0 mime-types: - specifier: ^2.1.35 - version: 2.1.35 + specifier: ^3.0.2 + version: 3.0.2 msgpackr: - specifier: ^1.11.8 - version: 1.11.8 + specifier: ^1.11.9 + version: 1.11.9 nanoid: - specifier: 3.3.11 - version: 3.3.11 + specifier: 5.1.7 + version: 5.1.7 nestjs-cls: specifier: ^6.2.0 - version: 6.2.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 6.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) nestjs-kysely: - specifier: ^1.2.0 - version: 1.2.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(kysely@0.28.2)(reflect-metadata@0.2.2) + specifier: ^3.1.2 + version: 3.1.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(kysely@0.28.14)(reflect-metadata@0.2.2) nestjs-pino: - specifier: ^4.5.0 - version: 4.5.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) + specifier: ^4.6.1 + version: 4.6.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) nodemailer: - specifier: ^7.0.12 - version: 7.0.12 + specifier: ^8.0.3 + version: 8.0.3 openid-client: - specifier: ^5.7.1 - version: 5.7.1 + specifier: ^6.8.2 + version: 6.8.2 otpauth: - specifier: ^9.4.1 - version: 9.4.1 + specifier: ^9.5.0 + version: 9.5.0 p-limit: - specifier: ^6.2.0 - version: 6.2.0 + specifier: ^7.3.0 + version: 7.3.0 passport-google-oauth20: specifier: ^2.0.0 version: 2.0.0 @@ -643,8 +637,8 @@ importers: specifier: ^4.0.1 version: 4.0.1 pdfjs-dist: - specifier: ^5.4.394 - version: 5.4.394 + specifier: ^5.5.207 + version: 5.5.207 pg-tsquery: specifier: ^8.4.2 version: 8.4.2 @@ -661,8 +655,8 @@ importers: specifier: ^3.4.8 version: 3.4.8 postmark: - specifier: ^4.0.5 - version: 4.0.5 + specifier: ^4.0.7 + version: 4.0.7 react: specifier: ^18.3.1 version: 18.3.1 @@ -679,8 +673,8 @@ importers: specifier: ^4.8.3 version: 4.8.3 stripe: - specifier: ^17.5.0 - version: 17.5.0 + specifier: ^17.7.0 + version: 17.7.0 tlds: specifier: ^1.261.0 version: 1.261.0 @@ -691,33 +685,33 @@ importers: specifier: ^1.3.1 version: 1.3.1 typesense: - specifier: ^2.1.0 - version: 2.1.0(@babel/runtime@7.26.10) + specifier: ^3.0.3 + version: 3.0.3(@babel/runtime@7.29.2) ws: specifier: 8.19.0 version: 8.19.0 yauzl: - specifier: ^3.2.0 - version: 3.2.0 + specifier: ^3.2.1 + version: 3.2.1 zod: specifier: ^4.3.6 version: 4.3.6 devDependencies: '@eslint/js': - specifier: ^9.20.0 - version: 9.20.0 + specifier: ^9.28.0 + version: 9.39.4 '@nestjs/cli': specifier: ^11.0.16 - version: 11.0.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4) + version: 11.0.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0) '@nestjs/schematics': - specifier: ^11.0.1 - version: 11.0.1(chokidar@4.0.3)(typescript@5.7.3) + specifier: ^11.0.9 + version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3) '@nestjs/testing': - specifier: ^11.0.10 - version: 11.0.10(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) + specifier: ^11.1.17 + version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) '@types/bcrypt': - specifier: ^5.0.2 - version: 5.0.2 + specifier: ^6.0.0 + version: 6.0.0 '@types/debounce': specifier: ^1.2.4 version: 1.2.4 @@ -728,17 +722,17 @@ importers: specifier: ^30.0.0 version: 30.0.0 '@types/mime-types': - specifier: ^2.1.4 - version: 2.1.4 + specifier: ^3.0.1 + version: 3.0.1 '@types/node': - specifier: ^22.13.4 - version: 22.13.4 + specifier: ^25.5.0 + version: 25.5.0 '@types/nodemailer': - specifier: ^6.4.17 - version: 6.4.17 + specifier: ^7.0.11 + version: 7.0.11 '@types/passport-google-oauth20': - specifier: ^2.0.16 - version: 2.0.16 + specifier: ^2.0.17 + version: 2.0.17 '@types/passport-jwt': specifier: ^4.0.1 version: 4.0.1 @@ -752,26 +746,26 @@ importers: specifier: ^2.10.3 version: 2.10.3 eslint: - specifier: ^9.39.2 - version: 9.39.2(jiti@2.4.2) + specifier: ^9.28.0 + version: 9.39.4(jiti@2.4.2) eslint-config-prettier: - specifier: ^10.0.1 - version: 10.0.1(eslint@9.39.2(jiti@2.4.2)) + specifier: ^10.1.8 + version: 10.1.8(eslint@9.39.4(jiti@2.4.2)) globals: - specifier: ^15.15.0 - version: 15.15.0 + specifier: ^17.4.0 + version: 17.4.0 jest: - specifier: ^30.2.0 - version: 30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)) + specifier: ^30.3.0 + version: 30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) kysely-codegen: specifier: ^0.20.0 - version: 0.20.0(kysely@0.28.2)(pg@8.16.3)(typescript@5.7.3) + version: 0.20.0(kysely@0.28.14)(pg@8.16.3)(typescript@5.9.3) prettier: - specifier: ^3.5.1 - version: 3.5.1 + specifier: ^3.8.1 + version: 3.8.1 react-email: - specifier: 5.2.8 - version: 5.2.8 + specifier: 5.2.10 + version: 5.2.10 source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -780,22 +774,22 @@ importers: version: 7.2.2 ts-jest: specifier: ^29.4.6 - version: 29.4.6(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)))(typescript@5.7.3) + version: 29.4.6(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.4 - version: 9.5.4(typescript@5.7.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))) + version: 9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))) ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3) + version: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3) tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 typescript: - specifier: ^5.7.3 - version: 5.7.3 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.24.1 - version: 8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) + specifier: ^8.57.1 + version: 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) packages/editor-ext: {} @@ -805,35 +799,35 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} - '@adobe/css-tools@4.3.3': - resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} + '@adobe/css-tools@4.4.3': + resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==} - '@ai-sdk/gateway@3.0.46': - resolution: {integrity: sha512-zH1UbNRjG5woOXXFOrVCZraqZuFTtmPvLardMGcgLkzpxKV0U3tAGoyWKSZ862H+eBJfI/Hf2yj/zzGJcCkycg==} + '@ai-sdk/gateway@3.0.77': + resolution: {integrity: sha512-UdwIG2H2YMuntJQ5L+EmED5XiwnlvDT3HOmKfVFxR4Nq/RSLFA/HcchhwfNXHZ5UJjyuL2VO0huLbWSZ9ijemQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/google@3.0.29': - resolution: {integrity: sha512-x0hcU10AA+i1ZUQHloGD5qXWsB+Y8qnxlmFUef6Ly4rB53MGVbQExkI9nOKiCO3mu2TGiiNoQMeKWSeQVLfRUA==} + '@ai-sdk/google@3.0.52': + resolution: {integrity: sha512-HiFB4VlHnv55k9xIbgQW9tHw5OsLXzbAghnDUqrnk/S94QpQuyrDwLSDsk/tUkxJeT00B+wvhL1y6/SARdLeXw==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai-compatible@2.0.30': - resolution: {integrity: sha512-iTjumHf1/u4NhjXYFn/aONM2GId3/o7J1Lp5ql8FCbgIMyRwrmanR5xy1S3aaVkfTscuDvLTzWiy1mAbGzK3nQ==} + '@ai-sdk/openai-compatible@2.0.37': + resolution: {integrity: sha512-+POSFVcgiu47BK64dhsI6OpcDC0/VAE2ZSaXdXGNNhpC/ava++uSRJYks0k2bpfY0wwCTgpAWZsXn/dG2Yppiw==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai@3.0.29': - resolution: {integrity: sha512-ugVTIVpuSLKTjzSPe1F1DWiblJT/lwrrHx0OZEKjpMk/EYP6j6VD/F7SJqM1dsqOJryeBCJWFbUzLNqc99PrMA==} + '@ai-sdk/openai@3.0.47': + resolution: {integrity: sha512-bRsb2sDN5u+pKO3Kdr0flpxtL+cPwQ2uCo/pVyzIbj2I4AkKAokJHhw5JWLVOeEwdlYzWfmv+hzaiGarzUcTFQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@4.0.15': - resolution: {integrity: sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==} + '@ai-sdk/provider-utils@4.0.21': + resolution: {integrity: sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -842,8 +836,8 @@ packages: resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==} engines: {node: '>=18'} - '@angular-devkit/core@19.1.7': - resolution: {integrity: sha512-q0I6L9KTqyQ7D5M8H+fWLT+yjapvMNb7SRdfU6GzmexO66Dpo83q4HDzuDKIPDF29Yl0ELs9ICJqe9yUXh6yDQ==} + '@angular-devkit/core@19.2.17': + resolution: {integrity: sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^4.0.0 @@ -865,8 +859,8 @@ packages: engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/schematics@19.1.7': - resolution: {integrity: sha512-AP6FvhMybCYs3gs+vzEAzSU1K//AFT3SVTRFv+C3WMO5dLeAHeGzM8I2dxD5EHQQtqIE/8apP6CxGrnpA5YlFg==} + '@angular-devkit/schematics@19.2.17': + resolution: {integrity: sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@angular-devkit/schematics@19.2.19': @@ -876,9 +870,6 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@antfu/utils@9.2.0': - resolution: {integrity: sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==} - '@asamuzakjp/css-color@2.8.3': resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} @@ -905,141 +896,141 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.1000.0': - resolution: {integrity: sha512-7kPy33qNGq3NfwHC0412T6LDK1bp4+eiPzetX0sVd9cpTSXuQDKpoOFnB0Njj6uZjJDcLS3n2OeyarwwgkQ0Ow==} + '@aws-sdk/client-s3@3.1014.0': + resolution: {integrity: sha512-0XLrOT4Cm3NEhhiME7l/8LbTXS4KdsbR4dSrY207KNKTcHLLTZ9EXt4ZpgnTfLvWQF3pGP2us4Zi1fYLo0N+Ow==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.15': - resolution: {integrity: sha512-AlC0oQ1/mdJ8vCIqu524j5RB7M8i8E24bbkZmya1CuiQxkY7SdIZAyw7NDNMGaNINQFq/8oGRMX0HeOfCVsl/A==} + '@aws-sdk/core@3.973.23': + resolution: {integrity: sha512-aoJncvD1XvloZ9JLnKqTRL9dBy+Szkryoag9VT+V1TqsuUgIxV9cnBVM/hrDi2vE8bDqLiDR8nirdRcCdtJu0w==} engines: {node: '>=20.0.0'} - '@aws-sdk/crc64-nvme@3.972.3': - resolution: {integrity: sha512-UExeK+EFiq5LAcbHm96CQLSia+5pvpUVSAsVApscBzayb7/6dJBJKwV4/onsk4VbWSmqxDMcfuTD+pC4RxgZHg==} + '@aws-sdk/crc64-nvme@3.972.5': + resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.13': - resolution: {integrity: sha512-6ljXKIQ22WFKyIs1jbORIkGanySBHaPPTOI4OxACP5WXgbcR0nDYfqNJfXEGwCK7IzHdNbCSFsNKKs0qCexR8Q==} + '@aws-sdk/credential-provider-env@3.972.21': + resolution: {integrity: sha512-BkAfKq8Bd4shCtec1usNz//urPJF/SZy14qJyxkSaRJQ/Vv1gVh0VZSTmS7aE6aLMELkFV5wHHrS9ZcdG8Kxsg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.15': - resolution: {integrity: sha512-dJuSTreu/T8f24SHDNTjd7eQ4rabr0TzPh2UTCwYexQtzG3nTDKm1e5eIdhiroTMDkPEJeY+WPkA6F9wod/20A==} + '@aws-sdk/credential-provider-http@3.972.23': + resolution: {integrity: sha512-4XZ3+Gu5DY8/n8zQFHBgcKTF7hWQl42G6CY9xfXVo2d25FM/lYkpmuzhYopYoPL1ITWkJ2OSBQfYEu5JRfHOhA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.13': - resolution: {integrity: sha512-JKSoGb7XeabZLBJptpqoZIFbROUIS65NuQnEHGOpuT9GuuZwag2qciKANiDLFiYk4u8nSrJC9JIOnWKVvPVjeA==} + '@aws-sdk/credential-provider-ini@3.972.23': + resolution: {integrity: sha512-PZLSmU0JFpNCDFReidBezsgL5ji9jOBry8CnZdw4Jj6d0K2z3Ftnp44NXgADqYx5BLMu/ZHujfeJReaDoV+IwQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.13': - resolution: {integrity: sha512-RtYcrxdnJHKY8MFQGLltCURcjuMjnaQpAxPE6+/QEdDHHItMKZgabRe/KScX737F9vJMQsmJy9EmMOkCnoC1JQ==} + '@aws-sdk/credential-provider-login@3.972.23': + resolution: {integrity: sha512-OmE/pSkbMM3dCj1HdOnZ5kXnKK+R/Yz+kbBugraBecp0pGAs21eEURfQRz+1N2gzIHLVyGIP1MEjk/uSrFsngg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.14': - resolution: {integrity: sha512-WqoC2aliIjQM/L3oFf6j+op/enT2i9Cc4UTxxMEKrJNECkq4/PlKE5BOjSYFcq6G9mz65EFbXJh7zOU4CvjSKQ==} + '@aws-sdk/credential-provider-node@3.972.24': + resolution: {integrity: sha512-9Jwi7aps3AfUicJyF5udYadPypPpCwUZ6BSKr/QjRbVCpRVS1wc+1Q6AEZ/qz8J4JraeRd247pSzyMQSIHVebw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.13': - resolution: {integrity: sha512-rsRG0LQA4VR+jnDyuqtXi2CePYSmfm5GNL9KxiW8DSe25YwJSr06W8TdUfONAC+rjsTI+aIH2rBGG5FjMeANrw==} + '@aws-sdk/credential-provider-process@3.972.21': + resolution: {integrity: sha512-nRxbeOJ1E1gVA0lNQezuMVndx+ZcuyaW/RB05pUsznN5BxykSlH6KkZ/7Ca/ubJf3i5N3p0gwNO5zgPSCzj+ww==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.13': - resolution: {integrity: sha512-fr0UU1wx8kNHDhTQBXioc/YviSW8iXuAxHvnH7eQUtn8F8o/FU3uu6EUMvAQgyvn7Ne5QFnC0Cj0BFlwCk+RFw==} + '@aws-sdk/credential-provider-sso@3.972.23': + resolution: {integrity: sha512-APUccADuYPLL0f2htpM8Z4czabSmHOdo4r41W6lKEZdy++cNJ42Radqy6x4TopENzr3hR6WYMyhiuiqtbf/nAA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.13': - resolution: {integrity: sha512-a6iFMh1pgUH0TdcouBppLJUfPM7Yd3R9S1xFodPtCRoLqCz2RQFA3qjA8x4112PVYXEd4/pHX2eihapq39w0rA==} + '@aws-sdk/credential-provider-web-identity@3.972.23': + resolution: {integrity: sha512-H5JNqtIwOu/feInmMMWcK0dL5r897ReEn7n2m16Dd0DPD9gA2Hg8Cq4UDzZ/9OzaLh/uqBM6seixz0U6Fi2Eag==} engines: {node: '>=20.0.0'} - '@aws-sdk/lib-storage@3.1000.0': - resolution: {integrity: sha512-/5KUjz08OS6ErUAaBBBXosFWcjUQJ7R9taPDYfmeKALQF4YXirS+n4/nholInOG4/8Cg89DeufqA/Ru89jC5Kw==} + '@aws-sdk/lib-storage@3.1014.0': + resolution: {integrity: sha512-mM0/YpIEKKQ1hM9n1Q2HhN1ztjk3oNKqFMYP+cZ57FR3XVU4UOw9Npu7gnjfzkNrS7WTDZMe0wCMuDvvtq1Oeg==} engines: {node: '>=20.0.0'} peerDependencies: - '@aws-sdk/client-s3': ^3.1000.0 + '@aws-sdk/client-s3': ^3.1014.0 - '@aws-sdk/middleware-bucket-endpoint@3.972.6': - resolution: {integrity: sha512-3H2bhvb7Cb/S6WFsBy/Dy9q2aegC9JmGH1inO8Lb2sWirSqpLJlZmvQHPE29h2tIxzv6el/14X/tLCQ8BQU6ZQ==} + '@aws-sdk/middleware-bucket-endpoint@3.972.8': + resolution: {integrity: sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-expect-continue@3.972.6': - resolution: {integrity: sha512-QMdffpU+GkSGC+bz6WdqlclqIeCsOfgX8JFZ5xvwDtX+UTj4mIXm3uXu7Ko6dBseRcJz1FA6T9OmlAAY6JgJUg==} + '@aws-sdk/middleware-expect-continue@3.972.8': + resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.973.1': - resolution: {integrity: sha512-QLXsxsI6VW8LuGK+/yx699wzqP/NMCGk/hSGP+qtB+Lcff+23UlbahyouLlk+nfT7Iu021SkXBhnAuVd6IZcPw==} + '@aws-sdk/middleware-flexible-checksums@3.974.3': + resolution: {integrity: sha512-fB7FNLH1+VPUs0QL3PLrHW+DD4gKu6daFgWtyq3R0Y0Lx8DLZPvyGAxCZNFBxH+M2xt9KvBJX6USwjuqvitmCQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.972.6': - resolution: {integrity: sha512-5XHwjPH1lHB+1q4bfC7T8Z5zZrZXfaLcjSMwTd1HPSPrCmPFMbg3UQ5vgNWcVj0xoX4HWqTGkSf2byrjlnRg5w==} + '@aws-sdk/middleware-host-header@3.972.8': + resolution: {integrity: sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-location-constraint@3.972.6': - resolution: {integrity: sha512-XdZ2TLwyj3Am6kvUc67vquQvs6+D8npXvXgyEUJAdkUDx5oMFJKOqpK+UpJhVDsEL068WAJl2NEGzbSik7dGJQ==} + '@aws-sdk/middleware-location-constraint@3.972.8': + resolution: {integrity: sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.972.6': - resolution: {integrity: sha512-iFnaMFMQdljAPrvsCVKYltPt2j40LQqukAbXvW7v0aL5I+1GO7bZ/W8m12WxW3gwyK5p5u1WlHg8TSAizC5cZw==} + '@aws-sdk/middleware-logger@3.972.8': + resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.6': - resolution: {integrity: sha512-dY4v3of5EEMvik6+UDwQ96KfUFDk8m1oZDdkSc5lwi4o7rFrjnv0A+yTV+gu230iybQZnKgDLg/rt2P3H+Vscw==} + '@aws-sdk/middleware-recursion-detection@3.972.8': + resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.972.15': - resolution: {integrity: sha512-WDLgssevOU5BFx1s8jA7jj6cE5HuImz28sy9jKOaVtz0AW1lYqSzotzdyiybFaBcQTs5zxXOb2pUfyMxgEKY3Q==} + '@aws-sdk/middleware-sdk-s3@3.972.23': + resolution: {integrity: sha512-50QgHGPQAb2veqFOmTF1A3GsAklLHZXL47KbY35khIkfbXH5PLvqpEc/gOAEBPj/yFxrlgxz/8mqWcWTNxBkwQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-ssec@3.972.6': - resolution: {integrity: sha512-acvMUX9jF4I2Ew+Z/EA6gfaFaz9ehci5wxBmXCZeulLuv8m+iGf6pY9uKz8TPjg39bdAz3hxoE0eLP8Qz+IYlA==} + '@aws-sdk/middleware-ssec@3.972.8': + resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.15': - resolution: {integrity: sha512-ABlFVcIMmuRAwBT+8q5abAxOr7WmaINirDJBnqGY5b5jSDo00UMlg/G4a0xoAgwm6oAECeJcwkvDlxDwKf58fQ==} + '@aws-sdk/middleware-user-agent@3.972.24': + resolution: {integrity: sha512-dLTWy6IfAMhNiSEvMr07g/qZ54be6pLqlxVblbF6AzafmmGAzMMj8qMoY9B4+YgT+gY9IcuxZslNh03L6PyMCQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.996.3': - resolution: {integrity: sha512-AU5TY1V29xqwg/MxmA2odwysTez+ccFAhmfRJk+QZT5HNv90UTA9qKd1J9THlsQkvmH7HWTEV1lDNxkQO5PzNw==} + '@aws-sdk/nested-clients@3.996.13': + resolution: {integrity: sha512-ptZ1HF4yYHNJX8cgFF+8NdYO69XJKZn7ft0/ynV3c0hCbN+89fAbrLS+fqniU2tW8o9Kfqhj8FUh+IPXb2Qsuw==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.6': - resolution: {integrity: sha512-Aa5PusHLXAqLTX1UKDvI3pHQJtIsF7Q+3turCHqfz/1F61/zDMWfbTC8evjhrrYVAtz9Vsv3SJ/waSUeu7B6gw==} + '@aws-sdk/region-config-resolver@3.972.9': + resolution: {integrity: sha512-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng==} engines: {node: '>=20.0.0'} - '@aws-sdk/s3-request-presigner@3.1000.0': - resolution: {integrity: sha512-DP6EbwCD0CKzBwBnT1X6STB5i+bY765CxjMbWCATDhCgOB343Q6AHM9c1S/300Uc5waXWtI/Wdeak9Ru56JOvg==} + '@aws-sdk/s3-request-presigner@3.1014.0': + resolution: {integrity: sha512-XEcK50lToSoLPrQztKQhONYQW45613H8oEL00mBUd/+OZgk0+3zJ8kSNDsIJioZ3H7Be+yC3CL6a22dZFIKUXQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.996.3': - resolution: {integrity: sha512-gQYI/Buwp0CAGQxY7mR5VzkP56rkWq2Y1ROkFuXh5XY94DsSjJw62B3I0N0lysQmtwiL2ht2KHI9NylM/RP4FA==} + '@aws-sdk/signature-v4-multi-region@3.996.11': + resolution: {integrity: sha512-SKgZY7x6AloLUXO20FJGnkKJ3a6CXzNDt6PYs2yqoPzgU0xKWcUoGGJGEBTsfM5eihKW42lbwp+sXzACLbSsaA==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.999.0': - resolution: {integrity: sha512-cx0hHUlgXULfykx4rdu/ciNAJaa3AL5xz3rieCz7NKJ68MJwlj3664Y8WR5MGgxfyYJBdamnkjNSx5Kekuc0cg==} + '@aws-sdk/token-providers@3.1014.0': + resolution: {integrity: sha512-gHTHNUoaOGNrSWkl32A7wFsU78jlNTlqMccLu0byUk5CysYYXaxNMIonIVr4YcykC7vgtDS5ABuz83giy6fzJA==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.4': - resolution: {integrity: sha512-RW60aH26Bsc016Y9B98hC0Plx6fK5P2v/iQYwMzrSjiDh1qRMUCP6KrXHYEHe3uFvKiOC93Z9zk4BJsUi6Tj1Q==} + '@aws-sdk/types@3.973.6': + resolution: {integrity: sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-arn-parser@3.972.2': - resolution: {integrity: sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==} + '@aws-sdk/util-arn-parser@3.972.3': + resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.996.3': - resolution: {integrity: sha512-yWIQSNiCjykLL+ezN5A+DfBb1gfXTytBxm57e64lYmwxDHNmInYHRJYYRAGWG1o77vKEiWaw4ui28e3yb1k5aQ==} + '@aws-sdk/util-endpoints@3.996.5': + resolution: {integrity: sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-format-url@3.972.6': - resolution: {integrity: sha512-0YNVNgFyziCejXJx0rzxPiD2rkxTWco4c9wiMF6n37Tb9aQvIF8+t7GyEyIFCwQHZ0VMQaAl+nCZHOYz5I5EKw==} + '@aws-sdk/util-format-url@3.972.8': + resolution: {integrity: sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A==} engines: {node: '>=20.0.0'} '@aws-sdk/util-locate-window@3.535.0': resolution: {integrity: sha512-PHJ3SL6d2jpcgbqdgiPxkXpu7Drc2PYViwxSIqvvMKhDwzSB1W3mMvtpzwKM4IE7zLFodZo0GKjJ9AsoXndXhA==} engines: {node: '>=14.0.0'} - '@aws-sdk/util-user-agent-browser@3.972.6': - resolution: {integrity: sha512-Fwr/llD6GOrFgQnKaI2glhohdGuBDfHfora6iG9qsBBBR8xv1SdCSwbtf5CWlUdCw5X7g76G/9Hf0Inh0EmoxA==} + '@aws-sdk/util-user-agent-browser@3.972.8': + resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.973.0': - resolution: {integrity: sha512-A9J2G4Nf236e9GpaC1JnA8wRn6u6GjnOXiTwBLA6NUJhlBTIGfrTy+K1IazmF8y+4OFdW3O5TZlhyspJMqiqjA==} + '@aws-sdk/util-user-agent-node@3.973.10': + resolution: {integrity: sha512-E99zeTscCc+pTMfsvnfi6foPpKmdD1cZfOC7/P8UUrjsoQdg9VEWPRD+xdFduKnfPXwcvby58AlO9jwwF6U96g==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1047,8 +1038,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.8': - resolution: {integrity: sha512-Ql8elcUdYCha83Ol7NznBsgN5GVZnv3vUd86fEc6waU6oUdY0T1O9NODkEEOS/Uaogr87avDrUC6DSeM4oXjZg==} + '@aws-sdk/xml-builder@3.972.15': + resolution: {integrity: sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.3': @@ -1134,14 +1125,6 @@ packages: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.25.9': - resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.28.6': resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} engines: {node: '>=6.9.0'} @@ -1170,18 +1153,10 @@ packages: resolution: {integrity: sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} @@ -1579,18 +1554,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.23.3': resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==} engines: {node: '>=6.9.0'} @@ -1693,16 +1656,20 @@ packages: resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + '@babel/traverse@7.27.0': + resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.0': - resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} '@babel/types@7.28.5': @@ -1718,11 +1685,8 @@ packages: '@braintree/sanitize-url@6.0.2': resolution: {integrity: sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==} - '@braintree/sanitize-url@7.1.0': - resolution: {integrity: sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==} - - '@braintree/sanitize-url@7.1.1': - resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} + '@braintree/sanitize-url@7.1.2': + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} '@cacheable/utils@2.3.4': resolution: {integrity: sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==} @@ -1730,11 +1694,11 @@ packages: '@casl/ability@6.8.0': resolution: {integrity: sha512-Ipt4mzI4gSgnomFdaPjaLgY2MWuXqAEZLrU6qqWBB7khGiBBuuEp6ytYDnq09bRXqcjaeeHiaCvCGFbBA2SpvA==} - '@casl/react@4.0.0': - resolution: {integrity: sha512-ovmI4JfNw7TfVVV+XhAJ//gXgMEkkPJU6YBWFVFZGa8Oikdh8Qxr/sdXcqj71QWEHAGN7aSKMtBE0MZylPUVsg==} + '@casl/react@5.0.1': + resolution: {integrity: sha512-8E3GkvwlxEW+bkxWTdvjn6SALNJZa9khNk591xTpU27nPK/nzgfqsVfEvrFjYIQ2LODrxXsWLmw7iHPEI0/TaQ==} peerDependencies: - '@casl/ability': ^3.0.0 || ^4.0.0 || ^5.1.0 || ^6.0.0 - react: ^16.0.0 || ^17.0.0 || ^18.0.0 + '@casl/ability': ^4.0.0 || ^5.1.0 || ^6.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} @@ -1742,23 +1706,38 @@ packages: '@chevrotain/cst-dts-gen@11.0.3': resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} + '@chevrotain/cst-dts-gen@11.1.2': + resolution: {integrity: sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q==} + '@chevrotain/gast@11.0.3': resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} + '@chevrotain/gast@11.1.2': + resolution: {integrity: sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g==} + '@chevrotain/regexp-to-ast@11.0.3': resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} + '@chevrotain/regexp-to-ast@11.1.2': + resolution: {integrity: sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw==} + '@chevrotain/types@11.0.3': resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} + '@chevrotain/types@11.1.2': + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} + '@chevrotain/utils@11.0.3': resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} - '@clickhouse/client-common@1.17.0': - resolution: {integrity: sha512-MiwwgXViFAQA2YZkN4ymF1ynzG0K49KeSX9/iOcmJetWkxqSekDdpyp1GjwATWa9R215uQ+hGzJtJujeQVZZIw==} + '@chevrotain/utils@11.1.2': + resolution: {integrity: sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==} - '@clickhouse/client@1.17.0': - resolution: {integrity: sha512-Y3DQoamKZ/Iyosoq7Lj7lqpDkQDK4R/5mI52yJs4ZLPIO+d6/CYDqTbFBIb4No3C/AlXUYE4TKhj/kXDpe6rOA==} + '@clickhouse/client-common@1.18.2': + resolution: {integrity: sha512-J0SG6q9V31ydxonglpj9xhNRsUxCsF71iEZ784yldqMYwsHixj/9xHFDgBDX3DuMiDx/kPDfXnf+pimp08wIBA==} + + '@clickhouse/client@1.18.2': + resolution: {integrity: sha512-fuquQswRSHWM6D079ZeuGqkMOsqtcUPL06UdTnowmoeeYjVrqisfVmvnw8pc3OeKS4kVb91oygb/MfLDiMs0TQ==} engines: {node: '>=16'} '@colors/colors@1.5.0': @@ -1808,6 +1787,9 @@ packages: '@emnapi/runtime@1.5.0': resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.9.1': + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -1820,161 +1802,320 @@ packages: emoji-mart: ^5.2 react: ^16.8 || ^17 || ^18 - '@esbuild/aix-ppc64@0.25.0': - resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.0': - resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.0': - resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.0': - resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.0': - resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.0': - resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.0': - resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.0': - resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.0': - resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.0': - resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.0': - resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.0': - resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.0': - resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.0': - resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.0': - resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.0': - resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.0': - resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.0': - resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.0': - resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.0': - resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.0': - resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.0': - resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.0': - resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.0': - resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.0': - resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} @@ -1982,12 +2123,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.4.2': @@ -1998,20 +2139,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.3': - resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.16.0': - resolution: {integrity: sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.20.0': - resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.39.2': - resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -2137,16 +2270,16 @@ packages: '@hocuspocus/transformer@3.4.4': resolution: {integrity: sha512-X0EJ863LV97YbL5m8WTt4NDSC6uHi6ZCq/teIH5aholdjdhdTmFrzMemdhha/ZqPUZyaKhOoAmZzwR55HZLPpQ==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.0.1 + '@tiptap/pm': ^3.0.1 y-prosemirror: 1.3.7 yjs: ^13.6.8 - '@hono/node-server@1.19.9': - resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} + '@hono/node-server@1.19.10': + resolution: {integrity: sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==} engines: {node: '>=18.14.1'} peerDependencies: - hono: ^4 + hono: 4.12.8 '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -2171,8 +2304,8 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@3.0.1': - resolution: {integrity: sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw==} + '@iconify/utils@3.1.0': + resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} '@inquirer/ansi@1.0.2': resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} @@ -2429,15 +2562,11 @@ packages: '@types/node': optional: true - '@ioredis/commands@1.2.0': - resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} - '@ioredis/commands@1.5.0': resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@ioredis/commands@1.5.1': + resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==} '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} @@ -2447,12 +2576,12 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jest/console@30.2.0': - resolution: {integrity: sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==} + '@jest/console@30.3.0': + resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/core@30.2.0': - resolution: {integrity: sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==} + '@jest/core@30.3.0': + resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -2464,36 +2593,44 @@ packages: resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/environment@30.2.0': - resolution: {integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==} + '@jest/diff-sequences@30.3.0': + resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@30.3.0': + resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect-utils@30.2.0': resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect@30.2.0': - resolution: {integrity: sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==} + '@jest/expect-utils@30.3.0': + resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/fake-timers@30.2.0': - resolution: {integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==} + '@jest/expect@30.3.0': + resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@30.3.0': + resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/get-type@30.1.0': resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/globals@30.2.0': - resolution: {integrity: sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==} + '@jest/globals@30.3.0': + resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/pattern@30.0.1': resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/reporters@30.2.0': - resolution: {integrity: sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==} + '@jest/reporters@30.3.0': + resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -2505,43 +2642,43 @@ packages: resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/snapshot-utils@30.2.0': - resolution: {integrity: sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==} + '@jest/snapshot-utils@30.3.0': + resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/source-map@30.0.1': resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-result@30.2.0': - resolution: {integrity: sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==} + '@jest/test-result@30.3.0': + resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-sequencer@30.2.0': - resolution: {integrity: sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==} + '@jest/test-sequencer@30.3.0': + resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/transform@30.2.0': - resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==} + '@jest/transform@30.3.0': + resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/types@30.2.0': resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@joplin/turndown-plugin-gfm@1.0.56': - resolution: {integrity: sha512-q9Pul+xfmjXNHgNgB+ksRkwcBf13X7C89CDxT4sShrh17dmGsc7AUy+GbnwlmavauMDvsdiDIG8pvGqa1L002g==} + '@jest/types@30.3.0': + resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@joplin/turndown@4.0.74': - resolution: {integrity: sha512-yISsLt6wQCVtJHWf6XaSQv3hw4FxzmL8jLa7GJNZAIpFSg9cWBp9f9+tIbEwT6fzCFt1Vs9dQJSVujUYP/hTzA==} + '@joplin/turndown-plugin-gfm@1.0.64': + resolution: {integrity: sha512-8GJ7f9OenE3zkSVII5B6qzIkvgF7C/a20gaASEjM6jWPLPJFFQ2nQ3Ou/kXH1mPUTs9dC9VYs8QXVPvZabKXBQ==} + + '@joplin/turndown@4.0.82': + resolution: {integrity: sha512-6TYWScwHIPEhBznTS2+TBK1SfGvbXAPELRMPnhAemZ8+AtIr/Ygv9XHAd7euIFhYrNXfRLXcicsjN7zBymBDcQ==} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} - '@jridgewell/remapping@2.3.5': resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} @@ -2549,10 +2686,6 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.6': resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} @@ -2574,8 +2707,8 @@ packages: '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - '@langchain/core@1.1.29': - resolution: {integrity: sha512-BPoegTtIdZX4gl2kxcSXAlLrrJFl1cxeRsk9DM/wlIuvyPrFwjWqrEK5NwF5diDt5XSArhQxIFaifGAl4F7fgw==} + '@langchain/core@1.1.34': + resolution: {integrity: sha512-IDlZES5Vexo5meLQRCGkAU7NM0tPGPfPP5wcUzBd7Ot+JoFBmSXutC4gGzvZod5AKRVn3I0Qy5k8vkTraY21jA==} engines: {node: '>=20'} '@langchain/textsplitters@1.0.1': @@ -2587,6 +2720,9 @@ packages: '@lifeomic/attempt@3.0.3': resolution: {integrity: sha512-GlM2AbzrErd/TmLL3E8hAHmb5Q7VhDJp35vIbyPVA5Rz55LZuRr8pwL3qrwwkVNo05gMX1J44gURKb4MHQZo7w==} + '@ltd/j-toml@1.38.0': + resolution: {integrity: sha512-lYtBcmvHustHQtg4X7TXUu1Xa/tbLC3p2wLvgQI+fWVySguVZJF60Snxijw5EiohumxZbR10kWYFFebh1zotiw==} + '@lukeed/csprng@1.1.0': resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} @@ -2595,64 +2731,67 @@ packages: resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} engines: {node: '>=8'} - '@mantine/core@8.3.14': - resolution: {integrity: sha512-ZOxggx65Av1Ii1NrckCuqzluRpmmG+8DyEw24wDom3rmwsPg9UV+0le2QTyI5Eo60LzPfUju1KuEPiUzNABIPg==} + '@mantine/core@8.3.18': + resolution: {integrity: sha512-9tph1lTVogKPjTx02eUxDUOdXacPzK62UuSqb4TdGliI54/Xgxftq0Dfqu6XuhCxn9J5MDJaNiLDvL/1KRkYqA==} peerDependencies: - '@mantine/hooks': 8.3.14 + '@mantine/hooks': 8.3.18 react: ^18.x || ^19.x react-dom: ^18.x || ^19.x - '@mantine/dates@8.3.14': - resolution: {integrity: sha512-NdStRo2ZQ55MoMF5B9vjhpBpHRDHF1XA9Dkb1kKSdNuLlaFXKlvoaZxj/3LfNPpn7Nqlns78nWt4X8/cgC2YIg==} + '@mantine/dates@8.3.18': + resolution: {integrity: sha512-FHx5teJOhupI0gO2o5evtVYQEdqOjayOkLRhEQfB5Nc5DvcysfPfmNILGkc1Nrp9ZQeQWKLT9qr+CkcCXwHOaw==} peerDependencies: - '@mantine/core': 8.3.14 - '@mantine/hooks': 8.3.14 + '@mantine/core': 8.3.18 + '@mantine/hooks': 8.3.18 dayjs: '>=1.0.0' react: ^18.x || ^19.x react-dom: ^18.x || ^19.x - '@mantine/form@8.3.14': - resolution: {integrity: sha512-LJUeab+oF+YzATrm/K03Z/QoVVYlaolWqLUZZj7XexNA4hS2/ycKyWT07YhGkdHTLXkf3DUtrg1sS77K7Oje8A==} + '@mantine/form@8.3.18': + resolution: {integrity: sha512-r5OGLJWTkmIruFjRZRZy9oA7maNYlyt50jB4Pmd2X5360WOmJLd4KH8MFhHZQC7vN+z8/rmBl3t3XGAR2I8xig==} peerDependencies: react: ^18.x || ^19.x - '@mantine/hooks@8.3.14': - resolution: {integrity: sha512-0SbHnGEuHcF2QyjzBBcqidpjNmIb6n7TC3obnhkBToYhUTbMcJSK/8ei/yHtAelridJH4CPeohRlQdc0HajHyQ==} + '@mantine/hooks@8.3.18': + resolution: {integrity: sha512-QoWr9+S8gg5050TQ06aTSxtlpGjYOpIllRbjYYXlRvZeTsUqiTbVfvQROLexu4rEaK+yy9Wwriwl9PMRgbLqPw==} peerDependencies: react: ^18.x || ^19.x - '@mantine/modals@8.3.14': - resolution: {integrity: sha512-BBM53MBq0vKZ7MKmTbqdt6i5eZEoAbfllCHVlQ7J4Xlr1LehoxO3q0MuwPr5kkjSWAPw5okiviKoMYXIKBn53w==} + '@mantine/modals@8.3.18': + resolution: {integrity: sha512-JfPDS4549L314SxFPC1x6CbKwzh82OdnIzwgMxPCVNsWLKV2vEHHUH/fzUYj4Wli6IBrsW4cufjMj9BTj3hm3Q==} peerDependencies: - '@mantine/core': 8.3.14 - '@mantine/hooks': 8.3.14 + '@mantine/core': 8.3.18 + '@mantine/hooks': 8.3.18 react: ^18.x || ^19.x react-dom: ^18.x || ^19.x - '@mantine/notifications@8.3.14': - resolution: {integrity: sha512-+ia97wrcU9Zfv+jXYvgr2GdISqKTHbQE9nnEIZvGUBPAqKr9b2JAsaXQS/RsAdoXUI+kKDEtH2fyVYS7zrSi/Q==} + '@mantine/notifications@8.3.18': + resolution: {integrity: sha512-IpQ0lmwbigTBbZCR6iSYWqIOKEx1tlcd7PcEJ5M5X1qeVSY/N3mmDQt1eJmObvcyDeL5cTJMbSA9UPqhRqo9jw==} peerDependencies: - '@mantine/core': 8.3.14 - '@mantine/hooks': 8.3.14 + '@mantine/core': 8.3.18 + '@mantine/hooks': 8.3.18 react: ^18.x || ^19.x react-dom: ^18.x || ^19.x - '@mantine/spotlight@8.3.14': - resolution: {integrity: sha512-AzBLfw2U03aVy7eByaHFDPf/GplhW4jZ/Eyy/H4sBCfIYTM8QO4W/Db/y5dZBO4tOEWFDNFabc85QyiyfMyHiw==} + '@mantine/spotlight@8.3.18': + resolution: {integrity: sha512-yFoEYG0wKduxbnv6+1CUOXc91lmQ5DN4QvEShYO2ftDm0kXhxeOvJFtGOBYK80tpmSCsaT253p9E3J3DcaOt2w==} peerDependencies: - '@mantine/core': 8.3.14 - '@mantine/hooks': 8.3.14 + '@mantine/core': 8.3.18 + '@mantine/hooks': 8.3.18 react: ^18.x || ^19.x react-dom: ^18.x || ^19.x - '@mantine/store@8.3.14': - resolution: {integrity: sha512-bgW+fYHDOp7Pk4+lcEm3ZF7dD/sIMKHyR985cOqSHAYJPRcVFb+zcEK/SWoFZqlyA4qh08CNrASOaod8N0XKfA==} + '@mantine/store@8.3.18': + resolution: {integrity: sha512-i+QRTLmZzLldea0egtUVnGALd6UMIu8jd44nrNWBSNIXJU/8B6rMlC6gyX+l4szopZSuOaaNJIXkqRdC1gQsVg==} peerDependencies: react: ^18.x || ^19.x '@mermaid-js/parser@0.6.3': resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} + '@mermaid-js/parser@1.0.1': + resolution: {integrity: sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==} + '@modelcontextprotocol/sdk@1.27.1': resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==} engines: {node: '>=18'} @@ -2693,68 +2832,74 @@ packages: cpu: [x64] os: [win32] - '@napi-rs/canvas-android-arm64@0.1.82': - resolution: {integrity: sha512-bvZhN0iI54ouaQOrgJV96H2q7J3ZoufnHf4E1fUaERwW29Rz4rgicohnAg4venwBJZYjGl5Yl3CGmlAl1LZowQ==} + '@napi-rs/canvas-android-arm64@0.1.97': + resolution: {integrity: sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@napi-rs/canvas-darwin-arm64@0.1.82': - resolution: {integrity: sha512-InuBHKCyuFqhNwNr4gpqazo5Xp6ltKflqOLiROn4hqAS8u21xAHyYCJRgHwd+a5NKmutFTaRWeUIT/vxWbU/iw==} + '@napi-rs/canvas-darwin-arm64@0.1.97': + resolution: {integrity: sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@napi-rs/canvas-darwin-x64@0.1.82': - resolution: {integrity: sha512-aQGV5Ynn96onSXcuvYb2y7TRXD/t4CL2EGmnGqvLyeJX1JLSNisKQlWN/1bPDDXymZYSdUqbXehj5qzBlOx+RQ==} + '@napi-rs/canvas-darwin-x64@0.1.97': + resolution: {integrity: sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.82': - resolution: {integrity: sha512-YIUpmHWeHGGRhWitT1KJkgj/JPXPfc9ox8oUoyaGPxolLGPp5AxJkq8wIg8CdFGtutget968dtwmx71m8o3h5g==} + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.97': + resolution: {integrity: sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@napi-rs/canvas-linux-arm64-gnu@0.1.82': - resolution: {integrity: sha512-AwLzwLBgmvk7kWeUgItOUor/QyG31xqtD26w1tLpf4yE0hiXTGp23yc669aawjB6FzgIkjh1NKaNS52B7/qEBQ==} + '@napi-rs/canvas-linux-arm64-gnu@0.1.97': + resolution: {integrity: sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@napi-rs/canvas-linux-arm64-musl@0.1.82': - resolution: {integrity: sha512-moZWuqepAwWBffdF4JDadt8TgBD02iMhG6I1FHZf8xO20AsIp9rB+p0B8Zma2h2vAF/YMjeFCDmW5un6+zZz9g==} + '@napi-rs/canvas-linux-arm64-musl@0.1.97': + resolution: {integrity: sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@napi-rs/canvas-linux-riscv64-gnu@0.1.82': - resolution: {integrity: sha512-w9++2df2kG9eC9LWYIHIlMLuhIrKGQYfUxs97CwgxYjITeFakIRazI9LYWgVzEc98QZ9x9GQvlicFsrROV59MQ==} + '@napi-rs/canvas-linux-riscv64-gnu@0.1.97': + resolution: {integrity: sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - '@napi-rs/canvas-linux-x64-gnu@0.1.82': - resolution: {integrity: sha512-lZulOPwrRi6hEg/17CaqdwWEUfOlIJuhXxincx1aVzsVOCmyHf+xFq4i6liJl1P+x2v6Iz2Z/H5zHvXJCC7Bwg==} + '@napi-rs/canvas-linux-x64-gnu@0.1.97': + resolution: {integrity: sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@napi-rs/canvas-linux-x64-musl@0.1.82': - resolution: {integrity: sha512-Be9Wf5RTv1w6GXlTph55K3PH3vsAh1Ax4T1FQY1UYM0QfD0yrwGdnJ8/fhqw7dEgMjd59zIbjJQC8C3msbGn5g==} + '@napi-rs/canvas-linux-x64-musl@0.1.97': + resolution: {integrity: sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@napi-rs/canvas-win32-x64-msvc@0.1.82': - resolution: {integrity: sha512-LN/i8VrvxTDmEEK1c10z2cdOTkWT76LlTGtyZe5Kr1sqoSomKeExAjbilnu1+oee5lZUgS5yfZ2LNlVhCeARuw==} + '@napi-rs/canvas-win32-arm64-msvc@0.1.97': + resolution: {integrity: sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/canvas-win32-x64-msvc@0.1.97': + resolution: {integrity: sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@napi-rs/canvas@0.1.82': - resolution: {integrity: sha512-FGjyUBoF0sl1EenSiE4UV2WYu76q6F9GSYedq5EiOCOyGYoQ/Owulcv6rd7v/tWOpljDDtefXXIaOCJrVKem4w==} + '@napi-rs/canvas@0.1.97': + resolution: {integrity: sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ==} engines: {node: '>= 10'} '@napi-rs/wasm-runtime@0.2.12': @@ -2763,6 +2908,9 @@ packages: '@napi-rs/wasm-runtime@0.2.4': resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + '@nestjs-labs/nestjs-ioredis@11.0.4': resolution: {integrity: sha512-4jPNOrxDiwNMIN5OLmsMWhA782kxv/ZBxkySX9l8n6sr55acHX/BciaFsOXVa/ILsm+Y7893y98/6WNhmEoiNQ==} engines: {node: '>=16'} @@ -2806,8 +2954,8 @@ packages: '@swc/core': optional: true - '@nestjs/common@11.1.14': - resolution: {integrity: sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==} + '@nestjs/common@11.1.17': + resolution: {integrity: sha512-hLODw5Abp8OQgA+mUO4tHou4krKgDtUcM9j5Ihxncst9XeyxYBTt2bwZm4e4EQr5E352S4Fyy6V3iFx9ggxKAg==} peerDependencies: class-transformer: '>=0.4.1' class-validator: '>=0.13.2' @@ -2825,8 +2973,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 rxjs: ^7.1.0 - '@nestjs/core@11.1.14': - resolution: {integrity: sha512-7OXPPMoDr6z+5NkoQKu4hOhfjz/YYqM3bNilPqv1WVFWrzSmuNXxvhbX69YMmNmRYascPXiwESqf5jJdjKXEww==} + '@nestjs/core@11.1.17': + resolution: {integrity: sha512-lD5mAYekTTurF3vDaa8C2OKPnjiz4tsfxIc5XlcSUzOhkwWf6Ay3HKvt6FmvuWQam6uIIHX52Clg+e6tAvf/cg==} engines: {node: '>= 20'} peerDependencies: '@nestjs/common': ^11.0.0 @@ -2849,8 +2997,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/jwt@11.0.0': - resolution: {integrity: sha512-v7YRsW3Xi8HNTsO+jeHSEEqelX37TVWgwt+BcxtkG/OfXJEOs6GZdbdza200d6KqId1pJQZ6UPj1F0M6E+mxaA==} + '@nestjs/jwt@11.0.2': + resolution: {integrity: sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 @@ -2873,8 +3021,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 passport: ^0.5.0 || ^0.6.0 || ^0.7.0 - '@nestjs/platform-fastify@11.1.14': - resolution: {integrity: sha512-/hnKvPpeH+CTWus7L+F+qXkdbTRXudSQU3Z8tcs5kfvluoBLsxSDTTvjjySr7zrPs5bebaFbGIyG2fKMAWl++g==} + '@nestjs/platform-fastify@11.1.17': + resolution: {integrity: sha512-7nndTu6I2SvIEQbQhv+GVLDpoN05OFZ7x4JhdKhJ+m+1DSNXdgAcPaM1P4FX4xBMKo4m+chbwdcTaGp0ECEnwA==} peerDependencies: '@fastify/static': ^8.0.0 || ^9.0.0 '@fastify/view': ^10.0.0 || ^11.0.0 @@ -2886,8 +3034,8 @@ packages: '@fastify/view': optional: true - '@nestjs/platform-socket.io@11.1.14': - resolution: {integrity: sha512-LLSIWkYz4FcvUhfepillYQboo9qbjq1YtQj8XC3zyex+EaqNXvxhZntx/1uJhAjc655pJts9HfZwWXei8jrRGw==} + '@nestjs/platform-socket.io@11.1.17': + resolution: {integrity: sha512-BSOAsENdmTtsnDL0hb4takbWzPy9WoPybjlM57ab3/rQgm0biMFYUupH2uzmCjmmIXJL/EFbAWznVl8xw2Sa6Q==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/websockets': ^11.0.0 @@ -2899,8 +3047,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/schematics@11.0.1': - resolution: {integrity: sha512-PHPAUk4sXkfCxiMacD1JFC+vEyzXjZJRCu1KT2MmG2hrTiMDMk5KtMprro148JUefNuWbVyN0uLTJVSmWVzhoA==} + '@nestjs/schematics@11.0.9': + resolution: {integrity: sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==} peerDependencies: typescript: '>=4.8.2' @@ -2952,8 +3100,8 @@ packages: typeorm: optional: true - '@nestjs/testing@11.0.10': - resolution: {integrity: sha512-uZcdnvmHXWnvozYOAwZi1elpRRfqIfYqHglCavjhjcj3cH1MVZkwoTqntW3XOPQlT4lf96InjP1exGaW4B9wUg==} + '@nestjs/testing@11.1.17': + resolution: {integrity: sha512-lNffw+z+2USewmw4W0tsK+Rq94A2N4PiHbcqoRUu5y8fnqxQeIWGHhjo5BFCqj7eivqJBhT7WdRydxVq4rAHzg==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -2965,8 +3113,8 @@ packages: '@nestjs/platform-express': optional: true - '@nestjs/websockets@11.1.14': - resolution: {integrity: sha512-fVP6RmmrmtLIitTXN9er7BUOIjjxcdIewN/zUtBlwgfng+qKBTxpNFOs3AXXbCu8bQr2xjzhjrBTfqri0Ske7w==} + '@nestjs/websockets@11.1.17': + resolution: {integrity: sha512-YbwQ0QfVj0lxkKQhdIIgk14ZSVWDqGk1J8nNSN6SLjf36sVv58Ma5ro+dtQua8wj3l2Ub7JJCVFixEhKtYc/rQ==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -2981,6 +3129,10 @@ packages: resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} + '@node-saml/node-saml@5.1.0': resolution: {integrity: sha512-t3cJnZ4aC7HhPZ6MGylGZULvUtBOZ6FzuUndaHGXjmIZHXnLfC/7L8a57O9Q9V7AxJGKAiRM5zu2wNm9EsvQpw==} engines: {node: '>= 18'} @@ -2989,88 +3141,76 @@ packages: resolution: {integrity: sha512-pBm+iFjv9eihcgeJuSUs4c0AuX1QEFdHwP8w1iaWCfDzXdeWZxUBU5HT2bY2S4dvNutcy+A9hYsH7ZLBGtgwDg==} engines: {node: '>= 18'} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@nuxt/opencollective@0.4.1': resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==} engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} hasBin: true - '@nx/devkit@22.5.3': - resolution: {integrity: sha512-zhRNTFsi4pbwg7L/zhBHtTOSevlgwm1iKlhPlQWoOv2PR6b+3JvjL8o4P1MbkIkut3Lsn+oTuJJ1LUPlr5vprg==} + '@nx/devkit@22.6.1': + resolution: {integrity: sha512-/mwG9zWY1phsWvMKzP0yZ4pE6aH0kLH31DuCYj4eLbhuUu0STL3xSdjPPzhDHf71R4K3YnuvG97e2qiGDbG5Qw==} peerDependencies: nx: '>= 21 <= 23 || ^22.0.0-0' - '@nx/js@22.5.3': - resolution: {integrity: sha512-gglQYL6GeSH0mt6NpEFTXMFFFePU3B7TEyZq7LLUYZDH5y65izgNpdSAuEqYR7xHLtahVnesDlhPw3rtRiwMwA==} + '@nx/js@22.6.1': + resolution: {integrity: sha512-Ikp2+B5TzO5/2KV/yc1r4zl6Cnaf0cPgJf+YlBrDUpKss6yrY4n5ORArbvEw9E1q2llVhA5C/zRd+mM8eEMmkQ==} peerDependencies: verdaccio: ^6.0.5 peerDependenciesMeta: verdaccio: optional: true - '@nx/nx-darwin-arm64@22.5.3': - resolution: {integrity: sha512-cKXBq5bJanXp8uv6+wPvx/G4q4oFpOxMSPGaeFOVhbul2QHGGq+XMcSo+D8aYJCsk1YnbyAnnQ8r8RH/kTK5Mw==} + '@nx/nx-darwin-arm64@22.6.1': + resolution: {integrity: sha512-lixkEBGFdEsUiqEZg9LIyjfiTv12Sg1Es/yUgrdOQUAZu+5oiUPMoybyBwrvINl+fZw+PLh66jOmB4GSP2aUMQ==} cpu: [arm64] os: [darwin] - '@nx/nx-darwin-x64@22.5.3': - resolution: {integrity: sha512-mToS41o8I+8CfxYVRMTISkgT7I1cnazgwMf7U9DoLqKOwOZzj9WD3NmsWc1h69QNJPltbeRPS8y/wnhu7RHzRA==} + '@nx/nx-darwin-x64@22.6.1': + resolution: {integrity: sha512-HvgtOtuWnEf0dpfWb05N0ptdFg040YgzsKFhXg6+qaBJg5Hg0e0AXPKaSgh2PCqCIDlKu40YtwVgF7KXxXAGlA==} cpu: [x64] os: [darwin] - '@nx/nx-freebsd-x64@22.5.3': - resolution: {integrity: sha512-CAWysdFSZVbTfdjNXojd9TgXbZiK9i0k3njROeV+jORsDWw4Eth3PDmK94Wk916b3n2hS0UjyI6RZaMy2GEqzA==} + '@nx/nx-freebsd-x64@22.6.1': + resolution: {integrity: sha512-g2wUltGX+7/+mdTV5d6ODa0ylrNu/krgb9YdrsbhW6oZeXYm2LeLOAnYqIlL/Kx140NLrb5Kcz7bi7JrBAw4Ow==} cpu: [x64] os: [freebsd] - '@nx/nx-linux-arm-gnueabihf@22.5.3': - resolution: {integrity: sha512-PRjPrijQQbdrvYwNuA3xQ3VXEQ4zfhnPjy+S2ZlQZqhFI4mlP22xfhOH1bQ7pIfzCNC2f/J9UMNYOrq/bEFjBg==} + '@nx/nx-linux-arm-gnueabihf@22.6.1': + resolution: {integrity: sha512-TTqisFPAPrj35EihvzotBbajS+0bX++PQggmRVmDmGwSTrpySRJwZnKNHYDqP6s9tigDvkNJOJftK+GkBEFRRA==} cpu: [arm] os: [linux] - '@nx/nx-linux-arm64-gnu@22.5.3': - resolution: {integrity: sha512-dmDBio/5z4Zch2VlRMdgBPm53d8xwq1l7xLj1dFMKjfE7ByfPukjPM7ZEYBiPckfiQfJBRh6HKDN7uEkA/y8CQ==} + '@nx/nx-linux-arm64-gnu@22.6.1': + resolution: {integrity: sha512-uIkPcanSTIcyh7/6LOoX0YpGO/7GkVhMRgyM9Mg/7ItFjCtRaeuPEPrJESsaNeB5zIVVhI4cXbGrM9NDnagiiw==} cpu: [arm64] os: [linux] - '@nx/nx-linux-arm64-musl@22.5.3': - resolution: {integrity: sha512-E81ET/MnnKfuLhKiovF5ueJirHOMjhC1eK0MDM2Do9wdPyusZzfGSVFQ9DOHtg7L37dAE95NNd1lCVO8gJ96vg==} + '@nx/nx-linux-arm64-musl@22.6.1': + resolution: {integrity: sha512-eqkG8s/7remiRZ1Lo2zIrFLSNsQ/0x9fAj++CV1nqFE+rfykPQhC48F8pqsq6tUQpI5HqRQEfQgv4CnFNpLR+w==} cpu: [arm64] os: [linux] - '@nx/nx-linux-x64-gnu@22.5.3': - resolution: {integrity: sha512-AgXCsPCzC0sAu2VRclMjs7LrvPQfqS3sFiehlXWTbNHQitPZLuAmQGb2l4T8lbMOs0Xn3EIrg6BF6/ntTTp6Xg==} + '@nx/nx-linux-x64-gnu@22.6.1': + resolution: {integrity: sha512-6DhSupCcDa6BYzQ48qsMK4LIdIO+y4E+4xuUBkX2YTGOZh58gctELCv7Gi6/FhiC8rzVzM7hDcygOvHCGc30zA==} cpu: [x64] os: [linux] - '@nx/nx-linux-x64-musl@22.5.3': - resolution: {integrity: sha512-sKs4bFQRu8Btxf5rMYKPsRVNxkQ2ey8sqoCyhJj8fwJF05DayK2ErJAR/rhtBK0c1NV7kQiKJA8nWBV3jnCdsg==} + '@nx/nx-linux-x64-musl@22.6.1': + resolution: {integrity: sha512-QqtfaBhdfLRKGucpP8RSv7KJ51XRWpfUcXPhkb/1dKP/b9/Z0kpaCgczGHdrAtX9m6haWw+sQXYGxnStZIg/TQ==} cpu: [x64] os: [linux] - '@nx/nx-win32-arm64-msvc@22.5.3': - resolution: {integrity: sha512-KOCQLakSO5vl4D6et9qPytOAmkgq2IIuhI8A/g0xbD1LqrIlRPa+bdkZqOGpODYAk3NyKAk7hWHsqfXKHwwX6w==} + '@nx/nx-win32-arm64-msvc@22.6.1': + resolution: {integrity: sha512-8pTWXphY5IIgY3edZ5SfzP8yPjBqoAxRV5snAYDctF4e0OC1nDOUims70jLesMle8DTSWiHPSfbLVfp2HkU9WQ==} cpu: [arm64] os: [win32] - '@nx/nx-win32-x64-msvc@22.5.3': - resolution: {integrity: sha512-a6ZB2La82RIHcz4nrt3H6RZaOa+xkC2IPzhU9hMo2gbkLdIxn8wyof8uGA0frncmIVHuLc3nFAhpBOgf4j6tMA==} + '@nx/nx-win32-x64-msvc@22.6.1': + resolution: {integrity: sha512-XMYrtsR5O39uNR4fVpFs65rVB09FyLXvUM735r2rO7IUWWHxHWTAgVcc+gqQaAchBPqR9f1q+3u2i1Inub3Cdw==} cpu: [x64] os: [win32] - '@nx/workspace@22.5.3': - resolution: {integrity: sha512-pioGwlt5zKB9PhX36I5KAeSml19Mq+g2KyQ9mh3F+3Lvft2JM4nIMELBaUfwPicPAOwNmrsx806IXO67Q4UHxQ==} + '@nx/workspace@22.6.1': + resolution: {integrity: sha512-mrvGubfownO/m0CtFvtRwQxQm//UoBRgrT0WCxrQi5tGlgViTYFSDRumhy94EuU00OGiAHNa3yeXWFd72O981A==} '@opentelemetry/api-logs@0.208.0': resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} @@ -3144,25 +3284,24 @@ packages: resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} engines: {node: '>=14'} + '@oxc-project/types@0.120.0': + resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + '@paralleldrive/cuid2@2.3.1': resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@posthog/core@1.22.0': - resolution: {integrity: sha512-WkmOnq95aAOu6yk6r5LWr5cfXsQdpVbWDCwOxQwxSne8YV6GuZET1ziO5toSQXgrgbdcjrSz2/GopAfiL6iiAA==} + '@posthog/core@1.24.1': + resolution: {integrity: sha512-e8AciAnc6MRFws89ux8lJKFAaI03yEon0ASDoUO7yS91FVqbUGXYekObUUR3LHplcg+pmyiJBI0jolY0SFbGRA==} - '@posthog/types@1.345.5': - resolution: {integrity: sha512-nPQQ5QfVMmsKquXQkXfA8g1/2IrHO7npw8ezcC0v3xJaTpAKoqBqknqugP+RwTlIfkhCbHSGd6x00yRSwTGaBQ==} + '@posthog/types@1.363.1': + resolution: {integrity: sha512-bFYk5XHgYEfVhQU0AwkG9MbMqq9QRbKDDJxOtYWGJ6Uw+/nLRNs/ZydXy3aMt0ldIdkNzZq+qaJ/p2Jg0+mP8g==} '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -3893,8 +4032,8 @@ packages: '@react-dnd/shallowequal@2.0.0': resolution: {integrity: sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==} - '@react-email/body@0.2.1': - resolution: {integrity: sha512-ljDiQiJDu/Fq//vSIIP0z5Nuvt4+DX1RqGasstChDGJB/14ogd4VdNS9aacoede/ZjGy3o3Qb+cxyS+XgM6SwQ==} + '@react-email/body@0.3.0': + resolution: {integrity: sha512-uGo0BOOzjbMUo3lu+BIDWayvn5o6Xyfmnlla5VGf05n8gHMvO1ll7U4FtzWe3hxMLwt53pmc4iE0M+B5slG+Ug==} engines: {node: '>=20.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -3923,8 +4062,8 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/components@1.0.7': - resolution: {integrity: sha512-mY+v4C1SMaGOKuKp0QWDQLGK+3fvH06ZE10EVavv+T6tQneDHq9cpQ9NdCrvuO1nWZnWrA/0tRpvyqyF0uo93w==} + '@react-email/components@1.0.10': + resolution: {integrity: sha512-r/BnqfAjr3apcvn/NDx2DqNRD5BP5wZLRdjn2IVHXjt4KmQ5RHWSCAvFiXAzRHys1BWQ2zgIc7cpWePUcAl+nw==} engines: {node: '>=20.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -4008,21 +4147,21 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/tailwind@2.0.4': - resolution: {integrity: sha512-cDp8Ss6LJKI8zBLKE+tsXFurn6I2nnQNg1qqjfZuNPNoToN1Uyx3egW0bwSVk1JjrNWx/Xnme7ZxvNLRrU9K0Q==} + '@react-email/tailwind@2.0.6': + resolution: {integrity: sha512-3PgL/GYWmgS+puLPQ2aLlsplHSOFztRl70fowBkbLIb8ZUIgvx5YId6zYCCHeM2+DQ/EG3iXXqLNTahVztuMqQ==} engines: {node: '>=20.0.0'} peerDependencies: - '@react-email/body': 0.2.1 - '@react-email/button': 0.2.1 - '@react-email/code-block': 0.2.1 - '@react-email/code-inline': 0.0.6 - '@react-email/container': 0.0.16 - '@react-email/heading': 0.0.16 - '@react-email/hr': 0.0.12 - '@react-email/img': 0.0.12 - '@react-email/link': 0.0.13 - '@react-email/preview': 0.0.14 - '@react-email/text': 0.1.6 + '@react-email/body': '>=0' + '@react-email/button': '>=0' + '@react-email/code-block': '>=0' + '@react-email/code-inline': '>=0' + '@react-email/container': '>=0' + '@react-email/heading': '>=0' + '@react-email/hr': '>=0' + '@react-email/img': '>=0' + '@react-email/link': '>=0' + '@react-email/preview': '>=0' + '@react-email/text': '>=0' react: ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@react-email/body': @@ -4064,118 +4203,100 @@ packages: '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} - '@rolldown/pluginutils@1.0.0-beta.47': - resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} - - '@rollup/rollup-android-arm-eabi@4.53.3': - resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.53.3': - resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + '@rolldown/binding-android-arm64@1.0.0-rc.10': + resolution: {integrity: sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.53.3': - resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + '@rolldown/binding-darwin-arm64@1.0.0-rc.10': + resolution: {integrity: sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.3': - resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + '@rolldown/binding-darwin-x64@1.0.0-rc.10': + resolution: {integrity: sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.53.3': - resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.53.3': - resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + '@rolldown/binding-freebsd-x64@1.0.0-rc.10': + resolution: {integrity: sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': - resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': + resolution: {integrity: sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.53.3': - resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.53.3': - resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.53.3': - resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': + resolution: {integrity: sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.53.3': - resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.53.3': - resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.53.3': - resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.53.3': - resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.53.3': - resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.53.3': - resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.53.3': - resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': + resolution: {integrity: sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.53.3': - resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': + resolution: {integrity: sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.53.3': - resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': + resolution: {integrity: sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': + resolution: {integrity: sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.3': - resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.53.3': - resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': + resolution: {integrity: sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.3': - resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} - cpu: [x64] - os: [win32] + '@rolldown/pluginutils@1.0.0-rc.10': + resolution: {integrity: sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==} + + '@rolldown/pluginutils@1.0.0-rc.7': + resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -4183,234 +4304,234 @@ packages: '@sinclair/typebox@0.34.48': resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} - '@sindresorhus/slugify@1.1.0': - resolution: {integrity: sha512-ujZRbmmizX26yS/HnB3P9QNlNa4+UvHh+rIse3RbOXLp8yl6n1TxB4t7NHggtVgS8QmmOtzXo48kCxZGACpkPw==} - engines: {node: '>=10'} + '@sindresorhus/slugify@3.0.0': + resolution: {integrity: sha512-SCrKh1zS96q+CuH5GumHcyQEVPsM4Ve8oE0E6tw7AAhGq50K8ojbTUOQnX/j9Mhcv/AXiIsbCfquovyGOo5fGw==} + engines: {node: '>=20'} - '@sindresorhus/transliterate@0.1.2': - resolution: {integrity: sha512-5/kmIOY9FF32nicXH+5yLNTX4NJ4atl7jRgqAJuIn/iyDFXBktOKDxCvyGE/EzmF4ngSUvjXxQUQlQiZ5lfw+w==} - engines: {node: '>=10'} + '@sindresorhus/transliterate@2.3.1': + resolution: {integrity: sha512-gVaaGtKYMYAMmI8buULVH3A2TXVJ98QiwGwI7ddrWGuGidGC2uRt4FHs22+8iROJ0QTzju9CuMjlVsrvpqsdhA==} + engines: {node: '>=20'} '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - '@sinonjs/fake-timers@13.0.5': - resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} + '@sinonjs/fake-timers@15.1.1': + resolution: {integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==} - '@smithy/abort-controller@4.2.10': - resolution: {integrity: sha512-qocxM/X4XGATqQtUkbE9SPUB6wekBi+FyJOMbPj0AhvyvFGYEmOlz6VB22iMePCQsFmMIvFSeViDvA7mZJG47g==} + '@smithy/abort-controller@4.2.12': + resolution: {integrity: sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader-native@4.2.2': - resolution: {integrity: sha512-QzzYIlf4yg0w5TQaC9VId3B3ugSk1MI/wb7tgcHtd7CBV9gNRKZrhc2EPSxSZuDy10zUZ0lomNMgkc6/VVe8xg==} + '@smithy/chunked-blob-reader-native@4.2.3': + resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@5.2.1': - resolution: {integrity: sha512-y5d4xRiD6TzeP5BWlb+Ig/VFqF+t9oANNhGeMqyzU7obw7FYgTgVi50i5JqBTeKp+TABeDIeeXFZdz65RipNtA==} + '@smithy/chunked-blob-reader@5.2.2': + resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.9': - resolution: {integrity: sha512-ejQvXqlcU30h7liR9fXtj7PIAau1t/sFbJpgWPfiYDs7zd16jpH0IsSXKcba2jF6ChTXvIjACs27kNMc5xxE2Q==} + '@smithy/config-resolver@4.4.13': + resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} engines: {node: '>=18.0.0'} - '@smithy/core@3.23.6': - resolution: {integrity: sha512-4xE+0L2NrsFKpEVFlFELkIHQddBvMbQ41LRIP74dGCXnY1zQ9DgksrBcRBDJT+iOzGy4VEJIeU3hkUK5mn06kg==} + '@smithy/core@3.23.12': + resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.2.10': - resolution: {integrity: sha512-3bsMLJJLTZGZqVGGeBVFfLzuRulVsGTj12BzRKODTHqUABpIr0jMN1vN3+u6r2OfyhAQ2pXaMZWX/swBK5I6PQ==} + '@smithy/credential-provider-imds@4.2.12': + resolution: {integrity: sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-codec@4.2.10': - resolution: {integrity: sha512-A4ynrsFFfSXUHicfTcRehytppFBcY3HQxEGYiyGktPIOye3Ot7fxpiy4VR42WmtGI4Wfo6OXt/c1Ky1nUFxYYQ==} + '@smithy/eventstream-codec@4.2.12': + resolution: {integrity: sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@4.2.10': - resolution: {integrity: sha512-0xupsu9yj9oDVuQ50YCTS9nuSYhGlrwqdaKQel9y2Fz7LU9fNErVlw9N0o4pm4qqvWEGbSTI4HKc6XJfB30MVw==} + '@smithy/eventstream-serde-browser@4.2.12': + resolution: {integrity: sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@4.3.10': - resolution: {integrity: sha512-8kn6sinrduk0yaYHMJDsNuiFpXwQwibR7n/4CDUqn4UgaG+SeBHu5jHGFdU9BLFAM7Q4/gvr9RYxBHz9/jKrhA==} + '@smithy/eventstream-serde-config-resolver@4.3.12': + resolution: {integrity: sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@4.2.10': - resolution: {integrity: sha512-uUrxPGgIffnYfvIOUmBM5i+USdEBRTdh7mLPttjphgtooxQ8CtdO1p6K5+Q4BBAZvKlvtJ9jWyrWpBJYzBKsyQ==} + '@smithy/eventstream-serde-node@4.2.12': + resolution: {integrity: sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@4.2.10': - resolution: {integrity: sha512-aArqzOEvcs2dK+xQVCgLbpJQGfZihw8SD4ymhkwNTtwKbnrzdhJsFDKuMQnam2kF69WzgJYOU5eJlCx+CA32bw==} + '@smithy/eventstream-serde-universal@4.2.12': + resolution: {integrity: sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.11': - resolution: {integrity: sha512-wbTRjOxdFuyEg0CpumjZO0hkUl+fetJFqxNROepuLIoijQh51aMBmzFLfoQdwRjxsuuS2jizzIUTjPWgd8pd7g==} + '@smithy/fetch-http-handler@5.3.15': + resolution: {integrity: sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==} engines: {node: '>=18.0.0'} - '@smithy/hash-blob-browser@4.2.11': - resolution: {integrity: sha512-DrcAx3PM6AEbWZxsKl6CWAGnVwiz28Wp1ZhNu+Hi4uI/6C1PIZBIaPM2VoqBDAsOWbM6ZVzOEQMxFLLdmb4eBQ==} + '@smithy/hash-blob-browser@4.2.13': + resolution: {integrity: sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.2.10': - resolution: {integrity: sha512-1VzIOI5CcsvMDvP3iv1vG/RfLJVVVc67dCRyLSB2Hn9SWCZrDO3zvcIzj3BfEtqRW5kcMg5KAeVf1K3dR6nD3w==} + '@smithy/hash-node@4.2.12': + resolution: {integrity: sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==} engines: {node: '>=18.0.0'} - '@smithy/hash-stream-node@4.2.10': - resolution: {integrity: sha512-w78xsYrOlwXKwN5tv1GnKIRbHb1HygSpeZMP6xDxCPGf1U/xDHjCpJu64c5T35UKyEPwa0bPeIcvU69VY3khUA==} + '@smithy/hash-stream-node@4.2.12': + resolution: {integrity: sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.2.10': - resolution: {integrity: sha512-vy9KPNSFUU0ajFYk0sDZIYiUlAWGEAhRfehIr5ZkdFrRFTAuXEPUd41USuqHU6vvLX4r6Q9X7MKBco5+Il0Org==} + '@smithy/invalid-dependency@4.2.12': + resolution: {integrity: sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@4.2.1': - resolution: {integrity: sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==} + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} engines: {node: '>=18.0.0'} - '@smithy/md5-js@4.2.10': - resolution: {integrity: sha512-Op+Dh6dPLWTjWITChFayDllIaCXRofOed8ecpggTC5fkh8yXes0vAEX7gRUfjGK+TlyxoCAA05gHbZW/zB9JwQ==} + '@smithy/md5-js@4.2.12': + resolution: {integrity: sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.10': - resolution: {integrity: sha512-TQZ9kX5c6XbjhaEBpvhSvMEZ0klBs1CFtOdPFwATZSbC9UeQfKHPLPN9Y+I6wZGMOavlYTOlHEPDrt42PMSH9w==} + '@smithy/middleware-content-length@4.2.12': + resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.20': - resolution: {integrity: sha512-9W6Np4ceBP3XCYAGLoMCmn8t2RRVzuD1ndWPLBbv7H9CrwM9Bprf6Up6BM9ZA/3alodg0b7Kf6ftBK9R1N04vw==} + '@smithy/middleware-endpoint@4.4.27': + resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.37': - resolution: {integrity: sha512-/1psZZllBBSQ7+qo5+hhLz7AEPGLx3Z0+e3ramMBEuPK2PfvLK4SrncDB9VegX5mBn+oP/UTDrM6IHrFjvX1ZA==} + '@smithy/middleware-retry@4.4.44': + resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.11': - resolution: {integrity: sha512-STQdONGPwbbC7cusL60s7vOa6He6A9w2jWhoapL0mgVjmR19pr26slV+yoSP76SIssMTX/95e5nOZ6UQv6jolg==} + '@smithy/middleware-serde@4.2.15': + resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.2.10': - resolution: {integrity: sha512-pmts/WovNcE/tlyHa8z/groPeOtqtEpp61q3W0nW1nDJuMq/x+hWa/OVQBtgU0tBqupeXq0VBOLA4UZwE8I0YA==} + '@smithy/middleware-stack@4.2.12': + resolution: {integrity: sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.3.10': - resolution: {integrity: sha512-UALRbJtVX34AdP2VECKVlnNgidLHA2A7YgcJzwSBg1hzmnO/bZBHl/LDQQyYifzUwp1UOODnl9JJ3KNawpUJ9w==} + '@smithy/node-config-provider@4.3.12': + resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.12': - resolution: {integrity: sha512-zo1+WKJkR9x7ZtMeMDAAsq2PufwiLDmkhcjpWPRRkmeIuOm6nq1qjFICSZbnjBvD09ei8KMo26BWxsu2BUU+5w==} + '@smithy/node-http-handler@4.5.0': + resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@4.2.10': - resolution: {integrity: sha512-5jm60P0CU7tom0eNrZ7YrkgBaoLFXzmqB0wVS+4uK8PPGmosSrLNf6rRd50UBvukztawZ7zyA8TxlrKpF5z9jw==} + '@smithy/property-provider@4.2.12': + resolution: {integrity: sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.3.10': - resolution: {integrity: sha512-2NzVWpYY0tRdfeCJLsgrR89KE3NTWT2wGulhNUxYlRmtRmPwLQwKzhrfVaiNlA9ZpJvbW7cjTVChYKgnkqXj1A==} + '@smithy/protocol-http@5.3.12': + resolution: {integrity: sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==} engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@4.2.10': - resolution: {integrity: sha512-HeN7kEvuzO2DmAzLukE9UryiUvejD3tMp9a1D1NJETerIfKobBUCLfviP6QEk500166eD2IATaXM59qgUI+YDA==} + '@smithy/querystring-builder@4.2.12': + resolution: {integrity: sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@4.2.10': - resolution: {integrity: sha512-4Mh18J26+ao1oX5wXJfWlTT+Q1OpDR8ssiC9PDOuEgVBGloqg18Fw7h5Ct8DyT9NBYwJgtJ2nLjKKFU6RP1G1Q==} + '@smithy/querystring-parser@4.2.12': + resolution: {integrity: sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.2.10': - resolution: {integrity: sha512-0R/+/Il5y8nB/By90o8hy/bWVYptbIfvoTYad0igYQO5RefhNCDmNzqxaMx7K1t/QWo0d6UynqpqN5cCQt1MCg==} + '@smithy/service-error-classification@4.2.12': + resolution: {integrity: sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.4.5': - resolution: {integrity: sha512-pHgASxl50rrtOztgQCPmOXFjRW+mCd7ALr/3uXNzRrRoGV5G2+78GOsQ3HlQuBVHCh9o6xqMNvlIKZjWn4Euug==} + '@smithy/shared-ini-file-loader@4.4.7': + resolution: {integrity: sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.3.10': - resolution: {integrity: sha512-Wab3wW8468WqTKIxI+aZe3JYO52/RYT/8sDOdzkUhjnLakLe9qoQqIcfih/qxcF4qWEFoWBszY0mj5uxffaVXA==} + '@smithy/signature-v4@5.3.12': + resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.12.0': - resolution: {integrity: sha512-R8bQ9K3lCcXyZmBnQqUZJF4ChZmtWT5NLi6x5kgWx5D+/j0KorXcA0YcFg/X5TOgnTCy1tbKc6z2g2y4amFupQ==} + '@smithy/smithy-client@4.12.7': + resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==} engines: {node: '>=18.0.0'} - '@smithy/types@4.13.0': - resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} + '@smithy/types@4.13.1': + resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@4.2.10': - resolution: {integrity: sha512-uypjF7fCDsRk26u3qHmFI/ePL7bxxB9vKkE+2WKEciHhz+4QtbzWiHRVNRJwU3cKhrYDYQE3b0MRFtqfLYdA4A==} + '@smithy/url-parser@4.2.12': + resolution: {integrity: sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.3.1': - resolution: {integrity: sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w==} + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@4.2.1': - resolution: {integrity: sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g==} + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@4.2.2': - resolution: {integrity: sha512-4rHqBvxtJEBvsZcFQSPQqXP2b/yy/YlB66KlcEgcH2WNoOKCKB03DSLzXmOsXjbl8dJ4OEYTn31knhdznwk7zw==} + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@4.2.1': - resolution: {integrity: sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==} + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@4.2.1': - resolution: {integrity: sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==} + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.36': - resolution: {integrity: sha512-R0smq7EHQXRVMxkAxtH5akJ/FvgAmNF6bUy/GwY/N20T4GrwjT633NFm0VuRpC+8Bbv8R9A0DoJ9OiZL/M3xew==} + '@smithy/util-defaults-mode-browser@4.3.43': + resolution: {integrity: sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.39': - resolution: {integrity: sha512-otWuoDm35btJV1L8MyHrPl462B07QCdMTktKc7/yM+Psv6KbED/ziXiHnmr7yPHUjfIwE9S8Max0LO24Mo3ZVg==} + '@smithy/util-defaults-mode-node@4.2.47': + resolution: {integrity: sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.3.1': - resolution: {integrity: sha512-xyctc4klmjmieQiF9I1wssBWleRV0RhJ2DpO8+8yzi2LO1Z+4IWOZNGZGNj4+hq9kdo+nyfrRLmQTzc16Op2Vg==} + '@smithy/util-endpoints@3.3.3': + resolution: {integrity: sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==} engines: {node: '>=18.0.0'} - '@smithy/util-hex-encoding@4.2.1': - resolution: {integrity: sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==} + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.2.10': - resolution: {integrity: sha512-LxaQIWLp4y0r72eA8mwPNQ9va4h5KeLM0I3M/HV9klmFaY2kN766wf5vsTzmaOpNNb7GgXAd9a25P3h8T49PSA==} + '@smithy/util-middleware@4.2.12': + resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.2.10': - resolution: {integrity: sha512-HrBzistfpyE5uqTwiyLsFHscgnwB0kgv8vySp7q5kZ0Eltn/tjosaSGGDj/jJ9ys7pWzIP/icE2d+7vMKXLv7A==} + '@smithy/util-retry@4.2.12': + resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.15': - resolution: {integrity: sha512-OlOKnaqnkU9X+6wEkd7mN+WB7orPbCVDauXOj22Q7VtiTkvy7ZdSsOg4QiNAZMgI4OkvNf+/VLUC3VXkxuWJZw==} + '@smithy/util-stream@4.5.20': + resolution: {integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==} engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@4.2.1': - resolution: {integrity: sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q==} + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.2.1': - resolution: {integrity: sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==} + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} engines: {node: '>=18.0.0'} - '@smithy/util-waiter@4.2.10': - resolution: {integrity: sha512-4eTWph/Lkg1wZEDAyObwme0kmhEb7J/JjibY2znJdrYRgKbKqB7YoEhhJVJ4R1g/SYih4zuwX7LpJaM8RsnTVg==} + '@smithy/util-waiter@4.2.13': + resolution: {integrity: sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==} engines: {node: '>=18.0.0'} - '@smithy/uuid@1.1.1': - resolution: {integrity: sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw==} + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} engines: {node: '>=18.0.0'} '@socket.io/component-emitter@3.1.0': @@ -4503,18 +4624,22 @@ packages: '@swc/types@0.1.25': resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - '@tabler/icons-react@3.36.1': - resolution: {integrity: sha512-/8nOXeNeMoze9xY/QyEKG65wuvRhkT3q9aytaur6Gj8bYU2A98YVJyLc9MRmc5nVvpy+bRlrrwK/Ykr8WGyUWg==} + '@tabler/icons-react@3.40.0': + resolution: {integrity: sha512-oO5+6QCnna4a//mYubx4euZfECtzQZFDGsDMIdzZUhbdyBCT+3bRVFBPueGIcemWld4Vb/0UQ39C/cmGfGylAg==} peerDependencies: react: '>= 16' - '@tabler/icons@3.36.1': - resolution: {integrity: sha512-f4Jg3Fof/Vru5ioix/UO4GX+sdDsF9wQo47FbtvG+utIYYVQ/QVAC0QYgcBbAjQGfbdOh2CCf0BgiFOF9Ixtjw==} + '@tabler/icons@3.40.0': + resolution: {integrity: sha512-V/Q4VgNPKubRTiLdmWjV/zscYcj5IIk+euicUtaVVqF6luSC9rDngYWgST5/yh3Mrg/mYUwRv1YVTk71Jp0twQ==} - '@tanstack/eslint-plugin-query@5.62.1': - resolution: {integrity: sha512-1886D5U+re1TW0wSH4/kUGG36yIoW5Wkz4twVEzlk3ZWmjF3XkRSWgB+Sc7n+Lyzt8usNV8ZqkZE6DA7IC47fQ==} + '@tanstack/eslint-plugin-query@5.94.4': + resolution: {integrity: sha512-iWebDii7oy+izhlMnmQDBkaDuXd1YlrUJNtK3x4lO4uGyZOn4VdCRIhTfA2SJLDsPTbxK+7a6rkDbKma+8IHWA==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + typescript: ^5.4.0 + peerDependenciesMeta: + typescript: + optional: true '@tanstack/query-core@5.90.17': resolution: {integrity: sha512-hDww+RyyYhjhUfoYQ4es6pbgxY7LNiPWxt4l1nJqhByjndxJ7HIjDxTBtfvMr5HwjYavMrd+ids5g4Rfev3lVQ==} @@ -4524,251 +4649,251 @@ packages: peerDependencies: react: ^18 || ^19 - '@tiptap/core@3.17.1': - resolution: {integrity: sha512-f8hB9MzXqsuXoF9qXEDEH5Fb3VgwhEFMBMfk9EKN88l5adri6oM8mt2XOWVxVVssjpEW0177zXSLPKWzoS/vrw==} + '@tiptap/core@3.20.4': + resolution: {integrity: sha512-3i/DG89TFY/b34T5P+j35UcjYuB5d3+9K8u6qID+iUqNPiza015HPIZLuPfE5elNwVdV3EXIoPo0LLeBLgXXAg==} peerDependencies: - '@tiptap/pm': 3.17.1 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-blockquote@3.17.1': - resolution: {integrity: sha512-X4jU/fllJQ8QbjCHUafU4QIHBobyXP3yGBoOcXxUaKlWbLvUs0SQTREM3n6/86m2YyAxwTPG1cn3Xypf42DMAQ==} + '@tiptap/extension-blockquote@3.20.4': + resolution: {integrity: sha512-9sskyyhYj2oKat//lyZVXCp9YrPt4oJAZnGHYWXS0xlskjsLElrfKKlM4vpbhGss3VrhQRoEGqWLnIaJYPF1zw==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-bold@3.17.0': - resolution: {integrity: sha512-Tpf3o7qTMjM1B7NV6QXAjRdn64vbFCsFfwLF3Tt5tY2TxqcwDLktx11XPvbqdEloOeA8deJA26jKdh7Dc3ZBxA==} + '@tiptap/extension-bold@3.20.4': + resolution: {integrity: sha512-Md7/mNAeJCY+VLJc8JRGI+8XkVPKiOGB1NgqQPdh3aYtxXQDChQOZoJEQl6TuudDxZ85bLZB67NjZlx3jo8/0g==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-bubble-menu@3.17.1': - resolution: {integrity: sha512-z3E8biLiWlzZJwNHnB6j/ZyBdFrJmpl1lqKHc72JqahUHZvidZHdCOYssvR3fc6IaI7MXV13XY1DXUdFbatnaw==} + '@tiptap/extension-bubble-menu@3.20.4': + resolution: {integrity: sha512-EXywPlI8wjPcAb8ozymgVhjtMjFrnhtoyNTy8ZcObdpUi5CdO9j892Y7aPbKe5hLhlDpvJk7rMfir4FFKEmfng==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-bullet-list@3.17.1': - resolution: {integrity: sha512-2zw17XHruOJQK7ntLVq0PmOLajFhvQ+U4/qTfJnV3VOsHkm+2GPAksFe7I7+X0XmSmDru0pcT339Yywx/6Aykw==} + '@tiptap/extension-bullet-list@3.20.4': + resolution: {integrity: sha512-1RTGrur1EKoxfnLZ3M6xeNj8GITAz74jH2DHGcjLsd2Xr7Q7BozGaIq6GkkvKguMwbI1zCOxTHFCpUETXAIQQA==} peerDependencies: - '@tiptap/extension-list': 3.17.1 + '@tiptap/extension-list': ^3.20.4 - '@tiptap/extension-code-block@3.17.1': - resolution: {integrity: sha512-h4i+Y/cN7nMi0Tmlp6V1w4dI7NTqrUFSr1W/vMqnq4vn+c6jvm35KubKU5ry/1qQp8KfndDA02BtVQiMx6DmpA==} + '@tiptap/extension-code-block@3.20.4': + resolution: {integrity: sha512-Zlw3FrXTy01+o1yISeX/LC+iJeHA+ym602bMXGmtA6lyl7QSOSO7WExweJ6xeJGhbCjldwT5al6fkRAs8iGJZg==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-code@3.17.1': - resolution: {integrity: sha512-4W0x1ZZqSnIVzQV0/b5VR0bktef2HykH5I/Czzir9yqoZ5zV2cLrMVuLvdFNgRIckU60tQLmHrfKWLF50OY0ew==} + '@tiptap/extension-code@3.20.4': + resolution: {integrity: sha512-7j8Hi964bH1SZ9oLdZC1fkqWz27mliSDV7M8lmL/M14+Qw42D/VOAKS4Aw9OCFtHMlTsjLR6qsoVxL8Lpkt6NA==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-collaboration-caret@3.17.1': - resolution: {integrity: sha512-tYzujG4ABacSbjd8QOqMt1IP3QdCmAEBHP2faF4SeFauaP6Nto88JvTiZVCHad0BBwiNrj4UPGZSujcNQiLjTA==} + '@tiptap/extension-collaboration-caret@3.20.4': + resolution: {integrity: sha512-JuuNOhwE6fbbJDNKqU5ul+KC5ddWdZNWlJdLgGA6K6Z8btyOMZKcWtT6rLjYYHmojP4qZ1K/niMBy2/n8B5ZcQ==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 '@tiptap/y-tiptap': ^3.0.2 - '@tiptap/extension-collaboration@3.17.1': - resolution: {integrity: sha512-4ehZ5LL7M3nFfcogCG7bWRHIR/8366i1vz5i0PaaoArJga2N5sXnWcuBGXG7ykC8owbgrfL3agFxjHlhTl4sNw==} + '@tiptap/extension-collaboration@3.20.4': + resolution: {integrity: sha512-6KSZ5hCpscT4losDeRbZ2G9xifgMvSGJZnw+tFPgmNrbZToKMSbPnKMUTx4DYUiB2yTDv/fJOrcCFaQ5EpbHBA==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 '@tiptap/y-tiptap': ^3.0.2 yjs: ^13 - '@tiptap/extension-color@3.17.1': - resolution: {integrity: sha512-QVlzpzGB+QcZgHgvFMRPckZutpkOLzNmZzhupNA7G2CMeeoCwZOJeZkyd3zvtAnRZkf7FrQBO123On30pJt7TA==} + '@tiptap/extension-color@3.20.4': + resolution: {integrity: sha512-+OT9wWEJnqoWmzfqPYt0oWm8LZcH+D44Z3jA2TNzBj4tLGQ2YPxN2SyS12AlRi7MuguVT7utFy7qDXrfir8eUA==} peerDependencies: - '@tiptap/extension-text-style': ^3.17.1 + '@tiptap/extension-text-style': ^3.20.4 - '@tiptap/extension-document@3.17.1': - resolution: {integrity: sha512-F7Q5HoAU383HWFa6AXZQ5N6t6lTJzVjYM8z93XrtH/2GzDFwy1UmDSrsXqvgznedBLAOgCNVTNh9PjXpLoOUbg==} + '@tiptap/extension-document@3.20.4': + resolution: {integrity: sha512-zF1CIFVLt8MfSpWWnPwtGyxPOsT0xYM2qJKcXf2yZcTG37wDKmUi6heG53vGigIavbQlLaAFvs+1mNdOu2x/0A==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-dropcursor@3.19.0': - resolution: {integrity: sha512-sf3dEZXiLvsGqVK2maUIzXY6qtYYCvBumag7+VPTMGQ0D4hiZ1X/4ukt4+6VXDg5R2WP1CoIt/QvUetUjWNhbQ==} + '@tiptap/extension-dropcursor@3.20.4': + resolution: {integrity: sha512-TgMwvZ8myXYdmd6bUV7qkpZXv7ZUiSmX/8eo+iPEzYo2CnDLAGvDKgC50nfq/g87SDvfBgPuAiBfFvsMQQWaTw==} peerDependencies: - '@tiptap/extensions': ^3.19.0 + '@tiptap/extensions': ^3.20.4 - '@tiptap/extension-floating-menu@3.19.0': - resolution: {integrity: sha512-JaoEkVRkt+Slq3tySlIsxnMnCjS0L5n1CA1hctjLy0iah8edetj3XD5mVv5iKqDzE+LIjF4nwLRRVKJPc8hFBg==} + '@tiptap/extension-floating-menu@3.20.4': + resolution: {integrity: sha512-AaPTFhoO8DBIElJyd/RTVJjkctvJuL+GHURX0npbtTxXq5HXbebVwf2ARNR7jMd/GThsmBaNJiGxZg4A2oeDqQ==} peerDependencies: '@floating-ui/dom': ^1.0.0 - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-gapcursor@3.19.0': - resolution: {integrity: sha512-w7DACS4oSZaDWjz7gropZHPc9oXqC9yERZTcjWxyORuuIh1JFf0TRYspleK+OK28plK/IftojD/yUDn1MTRhvA==} + '@tiptap/extension-gapcursor@3.20.4': + resolution: {integrity: sha512-JJ6f1iQ1e0s4kISgq55U3UYGwWV/N9f0PYMtB6e3L+SBQjXnywaLK0g6vfN6IvTCC2vdIuqeSOX8VlSO97sJLw==} peerDependencies: - '@tiptap/extensions': ^3.19.0 + '@tiptap/extensions': ^3.20.4 - '@tiptap/extension-hard-break@3.19.0': - resolution: {integrity: sha512-lAmQraYhPS5hafvCl74xDB5+bLuNwBKIEsVoim35I0sDJj5nTrfhaZgMJ91VamMvT+6FF5f1dvBlxBxAWa8jew==} + '@tiptap/extension-hard-break@3.20.4': + resolution: {integrity: sha512-gJbq58d8zB1gzyqVEopowej5CpW4/Fpg6oGJvlZxaCukqd0gJRWGC89K+jE62YA1Td4sfcKrekKvN7jm2y/ZUg==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-heading@3.17.1': - resolution: {integrity: sha512-rT+Su/YnHdlikg8f78t6RXlc1sVSfp7B0fdJdtFgS2e6BBYJQoDMp5L9nt54RR9Yy953aDW2sko7NArUCb8log==} + '@tiptap/extension-heading@3.20.4': + resolution: {integrity: sha512-xsnkmTGggJc5P2iCwS1lv8KFG31xC/GNPJKoi/3UH67j/lKDhA3AdtshsLeyv2FKtTtYDb8oV0IqzHB1MM6a7w==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-highlight@3.17.1': - resolution: {integrity: sha512-I4EdBhPVzJd4ECMI9kP0NE4aG4Numd46jy/AqeZyf3dqVgCxRyAbSyU7oy4aXUnsojYODrKKG6+djm07KgOGoQ==} + '@tiptap/extension-highlight@3.20.4': + resolution: {integrity: sha512-CyTVPorVWwE4v89+k1nmaoAvjXLo7/fYWBsYlHW6b9Y1Un0iLANgKMFmmuapyfpaqpvg7V0Eg5ElG9U9+rogVA==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-history@3.17.1': - resolution: {integrity: sha512-YHW4HP9ovZ/zqc1u3+cDdAY/LITaMQNRnX5foLsDFLV5FU+zqonYo2CqDkVwaQs9UfCp9PM0ehZzxMI8hc58oA==} + '@tiptap/extension-history@3.20.4': + resolution: {integrity: sha512-wpdbt06r18tJyvgEMNWYN9eEwugfeS3P0M1tjrfBzEcYESCfH8htWD6Mh5ZE1y9jl4FjiOCF9QkkHCuTyDBAJQ==} peerDependencies: - '@tiptap/extensions': ^3.17.1 + '@tiptap/extensions': ^3.20.4 - '@tiptap/extension-horizontal-rule@3.19.0': - resolution: {integrity: sha512-iqUHmgMGhMgYGwG6L/4JdelVQ5Mstb4qHcgTGd/4dkcUOepILvhdxajPle7OEdf9sRgjQO6uoAU5BVZVC26+ng==} + '@tiptap/extension-horizontal-rule@3.20.4': + resolution: {integrity: sha512-y6joCi49haAA0bo3EGUY+dWUMHH1GPUc84hxrBY/0pMs+Bn+kQ1+DQJErZDTWGJrlHPWU/yekBZT72SNdp0DNA==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-image@3.17.1': - resolution: {integrity: sha512-VbSSZ//5qijm8F0lQQ6K+DGnZgjLKYQY2c+O56QNEoN8BaCFrJlsVgF1ttrSRUmoG4XBNIMlAS07kZXvMZQr0g==} + '@tiptap/extension-image@3.20.4': + resolution: {integrity: sha512-57w2TevHQljTh6Xiry9duIm7NNOQAUSTwtwRn4GGLoKwHR8qXTxzp513ASrFOgR2kgs2TP471Au6RHf947P+jg==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-italic@3.19.0': - resolution: {integrity: sha512-6GffxOnS/tWyCbDkirWNZITiXRta9wrCmrfa4rh+v32wfaOL1RRQNyqo9qN6Wjyl1R42Js+yXTzTTzZsOaLMYA==} + '@tiptap/extension-italic@3.20.4': + resolution: {integrity: sha512-4ZqiWr7cmqPFux8tj1ZLiYytyWf343IvQemNX6AvVWvscrJcrfj3YX4Le2BA0RW3A3M6RpLQXXozuF8vxYFDeQ==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-link@3.17.1': - resolution: {integrity: sha512-5kdN7vms5hMXtjiophUkgvzy8dNGvGSmol1Sawh30TEPrgXc93Ayj7YyGZlbimInKZcD8q+Od/FFc+wkrof3nA==} + '@tiptap/extension-link@3.20.4': + resolution: {integrity: sha512-JNDSkWrVdb8NSvbQXwHWvK5tCMbTWwOHFOweknQZ1JPK4dei9FJVofYQaHyW4bJBdcCjds3NZSnXE8DM9iAWmg==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-list-item@3.19.0': - resolution: {integrity: sha512-VsSKuJz4/Tb6ZmFkXqWpDYkRzmaLTyE6dNSEpNmUpmZ32sMqo58mt11/huADNwfBFB0Ve7siH/VnFNIJYY3xvg==} + '@tiptap/extension-list-item@3.20.4': + resolution: {integrity: sha512-QoTc5RACXaZF+vIIBBxjGO7D0oWFUDgBKJCpvUZ0CoGGKosnfe4a9I5THFyLj4201cf0oUqgf1oZhTqETGxlVw==} peerDependencies: - '@tiptap/extension-list': 3.17.1 + '@tiptap/extension-list': ^3.20.4 - '@tiptap/extension-list-keymap@3.19.0': - resolution: {integrity: sha512-bxgmAgA3RzBGA0GyTwS2CC1c+QjkJJq9hC+S6PSOWELGRiTbwDN3MANksFXLjntkTa0N5fOnL27vBHtMStURqw==} + '@tiptap/extension-list-keymap@3.20.4': + resolution: {integrity: sha512-RIqXM649+8IP7p/KVfaGlJiwjCylm1m6OPlaoM3K8O7oEOGRQzNeexexECCD2jsXRxew4E+vBNMD2orXqJmu8A==} peerDependencies: - '@tiptap/extension-list': 3.17.1 + '@tiptap/extension-list': ^3.20.4 - '@tiptap/extension-list@3.17.1': - resolution: {integrity: sha512-LHKIxmXe5Me+vJZKhiwMBGHlApaBIAduNMRUpm5mkY7ER/m96zKR0VqrJd4LjVVH2iDvck5h1Ka4396MHWlKNg==} + '@tiptap/extension-list@3.20.4': + resolution: {integrity: sha512-X+5plTKhOioNcQ4KsAFJJSb/3+zR8Xhdpow4HzXtoV1KcbdDey1fhZdpsfkbrzCL0s6/wAgwZuAchCK7HujurQ==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-ordered-list@3.19.0': - resolution: {integrity: sha512-cxGsINquwHYE1kmhAcLNLHAofmoDEG6jbesR5ybl7tU5JwtKVO7S/xZatll2DU1dsDAXWPWEeeMl4e/9svYjCg==} + '@tiptap/extension-ordered-list@3.20.4': + resolution: {integrity: sha512-3budNL8BgBon3TcXZ4hjT0YpFvx1Ka3uSIECKDxHgES+OQcR+6cagxSb60gFEccf3Dr0PIwcVTY6g14lC1qKRQ==} peerDependencies: - '@tiptap/extension-list': 3.17.1 + '@tiptap/extension-list': ^3.20.4 - '@tiptap/extension-paragraph@3.19.0': - resolution: {integrity: sha512-xWa6gj82l5+AzdYyrSk9P4ynySaDzg/SlR1FarXE5yPXibYzpS95IWaVR0m2Qaz7Rrk+IiYOTGxGRxcHLOelNg==} + '@tiptap/extension-paragraph@3.20.4': + resolution: {integrity: sha512-lm6fOScWuZAF/Sfp97igUwFd3L1QHIVLAWP5NVdh0DTLrEIt4rMBmsww+yOpMQRhvz2uTgMbMXynrimhzi/QVw==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-placeholder@3.17.1': - resolution: {integrity: sha512-cE8Rij5/1t4KnWE7GaDewhBek9DKNB+97yrxyggMegILg6v195hOmOkRZkyfnFMYZoBDlrfSAtX9wBvbZBqIsg==} + '@tiptap/extension-placeholder@3.20.4': + resolution: {integrity: sha512-GB0KWtqm83YHG8cnqBLijvUBm+xvLfQHDfFRRH2fb3EzH3eIsM9jKRC31ADT27RSV1zVpHMFGcP3/pWpdrN1Lw==} peerDependencies: - '@tiptap/extensions': ^3.17.1 + '@tiptap/extensions': ^3.20.4 - '@tiptap/extension-strike@3.19.0': - resolution: {integrity: sha512-xYpabHsv7PccLUBQaP8AYiFCnYbx6P93RHPd0lgNwhdOjYFd931Zy38RyoxPHAgbYVmhf1iyx7lpuLtBnhS5dA==} + '@tiptap/extension-strike@3.20.4': + resolution: {integrity: sha512-It1Px9uDGTsVqyyg6cy7DigLoenljpQwqdI0jssM7QclZrHnsrye9fZxBBiiuCzzV1305MxKgHvratkHwqmVNA==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-subscript@3.17.1': - resolution: {integrity: sha512-+y/sl1d+TcecX2n1r6ZTjBmY3D6cfqAW86iKsvudCFSpp9SQk85RaumPzELOXWOjz9g0mtfUnXifrLYF3dS+vA==} + '@tiptap/extension-subscript@3.20.4': + resolution: {integrity: sha512-Sx7YyqovrbMB7bFjWkAkMH1iV2XhlgVuJ5Mz1q5jbnPHb1kB2I1vIRWxlh22FtJhSYxHlkGH69OBn9In/zsycg==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-superscript@3.17.1': - resolution: {integrity: sha512-FKt+lI1ocFRW0EFla9EuO71aLQINpkC/wt9zxWnJJnfPIWfxYlsTSFJLjLkVungTmwfeCnoCVcXnZ0dSKDnoGg==} + '@tiptap/extension-superscript@3.20.4': + resolution: {integrity: sha512-eJUAf8HJAaT/NeHPYeDogKd/3YWy5sMOGKktp+Gk/5t6XrLGy6KYs/avfcNuFg2QJwSCWpLaZBVfmbdLJI0rxg==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-table@3.17.1': - resolution: {integrity: sha512-FuAMdmM330tHJUYT5IV2ooFRqtXf+0D8llcE9nIQQCXKL4J0pfGSOIm40LVpunYgx2pV8SSCL51qTBuEmR84tQ==} + '@tiptap/extension-table@3.20.4': + resolution: {integrity: sha512-vEHXRL9k9G02pp3P+DyUnN4YRaRAHGfTBC6gck0s9TpsCM9NIchL0qI1fb/u46Bu6UaoMMk58DGr7xaJ29g7KQ==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-text-align@3.17.1': - resolution: {integrity: sha512-CyJbZf823dqPZ/1zwRsza5pk/NQwFZwILdFYLVkV88I4+Ua9YVztI9kmwTB6dJyuKT4kTc7nhQHdaa957alGZQ==} + '@tiptap/extension-text-align@3.20.4': + resolution: {integrity: sha512-6ZuRyClIyCimXu+S5LQ54DueEsYg5VOVOmubOVbG+WAjM9svn9Z8gv2sNDah2yEqXrX06B02zYcSyMiD7CHbfA==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-text-style@3.17.1': - resolution: {integrity: sha512-TCMsEU92r/TfZkN8AKo/WIcJ1uNq/5NiZxloq5drF1HXxDDjwliurgwBw3OTGUlKQmer0N9hV0AAePY/G+5Akw==} + '@tiptap/extension-text-style@3.20.4': + resolution: {integrity: sha512-PvW0Ja7ahWpo4bRuR8YCCVv4PH8lXjzhzlBAa4bMbsumOg+GbhX8Su7fwqd+IIPrHqfPXz9HTBMApSfzP6/08A==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-text@3.17.1': - resolution: {integrity: sha512-rGml96vokQbvPB+w6L3+WKyYJWwqELaLdFUr1WMgg+py5uNYGJYAExYNAbDb5biWJBrX9GgMlCaNeiJj849L1w==} + '@tiptap/extension-text@3.20.4': + resolution: {integrity: sha512-jchJcBZixDEO2J66Zx5dchsI2mA6IYsROqF8P1poxL4ienH7RVQRCTsBNnSfIeOtREKKWeOU/tEs5fcpvvGwIQ==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-typography@3.17.1': - resolution: {integrity: sha512-bEocTrK/gryk3VtthC9Ca03p2kutVIIFnDkVW6iOG8PgQWEspuQRgqE8yPnHxY8pBBDWxiaBzcGTSrp+3U9d5A==} + '@tiptap/extension-typography@3.20.4': + resolution: {integrity: sha512-78dlLJViuZehM2P5p9H3HRN6GMARvFQFkdnJrJsC6b6J7SN10ruytCMM7xnUnHlZElz19LU/0/+LDtMrOA814w==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-underline@3.19.0': - resolution: {integrity: sha512-800MGEWfG49j10wQzAFiW/ele1HT04MamcL8iyuPNu7ZbjbGN2yknvdrJlRy7hZlzIrVkZMr/1tz62KN33VHIw==} + '@tiptap/extension-underline@3.20.4': + resolution: {integrity: sha512-0OjMc3FDujX16G+jhvqcY/mLot8SrNtDu8ggUwNLAfiI/QIvMVgk7giFD71DATC/4Nb8i/iwAEegTD8MxBIXCg==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extension-unique-id@3.19.0': - resolution: {integrity: sha512-gzRW4LHkRq90k8DXte0GqkUj10d5032xf73+i5B4TeNTFiQup4LTPBHmx9WAum6up9Uz3qCZ7fq0pkkRoyT8zg==} + '@tiptap/extension-unique-id@3.20.4': + resolution: {integrity: sha512-mMtebmFhLdfDLRm/Ngu1qiNQHlxbMmx1cy4hTVnCx33sBTK/AZJod4/b5NFELRHvDwThM60Th8dbxf0e9JjQ0A==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/extension-youtube@3.17.1': - resolution: {integrity: sha512-AarpN4vI/S6jPMuLuFGEFLgdoasGiUW+rGLj+jH/0Of6l27nKRN00MTm/fD/62qjR6At3Rd7Xsue/GuXdmDUWw==} + '@tiptap/extension-youtube@3.20.4': + resolution: {integrity: sha512-CGwoYqaZKAlY0nju0Bq7ynlGs2GJCg4/oWHAAwgNSdTrCqz8SHa1keh99ZYHy2wmVvFVXOUipIfhI6+u3E81Bg==} peerDependencies: - '@tiptap/core': 3.17.1 + '@tiptap/core': ^3.20.4 - '@tiptap/extensions@3.19.0': - resolution: {integrity: sha512-ZmGUhLbMWaGqnJh2Bry+6V4M6gMpUDYo4D1xNux5Gng/E/eYtc+PMxMZ/6F7tNTAuujLBOQKj6D+4SsSm457jw==} + '@tiptap/extensions@3.20.4': + resolution: {integrity: sha512-8p6hVT65DjuQjtEdlH6ewX9SOJHlVQAOee3sWIJQmeJNRnZNvqPIBLleebUqDiljNTpxBv6s6QWkSTKgf3btwg==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 - '@tiptap/html@3.17.1': - resolution: {integrity: sha512-fLb2fo8+3oQ+5FTx5IGZvLI5+VLgN9BM6pHaO1+IrwqQ5w2RBFIGp8M946asBPkxJ74EtzHqFKJpVFtaY2CcpA==} + '@tiptap/html@3.20.4': + resolution: {integrity: sha512-EXysba9tDD2Ay/S+83BhR97rlSiH9gvRMaCVdqx/QuFln28y72A522esCbrIH6JL/k1os4bFriji2egdzZFP+w==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 happy-dom: ^20.0.2 - '@tiptap/pm@3.17.1': - resolution: {integrity: sha512-UyVLkN8axV/zop6Se2DCBJRu5DM21X0XEQvwEC5P/vk8eC9OcQZ3FLtxeYy2ZjpAZUzBGLw0/BGsmEip/n7olw==} + '@tiptap/pm@3.20.4': + resolution: {integrity: sha512-rCHYSBToilBEuI6PtjziHDdRkABH/XqwJ7dG4Amn/SD3yGiZKYCiEApQlTUS2zZeo8DsLeuqqqB4vEOeD4OEPg==} - '@tiptap/react@3.17.1': - resolution: {integrity: sha512-Hn/pIP3HG9xYnhI3iGrfVhgQhfIdOaEBSxOFzJ37patqSOlIoP5aZH/b2HZ4vgo5DdRlV56q7WtRC+vLIw4Neg==} + '@tiptap/react@3.20.4': + resolution: {integrity: sha512-1B8iWsHWwb5TeyVaUs8BRPzwWo4PsLQcl03urHaz0zTJ8DauopqvxzV3+lem1OkzRHn7wnrapDvwmIGoROCaQw==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tiptap/starter-kit@3.17.1': - resolution: {integrity: sha512-3vBGqag9mwuQoWTrfQlULtHeoFs7k/2Q8CREf3Y79hv2fqAXTvTOKlWYPSgZhiGVMp6Dti7BDiE9Y1QpvAat2g==} + '@tiptap/starter-kit@3.20.4': + resolution: {integrity: sha512-WcyK6hsTl8eBsQhQ+d9Sq8fYZKOYdL+D45MyH3hz583elXqJlW3h3JPFYb0o87gddGxn8Mm57OA/gA1zEdeDMw==} - '@tiptap/suggestion@3.17.1': - resolution: {integrity: sha512-a188uVYjlLsUiwK3Ki7KsaWVWC0u28KsqGEAqCk9ECYmtVY99Hrb+rcAwGpMjA7tn8WAwThOxiLISoMdpuqXwg==} + '@tiptap/suggestion@3.20.4': + resolution: {integrity: sha512-7uqgLwjEAvsosrfoVrYVBQtAKI2pJFHOM8wgsQMLv1pQL8mfRz5ByD2xet4DOKb23q4mDvrvzp6z0kvCp0hMDw==} peerDependencies: - '@tiptap/core': 3.17.1 - '@tiptap/pm': 3.17.1 + '@tiptap/core': ^3.20.4 + '@tiptap/pm': ^3.20.4 '@tiptap/y-tiptap@3.0.2': resolution: {integrity: sha512-flMn/YW6zTbc6cvDaUPh/NfLRTXDIqgpBUkYzM74KA1snqQwhOMjnRcnpu4hDFrTnPO6QGzr99vRyXEA7M44WA==} @@ -4805,9 +4930,6 @@ packages: '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} - '@types/asn1@0.2.4': - resolution: {integrity: sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -4820,8 +4942,8 @@ packages: '@types/babel__traverse@7.20.5': resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} - '@types/bcrypt@5.0.2': - resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==} + '@types/bcrypt@6.0.0': + resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} '@types/blueimp-load-image@5.16.6': resolution: {integrity: sha512-e7s6CdDCUoBQdCe62Q6OS+DF68M8+ABxCEMh2Isjt4Fl3xuddljCHMN8mak48AMSVGGwUUtNRaZbkzgL5PEWew==} @@ -4997,14 +5119,14 @@ packages: '@types/jsonfile@6.1.4': resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/jsonwebtoken@9.0.10': + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + '@types/jsonwebtoken@9.0.6': resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} - '@types/jsonwebtoken@9.0.7': - resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==} - - '@types/katex@0.16.7': - resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/katex@0.16.8': + resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -5021,8 +5143,8 @@ packages: '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} - '@types/mime-types@2.1.4': - resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + '@types/mime-types@3.0.1': + resolution: {integrity: sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==} '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -5033,17 +5155,14 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@20.19.19': - resolution: {integrity: sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg==} - - '@types/node@22.13.4': - resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==} - '@types/node@22.19.1': resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} - '@types/nodemailer@6.4.17': - resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==} + '@types/node@25.5.0': + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + + '@types/nodemailer@7.0.11': + resolution: {integrity: sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==} '@types/oauth@0.9.6': resolution: {integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==} @@ -5051,8 +5170,8 @@ packages: '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - '@types/passport-google-oauth20@2.0.16': - resolution: {integrity: sha512-ayXK2CJ7uVieqhYOc6k/pIr5pcQxOLB6kBev+QUGS7oEZeTgIs1odDobXRqgfBPvXzl0wXCQHftV5220czZCPA==} + '@types/passport-google-oauth20@2.0.17': + resolution: {integrity: sha512-MHNOd2l7gOTCn3iS+wInPQMiukliAUvMpODO3VlXxOiwNEMSyzV7UNvAdqxSN872o8OXx1SqPDVT6tLW74AtqQ==} '@types/passport-jwt@4.0.1': resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==} @@ -5072,8 +5191,8 @@ packages: '@types/prop-types@15.7.11': resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - '@types/qrcode@1.5.5': - resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} + '@types/qrcode@1.5.6': + resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -5144,113 +5263,63 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.17.0': - resolution: {integrity: sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w==} + '@typescript-eslint/eslint-plugin@8.57.1': + resolution: {integrity: sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser': ^8.57.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/eslint-plugin@8.24.1': - resolution: {integrity: sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==} + '@typescript-eslint/parser@8.57.1': + resolution: {integrity: sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.17.0': - resolution: {integrity: sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg==} + '@typescript-eslint/project-service@8.57.1': + resolution: {integrity: sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.24.1': - resolution: {integrity: sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==} + '@typescript-eslint/scope-manager@8.57.1': + resolution: {integrity: sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.57.1': + resolution: {integrity: sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.17.0': - resolution: {integrity: sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/scope-manager@8.24.1': - resolution: {integrity: sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/type-utils@8.17.0': - resolution: {integrity: sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw==} + '@typescript-eslint/type-utils@8.57.1': + resolution: {integrity: sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.24.1': - resolution: {integrity: sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==} + '@typescript-eslint/types@8.57.1': + resolution: {integrity: sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.57.1': + resolution: {integrity: sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.17.0': - resolution: {integrity: sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/types@8.24.1': - resolution: {integrity: sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.17.0': - resolution: {integrity: sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==} + '@typescript-eslint/utils@8.57.1': + resolution: {integrity: sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.24.1': - resolution: {integrity: sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/utils@8.17.0': - resolution: {integrity: sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@8.24.1': - resolution: {integrity: sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/visitor-keys@8.17.0': - resolution: {integrity: sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/visitor-keys@8.24.1': - resolution: {integrity: sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==} + '@typescript-eslint/visitor-keys@8.57.1': + resolution: {integrity: sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ucast/core@1.10.2': @@ -5363,15 +5432,25 @@ packages: cpu: [x64] os: [win32] + '@upsetjs/venn.js@2.0.0': + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + '@vercel/oidc@3.1.0': resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} - '@vitejs/plugin-react@5.1.1': - resolution: {integrity: sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==} + '@vitejs/plugin-react@6.0.1': + resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -5474,6 +5553,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + address@1.2.2: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} @@ -5482,14 +5566,18 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} - ai-sdk-ollama@3.7.0: - resolution: {integrity: sha512-RtiOsAjfjykqtqp0vpnkkUe/lSlnZy7BEapuxZl6VnHwo8hq+DqRn35RpABoAI+eCBjzLioBcZk/msnhwfc8CA==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ai-sdk-ollama@3.8.1: + resolution: {integrity: sha512-DeNG88t3Knsu0AFNU8qe20tp82W+9yczgjduSltIEFWvo2mKuR3MeUNjg5/iGz2IBOkYKnni8pDoI19OxOCN/Q==} engines: {node: '>=22'} peerDependencies: - ai: ^6.0.80 + ai: ^6.0.116 - ai@6.0.86: - resolution: {integrity: sha512-U2W2LBCHA/pr0Ui7vmmsjBiLEzBbZF3yVHNy7Rbzn7IX+SvoQPFM5rN74hhfVzZoE8zBuGD4nLLk+j0elGacvQ==} + ai@6.0.134: + resolution: {integrity: sha512-YalNEaavld/kE444gOcsMKXdVVRGEe0SK77fAFcWYcqLg+a7xKnEet8bdfrEAJTfnMjj01rhgrIL10903w1a5Q==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -5497,7 +5585,7 @@ packages: ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: - ajv: ^8.0.0 + ajv: 8.18.0 peerDependenciesMeta: ajv: optional: true @@ -5505,7 +5593,7 @@ packages: ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: - ajv: ^8.0.0 + ajv: 8.18.0 peerDependenciesMeta: ajv: optional: true @@ -5513,18 +5601,18 @@ packages: ajv-keywords@3.5.2: resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} peerDependencies: - ajv: ^6.9.1 + ajv: 6.14.0 ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: - ajv: ^8.8.2 + ajv: 8.18.0 - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} alfaaz@1.1.0: resolution: {integrity: sha512-J/P07R41APslK7NmD5303bwStN8jpRA4DdvtLeAr1Jhfj6XWGrASUWI0G6jbWjJAZyw3Lu1Pb4J8rsM/cb+xDQ==} @@ -5556,10 +5644,6 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - ansis@4.2.0: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} @@ -5585,6 +5669,10 @@ packages: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + array-includes@3.1.8: resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} @@ -5600,8 +5688,8 @@ packages: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} engines: {node: '>= 0.4'} array.prototype.tosorted@1.1.4: @@ -5612,12 +5700,13 @@ packages: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} - async-lock@1.4.1: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} @@ -5644,11 +5733,11 @@ packages: avvio@9.1.0: resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} - axios@1.13.5: - resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + axios@1.13.6: + resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} - babel-jest@30.2.0: - resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==} + babel-jest@30.3.0: + resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 @@ -5662,8 +5751,8 @@ packages: resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} engines: {node: '>=12'} - babel-plugin-jest-hoist@30.2.0: - resolution: {integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==} + babel-plugin-jest-hoist@30.3.0: + resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} babel-plugin-macros@3.1.0: @@ -5699,8 +5788,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-jest@30.2.0: - resolution: {integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==} + babel-preset-jest@30.3.0: + resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-beta.1 @@ -5775,11 +5864,6 @@ packages: browser-fs-access@0.29.1: resolution: {integrity: sha512-LSvVX5e21LRrXqVMhqtAwj5xPgDb+fXAIH80NsnCQ9xuZPs2xWsOREi24RKgZa1XOiQRbcmVrv87+ulOKsgjxw==} - browserslist@4.24.2: - resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5807,8 +5891,8 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - bullmq@5.70.1: - resolution: {integrity: sha512-HjfGHfICkAClrFL0Y07qNbWcmiOCv1l+nusupXUjrvTPuDEyPEJ23MP0lUwUs/QEy1a3pWt/P/sCsSZ1RjRK+w==} + bullmq@5.71.0: + resolution: {integrity: sha512-aeNWh4drsafSKnAJeiNH/nZP/5O8ZdtdMbnOPZmpjXj7NZUP5YC901U3bIH41iZValm7d1i3c34ojv7q31m30w==} bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} @@ -5825,6 +5909,10 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -5845,9 +5933,6 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001684: - resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==} - caniuse-lite@1.0.30001769: resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} @@ -5891,6 +5976,9 @@ packages: chevrotain@11.0.3: resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} + chevrotain@11.1.2: + resolution: {integrity: sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -6018,8 +6106,8 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - comment-json@4.2.5: - resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} + comment-json@4.4.1: + resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} engines: {node: '>= 6'} component-emitter@1.3.1: @@ -6028,8 +6116,8 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@9.1.2: - resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} + concurrently@9.2.1: + resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} engines: {node: '>=18'} hasBin: true @@ -6138,6 +6226,11 @@ packages: resolution: {integrity: sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==} engines: {node: '>=18.x'} + cross-env@10.1.0: + resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} + engines: {node: '>=20'} + hasBin: true + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -6150,6 +6243,10 @@ packages: resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} engines: {node: '>= 8'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} @@ -6179,8 +6276,8 @@ packages: peerDependencies: cytoscape: ^3.2.0 - cytoscape@3.30.2: - resolution: {integrity: sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw==} + cytoscape@3.33.1: + resolution: {integrity: sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==} engines: {node: '>=0.10'} d3-array@2.12.1: @@ -6322,8 +6419,8 @@ packages: resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} engines: {node: '>=12'} - dagre-d3-es@7.0.13: - resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==} + dagre-d3-es@7.0.14: + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} @@ -6333,14 +6430,26 @@ packages: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} engines: {node: '>= 0.4'} + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} @@ -6407,8 +6516,8 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} dedent@1.7.1: resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} @@ -6459,6 +6568,10 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -6547,9 +6660,6 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} @@ -6564,9 +6674,6 @@ packages: electron-to-chromium@1.5.286: resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} - electron-to-chromium@1.5.65: - resolution: {integrity: sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==} - emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -6580,9 +6687,6 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -6647,8 +6751,8 @@ packages: resolution: {integrity: sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==} engines: {node: '>= 0.4'} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -6659,25 +6763,17 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.0: - resolution: {integrity: sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==} + es-iterator-helpers@1.3.1: + resolution: {integrity: sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==} engines: {node: '>= 0.4'} es-module-lexer@2.0.0: resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} @@ -6693,14 +6789,15 @@ packages: resolution: {integrity: sha512-VHErXfzR/6r/+yyzPKeBvO0lgjfC5cbDCQWjWwMZWSb6YU39TGIl51OUmCfWCq4ylMdJSB8zkz2vIuIeIxXApA==} engines: {node: '>=0.10.0'} - esbuild@0.25.0: - resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true - escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + engines: {node: '>=18'} + hasBin: true escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} @@ -6721,25 +6818,29 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-prettier@10.0.1: - resolution: {integrity: sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-react-hooks@5.1.0: - resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-refresh@0.4.16: - resolution: {integrity: sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==} + eslint-plugin-react-refresh@0.5.2: + resolution: {integrity: sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==} peerDependencies: - eslint: '>=8.40' + eslint: ^9 || ^10 - eslint-plugin-react@7.37.2: - resolution: {integrity: sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==} + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 @@ -6756,16 +6857,16 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-visitor-keys@4.2.1: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.2: - resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -6783,8 +6884,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -6837,8 +6938,12 @@ packages: resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - express-rate-limit@8.2.1: - resolution: {integrity: sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==} + expect@30.3.0: + resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + express-rate-limit@8.2.2: + resolution: {integrity: sha512-Ybv7bqtOgA914MLwaHWVFXMpMYeR1MQu/D+z2MaLYteqBsTIp9sY3AU7mGNLMJv8eLg8uQMpE20I+L2Lv49nSg==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -6867,10 +6972,6 @@ packages: resolution: {integrity: sha512-d+yU9iNQbbC098NOuMlAIth/g+owbpX/uuOkH/DQcC2fMMyjOlX292Op29DrUKq388m4UUyOdWakUH/msGypOg==} engines: {node: '>=6.0.0'} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -6889,8 +6990,11 @@ packages: fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} - fast-xml-parser@5.3.6: - resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==} + fast-xml-builder@1.1.4: + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + + fast-xml-parser@5.5.8: + resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} hasBin: true fastify-plugin@5.0.1: @@ -6899,8 +7003,8 @@ packages: fastify-plugin@5.1.0: resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==} - fastify@5.7.4: - resolution: {integrity: sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA==} + fastify@5.8.2: + resolution: {integrity: sha512-lZmt3navvZG915IE+f7/TIVamxIwmBd+OMB+O9WBzcpIwOo6F0LTh0sluoMFk5VkrKTvvrwIaoJPkir4Z+jtAg==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -6931,8 +7035,8 @@ packages: file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} - file-type@21.3.0: - resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} + file-type@21.3.2: + resolution: {integrity: sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==} engines: {node: '>=20'} filelist@1.0.4: @@ -6946,8 +7050,8 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} - find-my-way@9.4.0: - resolution: {integrity: sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w==} + find-my-way@9.5.0: + resolution: {integrity: sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==} engines: {node: '>=20'} find-up@4.1.0: @@ -6966,8 +7070,8 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true - flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} @@ -6981,9 +7085,9 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} fork-ts-checker-webpack-plugin@9.1.0: resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} @@ -7025,8 +7129,8 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} - fs-extra@11.3.3: - resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} engines: {node: '>=14.14'} fs-monkey@1.0.5: @@ -7044,6 +7148,10 @@ packages: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} @@ -7063,10 +7171,6 @@ packages: resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -7091,6 +7195,10 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + get-tsconfig@4.7.5: resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} @@ -7105,10 +7213,9 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - hasBin: true + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} @@ -7122,8 +7229,8 @@ packages: resolution: {integrity: sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==} engines: {node: '>=18'} - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + globals@17.4.0: + resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} engines: {node: '>=18'} globalthis@1.0.4: @@ -7140,9 +7247,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} @@ -7151,8 +7255,8 @@ packages: engines: {node: '>=0.4.7'} hasBin: true - happy-dom@20.1.0: - resolution: {integrity: sha512-ebvqjBqzenBk2LjzNEAzoj7yhw7rW/R2/wVevMu6Mrq3MXtcI/RUz4+ozpcOcqVLEWPqLfg2v9EAU7fFXZUUJw==} + happy-dom@20.8.4: + resolution: {integrity: sha512-GKhjq4OQCYB4VLFBzv8mmccUadwlAusOZOI7hC1D9xDIT5HhzkJK17c4el2f6R6C715P9xB4uiMxeKUa2nHMwQ==} engines: {node: '>=20.0.0'} has-bigints@1.0.2: @@ -7162,10 +7266,6 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-own-prop@2.0.0: - resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} - engines: {node: '>=8'} - has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -7173,8 +7273,8 @@ packages: resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} has-symbols@1.1.0: @@ -7196,6 +7296,12 @@ packages: help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + highlight.js@11.11.1: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} @@ -7206,8 +7312,8 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - hono@4.12.3: - resolution: {integrity: sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==} + hono@4.12.8: + resolution: {integrity: sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==} engines: {node: '>=16.9.0'} hookified@1.15.1: @@ -7248,19 +7354,24 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - i18next-http-backend@2.7.3: - resolution: {integrity: sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==} + i18next-http-backend@3.0.2: + resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==} - i18next@23.16.8: - resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} + i18next@25.10.1: + resolution: {integrity: sha512-d7MZx1UDamSmjbaqFg00w+EXUTqIB8x8cmYRGsAzQqXFVyrNFprGqPItANtlF6V1tuBFyZyp+4/q2MFqODWerg==} + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -7301,8 +7412,8 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + immutable@4.3.8: + resolution: {integrity: sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==} import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -7324,6 +7435,10 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + internmap@1.0.1: resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} @@ -7334,16 +7449,16 @@ packages: invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ioredis@5.4.1: - resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} + ioredis@5.10.1: + resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==} engines: {node: '>=12.22.0'} ioredis@5.9.3: resolution: {integrity: sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==} engines: {node: '>=12.22.0'} - ip-address@10.0.1: - resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -7358,6 +7473,10 @@ packages: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -7377,6 +7496,10 @@ packages: resolution: {integrity: sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==} engines: {node: '>= 0.4'} + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -7388,10 +7511,18 @@ packages: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -7441,6 +7572,10 @@ packages: resolution: {integrity: sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==} engines: {node: '>= 0.4'} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -7455,6 +7590,10 @@ packages: resolution: {integrity: sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==} engines: {node: '>= 0.4'} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} @@ -7463,6 +7602,10 @@ packages: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -7471,14 +7614,26 @@ packages: resolution: {integrity: sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==} engines: {node: '>= 0.4'} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + is-symbol@1.1.0: resolution: {integrity: sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==} engines: {node: '>= 0.4'} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + is-typed-array@1.1.13: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -7498,6 +7653,10 @@ packages: is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + is-weakset@2.0.3: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} @@ -7545,28 +7704,25 @@ packages: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} - iterator.prototype@1.1.3: - resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==} + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jake@10.8.7: resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} engines: {node: '>=10'} hasBin: true - jest-changed-files@30.2.0: - resolution: {integrity: sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==} + jest-changed-files@30.3.0: + resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-circus@30.2.0: - resolution: {integrity: sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==} + jest-circus@30.3.0: + resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-cli@30.2.0: - resolution: {integrity: sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==} + jest-cli@30.3.0: + resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -7575,8 +7731,8 @@ packages: node-notifier: optional: true - jest-config@30.2.0: - resolution: {integrity: sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==} + jest-config@30.3.0: + resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@types/node': '*' @@ -7594,38 +7750,54 @@ packages: resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-diff@30.3.0: + resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-docblock@30.2.0: resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-each@30.2.0: - resolution: {integrity: sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==} + jest-each@30.3.0: + resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-environment-node@30.2.0: - resolution: {integrity: sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==} + jest-environment-node@30.3.0: + resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-haste-map@30.2.0: - resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} + jest-haste-map@30.3.0: + resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-leak-detector@30.2.0: - resolution: {integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==} + jest-leak-detector@30.3.0: + resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-matcher-utils@30.2.0: resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-matcher-utils@30.3.0: + resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-message-util@30.2.0: resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-message-util@30.3.0: + resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-mock@30.2.0: resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-mock@30.3.0: + resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-pnp-resolver@1.2.3: resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} @@ -7639,48 +7811,52 @@ packages: resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve-dependencies@30.2.0: - resolution: {integrity: sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==} + jest-resolve-dependencies@30.3.0: + resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve@30.2.0: - resolution: {integrity: sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==} + jest-resolve@30.3.0: + resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runner@30.2.0: - resolution: {integrity: sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==} + jest-runner@30.3.0: + resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runtime@30.2.0: - resolution: {integrity: sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==} + jest-runtime@30.3.0: + resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-snapshot@30.2.0: - resolution: {integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==} + jest-snapshot@30.3.0: + resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-util@30.2.0: resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-validate@30.2.0: - resolution: {integrity: sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==} + jest-util@30.3.0: + resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-watcher@30.2.0: - resolution: {integrity: sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==} + jest-validate@30.3.0: + resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-watcher@30.3.0: + resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jest-worker@30.2.0: - resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==} + jest-worker@30.3.0: + resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest@30.2.0: - resolution: {integrity: sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==} + jest@30.3.0: + resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -7693,9 +7869,6 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - jose@4.15.9: - resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - jose@6.1.3: resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} @@ -7723,8 +7896,8 @@ packages: react: optional: true - jotai@2.16.2: - resolution: {integrity: sha512-DH0lBiTXvewsxtqqwjDW6Hg9JPTDnq9LcOsXSFWCAUEt+qj5ohl9iRVX9zQXPPHKLXCdH+5mGvM28fsXMl17/g==} + jotai@2.18.1: + resolution: {integrity: sha512-e0NOzK+yRFwHo7DOp0DS0Ycq74KMEAObDWFGmfEL28PD9nLqBTt3/Ug7jf9ca72x0gC9LQZG9zH+0ISICmy3iA==} engines: {node: '>=12.20.0'} peerDependencies: '@babel/core': '>=7.0.0' @@ -7763,11 +7936,11 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdom@25.0.1: - resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} engines: {node: '>=18'} peerDependencies: - canvas: ^2.11.2 + canvas: ^3.0.0 peerDependenciesMeta: canvas: optional: true @@ -7819,8 +7992,8 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - jsonrepair@3.13.2: - resolution: {integrity: sha512-Leuly0nbM4R+S5SVJk3VHfw1oxnlEK9KygdZvfUtEtTawNDyzB4qa1xWTmFt1aeoA7sXZkVTRuIixJ8bAvqVUg==} + jsonrepair@3.13.3: + resolution: {integrity: sha512-BTznj0owIt2CBAH/LTo7+1I5pMvl1e1033LRl/HUowlZmJOIhzC0zbX5bxMngLkfT4WnzPP26QnW5wMr2g9tsQ==} hasBin: true jsonwebtoken@9.0.3: @@ -7844,8 +8017,8 @@ packages: resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} engines: {node: '>=18'} - katex@0.16.27: - resolution: {integrity: sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==} + katex@0.16.40: + resolution: {integrity: sha512-1DJcK/L05k1Y9Gf7wMcyuqFOL6BiY3vY0CFcAM/LPRN04NALxcl6u7lOWNsp3f/bCHWxigzQl6FbR95XJ4R84Q==} hasBin: true keyv@4.5.4: @@ -7869,9 +8042,6 @@ packages: resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} engines: {node: '>= 8'} - kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - kysely-codegen@0.20.0: resolution: {integrity: sha512-LSi2KBG7uDmNCZ+XurLSA9LH7XFyyoQ6xb5DLJSInPTSYLVApjOP2KwO8mSaREWTtoX+C2AG2GTlYR0DLjTbcA==} engines: {node: '>=20.0.0'} @@ -7921,14 +8091,18 @@ packages: postgres: optional: true - kysely@0.28.2: - resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==} - engines: {node: '>=18.0.0'} + kysely@0.28.14: + resolution: {integrity: sha512-SU3lgh0rPvq7upc6vvdVrCsSMUG1h3ChvHVOY7wJ2fw4C9QEB7X3d5eyYEyULUX7UQtxZJtZXGuT6U2US72UYA==} + engines: {node: '>=20.0.0'} langium@3.3.1: resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} engines: {node: '>=16.0.0'} + langium@4.2.1: + resolution: {integrity: sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==} + engines: {node: '>=20.10.0', npm: '>=10.2.3'} + langsmith@0.5.7: resolution: {integrity: sha512-FjYf2oBGMoSXnaT4SRaFguIiGJaonZ5VKWKJDPl9awLZjz2RkN29AcQWceecSINVzXzTvtRWPOjAWT+XggqNNg==} peerDependencies: @@ -7952,9 +8126,9 @@ packages: layout-base@2.0.1: resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} - ldapts@7.4.0: - resolution: {integrity: sha512-QLgx2pLvxMXY1nCc85Fx+cwVJDvC0sQ3l4CJZSl1FJ/iV8Ypfl6m+5xz4lm1lhoXcUlvhPqxEoyIj/8LR6ut+A==} - engines: {node: '>=18'} + ldapts@8.1.7: + resolution: {integrity: sha512-TJl6T92eIwMf/OJ0hDfKVa6ISwzo+lqCWCI5Mf//ARlKa3LKQZaSrme/H2rCRBhW0DZCQlrsV+fgoW5YHRNLUw==} + engines: {node: '>=20'} leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} @@ -7986,6 +8160,76 @@ packages: light-my-request@6.6.0: resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -8007,10 +8251,6 @@ packages: resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} - local-pkg@1.1.2: - resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} - engines: {node: '>=14'} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -8025,9 +8265,6 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.deburr@4.1.0: - resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} - lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} @@ -8079,8 +8316,8 @@ packages: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} - loglevel@1.9.1: - resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} engines: {node: '>= 0.6.0'} long@5.3.2: @@ -8099,13 +8336,13 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.7: + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - luxon@3.7.2: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} @@ -8127,8 +8364,8 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - mammoth@1.11.0: - resolution: {integrity: sha512-BcEqqY/BOwIcI1iR5tqyVlqc3KIaMRa4egSoK83YAVrBf6+yqdAAbtUcFDCWX8Zef8/fgNZ6rl4VUv+vVX8ddQ==} + mammoth@1.12.0: + resolution: {integrity: sha512-cwnK1RIcRdDMi2HRx2EXGYlxqIEh0Oo3bLhorgnsVJi2UkbX1+jKxuBNR9PC5+JaX7EkmJxFPmo6mjLpqShI2w==} engines: {node: '>=12.0.0'} hasBin: true @@ -8143,11 +8380,6 @@ packages: resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} hasBin: true - marked@13.0.3: - resolution: {integrity: sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==} - engines: {node: '>= 18'} - hasBin: true - marked@15.0.12: resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} engines: {node: '>= 18'} @@ -8158,6 +8390,11 @@ packages: engines: {node: '>= 20'} hasBin: true + marked@17.0.5: + resolution: {integrity: sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg==} + engines: {node: '>= 20'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -8183,12 +8420,8 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - mermaid@11.12.2: - resolution: {integrity: sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==} + mermaid@11.13.0: + resolution: {integrity: sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw==} methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} @@ -8237,26 +8470,22 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} - minimatch@10.2.1: - resolution: {integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==} - engines: {node: 20 || >=22} + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + minimatch@5.1.8: + resolution: {integrity: sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==} engines: {node: '>=10'} - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} mitt@3.0.1: @@ -8282,8 +8511,8 @@ packages: msgpackr@1.11.5: resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==} - msgpackr@1.11.8: - resolution: {integrity: sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==} + msgpackr@1.11.9: + resolution: {integrity: sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw==} multimath@2.0.0: resolution: {integrity: sha512-toRx66cAMJ+Ccz7pMIg38xSIrtnbozk0dchXezwQDMgQmbGpfxjtv68H+L00iFL8hxDaVjrmwAFSb3I6bg8Q2g==} @@ -8296,18 +8525,8 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@3.3.3: - resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -8316,6 +8535,11 @@ packages: engines: {node: ^14 || ^16 || >=18} hasBin: true + nanoid@5.1.7: + resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} + engines: {node: ^18 || >=20} + hasBin: true + napi-postinstall@0.3.4: resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -8349,16 +8573,16 @@ packages: reflect-metadata: '*' rxjs: '>= 7' - nestjs-kysely@1.2.0: - resolution: {integrity: sha512-KseCGb0SXCzIYC+Hx3Z3d+kPAfSZCSK6j9UoqUV/gcBCPad9utC7itmoUw0/w5sV+Jf9pc1DKpgClP1IkflA4w==} + nestjs-kysely@3.1.2: + resolution: {integrity: sha512-m7gK37oza6yyRmOs6VWQ6Ri5wgqUnmWpMUByd1V8WLZcYCeVPgXGxzw3ABRWUIX/1kr+nZUhaAZRgAIhk2JXoQ==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 kysely: 0.x reflect-metadata: ^0.1.13 || ^0.2.2 - nestjs-pino@4.5.0: - resolution: {integrity: sha512-e54ChJMACSGF8gPYaHsuD07RW7l/OVoV6aI8Hqhpp0ZQ4WA8QY3eewL42JX7Z1U6rV7byNU7bGBV9l6d9V6PDQ==} + nestjs-pino@4.6.1: + resolution: {integrity: sha512-nuARXa0xpdJ1lY2+fgycIQr6H3g0VgqAWNK3xMYjOFcj2DoPETNXj0lV3Y86nRuI7BUfQp5PGiVoZvT4dTWbpQ==} engines: {node: '>= 14'} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 @@ -8396,17 +8620,14 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-machine-id@1.1.12: - resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} - - node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + node-readable-to-web-readable-stream@0.4.2: + resolution: {integrity: sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==} node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - nodemailer@7.0.12: - resolution: {integrity: sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==} + nodemailer@8.0.3: + resolution: {integrity: sha512-JQNBqvK+bj3NMhUFR3wmCl3SYcOeMotDiwDBvIoCuQdF0PvlIY0BH+FJ2CG7u4cXKPChplE78oowlH/Otsc4ZQ==} engines: {node: '>=6.0.0'} normalize-path@3.0.0: @@ -8426,8 +8647,8 @@ packages: nwsapi@2.2.16: resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} - nx@22.5.3: - resolution: {integrity: sha512-IaEPqdgaFBIr0Bfmnt6WAcX3t660sOuDXQ71lpoS8GgpD8cqX1LIW2ZyzEAdOvCP1iD6HCZehpofcVvaaL1GNQ==} + nx@22.6.1: + resolution: {integrity: sha512-b4eo52o5aCVt3oG6LPYvD2Cul3JFBMgr2p9OjMBIo6oU6QfSR693H2/UuUMepLtO6jcIniPKOcIrf6Ue8aXAww==} hasBin: true peerDependencies: '@swc-node/register': ^1.11.1 @@ -8443,6 +8664,9 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true + oauth4webapi@3.8.5: + resolution: {integrity: sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg==} + oauth@0.10.0: resolution: {integrity: sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==} @@ -8450,10 +8674,6 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-hash@2.2.0: - resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} - engines: {node: '>= 6'} - object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -8462,6 +8682,10 @@ packages: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -8470,22 +8694,22 @@ packages: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} - object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} engines: {node: '>= 0.4'} object.fromentries@2.0.8: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - oidc-token-hash@5.0.3: - resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} - engines: {node: ^10.13.0 || >=12.0.0} - ollama@0.6.3: resolution: {integrity: sha512-KEWEhIqE5wtfzEIZbDCLH51VFZ6Z3ZSa6sIOg/E/tBV8S51flyqBOXi+bRxlOYKDf8i327zG9eSTb8IJxvm3Zg==} @@ -8524,8 +8748,8 @@ packages: zod: optional: true - openid-client@5.7.1: - resolution: {integrity: sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==} + openid-client@6.8.2: + resolution: {integrity: sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA==} optics-ts@2.4.1: resolution: {integrity: sha512-HaYzMHvC80r7U/LqAd4hQyopDezC60PO2qF5GuIwALut2cl5rK1VWHsqTp0oqoJJWjiv6uXKqsO+Q2OO0C3MmQ==} @@ -8552,8 +8776,12 @@ packages: orderedmap@2.1.1: resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} - otpauth@9.4.1: - resolution: {integrity: sha512-+iVvys36CFsyXEqfNftQm1II7SW23W1wx9RwNk0Cd97lbvorqAhBDksb/0bYry087QMxjiuBS0wokdoZ0iUeAw==} + otpauth@9.5.0: + resolution: {integrity: sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} @@ -8567,9 +8795,9 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@6.2.0: - resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} - engines: {node: '>=18'} + p-limit@7.3.0: + resolution: {integrity: sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==} + engines: {node: '>=20'} p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} @@ -8591,9 +8819,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - package-manager-detector@1.3.0: resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} @@ -8621,9 +8846,6 @@ packages: parse5-parser-stream@7.1.2: resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} - parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} - parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -8660,6 +8882,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-expression-matcher@1.2.0: + resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==} + engines: {node: '>=14.0.0'} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -8671,9 +8897,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -8688,9 +8914,9 @@ packages: pause@0.0.1: resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} - pdfjs-dist@5.4.394: - resolution: {integrity: sha512-9ariAYGqUJzx+V/1W4jHyiyCep6IZALmDzoaTLZ6VNu8q9LWi1/ukhzHgE2Xsx96AZi0mbZuK4/ttIbqSbLypg==} - engines: {node: '>=20.16.0 || >=22.3.0'} + pdfjs-dist@5.5.207: + resolution: {integrity: sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==} + engines: {node: '>=20.19.0 || >=22.13.0 || >=24'} peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -8839,25 +9065,25 @@ packages: peerDependencies: postcss: ^8.4.21 - postcss-mixins@9.0.4: - resolution: {integrity: sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==} - engines: {node: '>=14.0'} + postcss-mixins@12.1.2: + resolution: {integrity: sha512-90pSxmZVfbX9e5xCv7tI5RV1mnjdf16y89CJKbf/hD7GyOz1FCxcYMl8ZYA8Hc56dbApTKKmU9HfvgfWdCxlwg==} + engines: {node: ^20.0 || ^22.0 || >=24.0} peerDependencies: postcss: ^8.2.14 - postcss-nested@6.0.1: - resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} - engines: {node: '>=12.0'} + postcss-nested@7.0.2: + resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} + engines: {node: '>=18.0'} peerDependencies: postcss: ^8.2.14 - postcss-preset-mantine@1.17.0: - resolution: {integrity: sha512-ji1PMDBUf2Vsx/HE5faMSs1+ff6qE6YRulTr4Ja+6HD3gop8rSMTCYdpN7KrdsEg079kfBKkO/PaKhG9uR0zwQ==} + postcss-preset-mantine@1.18.0: + resolution: {integrity: sha512-sP6/s1oC7cOtBdl4mw/IRKmKvYTuzpRrH/vT6v9enMU/EQEQ31eQnHcWtFghOXLH87AAthjL/Q75rLmin1oZoA==} peerDependencies: postcss: '>=8.0.0' - postcss-selector-parser@6.0.15: - resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} postcss-simple-vars@7.0.1: @@ -8866,12 +9092,8 @@ packages: peerDependencies: postcss: ^8.2.1 - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -8894,11 +9116,11 @@ packages: resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} engines: {node: '>=12'} - posthog-js@1.345.5: - resolution: {integrity: sha512-Hplt/aRD3DQLTQl3NxmS7V0jZPF18nnKJ4rST0qinz/6tXNCMemYqOVb4C/HYAXXWrtVVzGSJeNCNKvECFHHoQ==} + posthog-js@1.363.1: + resolution: {integrity: sha512-iaDtRxCs/FiB+RXe83uo7RZXgpLlyB6qFoNHl3bNMgRCgrPI2nkzx2m9Va1l30HHl/zA1kPOXSy2/tZC5Ql5kg==} - postmark@4.0.5: - resolution: {integrity: sha512-nerZdd3TwOH4CgGboZnlUM/q7oZk0EqpZgJL+Y3Nup8kHeaukxouQ6JcFF3EJEijc4QbuNv1TefGhboAKtf/SQ==} + postmark@4.0.7: + resolution: {integrity: sha512-DjNniUl1XNCGUKhCR98ePd5gv16rlUAVKKaU9TUqnE3hDSqfT9XDulu1idjagQmdyGscqnRtXk/puAEiYMeevg==} preact@10.28.3: resolution: {integrity: sha512-tCmoRkPQLpBeWzpmbhryairGnhW9tKV6c6gr/w+RhoRoKEJwsjzipwp//1oCpGPOchvSLaAPlpcJi9MwMmoPyA==} @@ -8907,16 +9129,6 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.4.1: - resolution: {integrity: sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==} - engines: {node: '>=14'} - hasBin: true - - prettier@3.5.1: - resolution: {integrity: sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==} - engines: {node: '>=14'} - hasBin: true - prettier@3.8.1: resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} engines: {node: '>=14'} @@ -8926,6 +9138,10 @@ packages: resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + pretty-format@30.3.0: + resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -8946,8 +9162,8 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - prosemirror-changeset@2.3.1: - resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} + prosemirror-changeset@2.4.0: + resolution: {integrity: sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==} prosemirror-collab@1.3.1: resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} @@ -9044,15 +9260,9 @@ packages: resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} engines: {node: '>=0.6'} - quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - query-selector-shadow-dom@1.0.1: resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -9069,9 +9279,6 @@ packages: '@types/react-dom': optional: true - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -9086,8 +9293,8 @@ packages: react: '>= 16.14' react-dom: '>= 16.14' - react-clear-modal@2.0.17: - resolution: {integrity: sha512-qN5w8aDdY/Hd96WATEX77SzorYJGPHfb3qy5vnkoEjNyeyjvBvIcj0LpfZSUFKWNw5012XHl3Afq7pDcPHPJjQ==} + react-clear-modal@2.0.18: + resolution: {integrity: sha512-Aiv8Bw5NVm19tlUt3RLV2a1I/ya+UlyEZjREosn5G887nnusnefT+ls4AXkuP8XLn1KOah6DrM5MemV7cXgwWg==} peerDependencies: '@types/react': ^16.8 || ^17 || ^18 || ^19 react: ^16.8 || ^17 || ^18 || ^19 @@ -9124,36 +9331,39 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-email@5.2.8: - resolution: {integrity: sha512-noPcnpl78vsyBnhiKCzxK9Mdsv7ncAYI80osS5kbMgaKH2IgPtPab5BzLJX6INXuiNk5ju+9YRnCjPoPTOHZjA==} + react-email@5.2.10: + resolution: {integrity: sha512-Ys8yR5/a0nXf5u2GlT2UV93PJHC3ZnuMnNebEn7I5UE9XfMFPtlpgDs02mPJOJn49fhJjDTWIUlZD1vmQPDgJg==} engines: {node: '>=20.0.0'} hasBin: true - react-error-boundary@4.1.2: - resolution: {integrity: sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==} + react-error-boundary@6.1.1: + resolution: {integrity: sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==} peerDependencies: - react: '>=16.13.1' + react: ^18.0.0 || ^19.0.0 react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - react-helmet-async@2.0.5: - resolution: {integrity: sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==} + react-helmet-async@3.0.0: + resolution: {integrity: sha512-nA3IEZfXiclgrz4KLxAhqJqIfFDuvzQwlKwpdmzZIuC1KNSghDEIXmyU0TKtbM+NafnkICcwx8CECFrZ/sL/1w==} peerDependencies: - react: ^16.6.0 || ^17.0.0 || ^18.0.0 + react: ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-i18next@15.0.1: - resolution: {integrity: sha512-NwxLqNM6CLbeGA9xPsjits0EnXdKgCRSS6cgkgOdNcPXqL+1fYNl8fBg1wmnnHvFy812Bt4IWTPE9zjoPmFj3w==} + react-i18next@16.5.8: + resolution: {integrity: sha512-2ABeHHlakxVY+LSirD+OiERxFL6+zip0PaHo979bgwzeHg27Sqc82xxXWIrSFmfWX0ZkrvXMHwhsi/NGUf5VQg==} peerDependencies: - i18next: '>= 23.2.3' + i18next: '>= 25.6.2' react: '>= 16.8.0' react-dom: '*' react-native: '*' + typescript: ^5 peerDependenciesMeta: react-dom: optional: true react-native: optional: true + typescript: + optional: true react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -9167,10 +9377,6 @@ packages: react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-refresh@0.18.0: - resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} - engines: {node: '>=0.10.0'} - react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -9191,15 +9397,15 @@ packages: '@types/react': optional: true - react-router-dom@7.12.0: - resolution: {integrity: sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==} + react-router-dom@7.13.1: + resolution: {integrity: sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' react-dom: '>=18' - react-router@7.12.0: - resolution: {integrity: sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==} + react-router@7.13.1: + resolution: {integrity: sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' @@ -9277,6 +9483,10 @@ packages: reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.7: resolution: {integrity: sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g==} engines: {node: '>= 0.4'} @@ -9298,6 +9508,10 @@ packages: resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -9306,10 +9520,6 @@ packages: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true - repeat-string@1.6.1: - resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} - engines: {node: '>=0.10'} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -9368,8 +9578,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfc6902@5.1.2: - resolution: {integrity: sha512-zxcb+PWlE8PwX0tiKE6zP97THQ8/lHmeiwucRrJ3YFupWEmp25RmFSlB1dNTqjkovwqG4iq+u1gzJMBS3um8mA==} + rfc6902@5.2.0: + resolution: {integrity: sha512-ZNQpVnWsdLMTCcLZcUY9ZBhxWuj7/H13EM7NL2gvbAMBXfLeN3iOLi3TWT1+8Or7OTLYXRDCy7sTEStk3j4KBA==} rfdc@1.3.1: resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} @@ -9377,9 +9587,9 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rollup@4.53.3: - resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + rolldown@1.0.0-rc.10: + resolution: {integrity: sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true rope-sequence@1.3.4: @@ -9395,15 +9605,9 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - rrweb-cssom@0.7.1: - resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} - rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} @@ -9417,16 +9621,28 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + safe-regex2@5.0.0: resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} @@ -9481,16 +9697,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} @@ -9500,8 +9706,9 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serialize-javascript@7.0.3: + resolution: {integrity: sha512-h+cZ/XXarqDgCjo+YSyQU/ulDEESGGf8AMK9pPNmhNSl/FzPl6L8pMp1leca5z6NuG6tvV/auC8/43tmovowww==} + engines: {node: '>=20.0.0'} serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} @@ -9521,6 +9728,10 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -9538,8 +9749,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} @@ -9553,10 +9765,6 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - side-channel@1.1.0: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} @@ -9588,8 +9796,8 @@ packages: resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} engines: {node: '>=10.0.0'} - socket.io-parser@4.2.4: - resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + socket.io-parser@4.2.6: + resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} engines: {node: '>=10.0.0'} socket.io@4.8.3: @@ -9646,6 +9854,10 @@ packages: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} @@ -9660,21 +9872,21 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} engines: {node: '>= 0.4'} @@ -9682,6 +9894,10 @@ packages: string.prototype.trimend@1.0.8: resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} @@ -9720,12 +9936,12 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - stripe@17.5.0: - resolution: {integrity: sha512-kcyeAkDFjGsVl17FqnG7q/+xIjt0ZjOo9Dm+q8deAvs2Xe4iAHrhxyoP4etUVFc+/LZJANjIPVR+ZOnt9hr/Ug==} + stripe@17.7.0: + resolution: {integrity: sha512-aT2BU9KkizY9SATf14WhhYVv2uOapBWX0OFWF4xvcj1mPaNotlSc2CsxpS4DS46ZueSppmCF5BX1sNYBtwBvfw==} engines: {node: '>=12.*'} - strnum@2.1.2: - resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + strnum@2.2.1: + resolution: {integrity: sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==} strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} @@ -9740,9 +9956,9 @@ packages: stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - sugarss@4.0.1: - resolution: {integrity: sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==} - engines: {node: '>=12.0'} + sugarss@5.0.1: + resolution: {integrity: sha512-ctS5RYCBVvPoZAnzIaX5QSShK8ZiZxD5HUqSxlusvEMC+QZQIPCPOIJg6aceFX+K2rf4+SH89eu++h1Zmsr2nw==} + engines: {node: '>=18.0'} peerDependencies: postcss: ^8.3.3 @@ -9877,17 +10093,13 @@ packages: resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} engines: {node: '>=14.16'} - tough-cookie@5.1.0: - resolution: {integrity: sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==} + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@5.0.0: - resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} - engines: {node: '>=18'} - tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} @@ -9899,14 +10111,8 @@ packages: truncate-utf8-bytes@1.0.2: resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} - ts-api-utils@1.3.0: - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' - - ts-api-utils@2.0.1: - resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -9977,8 +10183,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.19.3: - resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} engines: {node: '>=18.0.0'} hasBin: true @@ -10017,52 +10223,44 @@ packages: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.3: resolution: {integrity: sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==} engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + typed-array-length@1.0.7: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.17.0: - resolution: {integrity: sha512-409VXvFd/f1br1DCbuKNFqQpXICoTB+V51afcwG1pn1a3Cp92MqAUges3YjwEdQ0cMUoCIodjVDAYzyD8h3SYA==} + typescript-eslint@8.57.1: + resolution: {integrity: sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - typescript-eslint@8.24.1: - resolution: {integrity: sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - typescript@5.7.2: - resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} - engines: {node: '>=14.17'} - hasBin: true - - typescript@5.7.3: - resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} - engines: {node: '>=14.17'} - hasBin: true + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - typesense@2.1.0: - resolution: {integrity: sha512-a/IRTL+dRXlpRDU4UodyGj8hl5xBz3nKihVRd/KfSFAfFPGcpdX6lxIgwdXy3O6VLNNiEsN8YwIsPHQPVT0vNw==} + typesense@3.0.3: + resolution: {integrity: sha512-Cue72Hbz0Aj7DMNXzHIuitBKHWK12GprIFC/7AQ8kR73vJ4iNaKz4eUPICJmQOdWhblEeBv8Am4f9wqEDFd66A==} engines: {node: '>=18'} peerDependencies: '@babel/runtime': ^7.23.2 @@ -10096,17 +10294,21 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - underscore@1.13.7: - resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} - undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici@7.22.0: - resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici@7.24.0: + resolution: {integrity: sha512-jxytwMHhsbdpBXxLAcuu0fzlQeXCNnWdDyRHpvWsUl8vd98UwYdl9YTyn8/HcpcJPC3pwUveefsa3zTxyD/ERg==} engines: {node: '>=20.18.1'} unicode-canonical-property-names-ecmascript@2.0.0: @@ -10136,12 +10338,6 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.1.1: - resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -10222,6 +10418,10 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + hasBin: true + v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -10237,15 +10437,16 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@7.2.4: - resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} + vite@8.0.1: + resolution: {integrity: sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.0 + esbuild: ^0.27.0 jiti: '>=1.21.0' less: ^4.0.0 - lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' @@ -10256,12 +10457,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -10301,6 +10504,9 @@ packages: vscode-uri@3.0.8: resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} @@ -10365,10 +10571,6 @@ packages: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} - whatwg-url@14.0.0: - resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} - engines: {node: '>=18'} - whatwg-url@14.2.0: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} engines: {node: '>=18'} @@ -10383,10 +10585,18 @@ packages: resolution: {integrity: sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==} engines: {node: '>= 0.4'} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + which-builtin-type@1.2.0: resolution: {integrity: sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==} engines: {node: '>= 0.4'} + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + which-collection@1.0.2: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} @@ -10398,6 +10608,10 @@ packages: resolution: {integrity: sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==} engines: {node: '>= 0.4'} + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -10418,10 +10632,6 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -10523,9 +10733,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -10551,12 +10758,12 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yauzl@3.2.0: - resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==} + yauzl@3.2.1: + resolution: {integrity: sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A==} engines: {node: '>=12'} - yjs@13.6.29: - resolution: {integrity: sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==} + yjs@13.6.30: + resolution: {integrity: sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} yn@3.1.1: @@ -10588,6 +10795,12 @@ packages: peerDependencies: zod: ^3.25 || ^4 + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -10610,34 +10823,34 @@ snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} - '@adobe/css-tools@4.3.3': {} + '@adobe/css-tools@4.4.3': {} - '@ai-sdk/gateway@3.0.46(zod@4.3.6)': + '@ai-sdk/gateway@3.0.77(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) + '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) '@vercel/oidc': 3.1.0 zod: 4.3.6 - '@ai-sdk/google@3.0.29(zod@4.3.6)': + '@ai-sdk/google@3.0.52(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) + '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) zod: 4.3.6 - '@ai-sdk/openai-compatible@2.0.30(zod@4.3.6)': + '@ai-sdk/openai-compatible@2.0.37(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) + '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) zod: 4.3.6 - '@ai-sdk/openai@3.0.29(zod@4.3.6)': + '@ai-sdk/openai@3.0.47(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) + '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) zod: 4.3.6 - '@ai-sdk/provider-utils@4.0.15(zod@4.3.6)': + '@ai-sdk/provider-utils@4.0.21(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.8 '@standard-schema/spec': 1.1.0 @@ -10648,10 +10861,10 @@ snapshots: dependencies: json-schema: 0.4.0 - '@angular-devkit/core@19.1.7(chokidar@4.0.3)': + '@angular-devkit/core@19.2.17(chokidar@4.0.3)': dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) jsonc-parser: 3.3.1 picomatch: 4.0.2 rxjs: 7.8.1 @@ -10661,8 +10874,8 @@ snapshots: '@angular-devkit/core@19.2.19(chokidar@4.0.3)': dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) jsonc-parser: 3.3.1 picomatch: 4.0.2 rxjs: 7.8.1 @@ -10670,11 +10883,11 @@ snapshots: optionalDependencies: chokidar: 4.0.3 - '@angular-devkit/schematics-cli@19.2.19(@types/node@22.13.4)(chokidar@4.0.3)': + '@angular-devkit/schematics-cli@19.2.19(@types/node@25.5.0)(chokidar@4.0.3)': dependencies: '@angular-devkit/core': 19.2.19(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) - '@inquirer/prompts': 7.3.2(@types/node@22.13.4) + '@inquirer/prompts': 7.3.2(@types/node@25.5.0) ansi-colors: 4.1.3 symbol-observable: 4.0.0 yargs-parser: 21.1.1 @@ -10682,9 +10895,9 @@ snapshots: - '@types/node' - chokidar - '@angular-devkit/schematics@19.1.7(chokidar@4.0.3)': + '@angular-devkit/schematics@19.2.17(chokidar@4.0.3)': dependencies: - '@angular-devkit/core': 19.1.7(chokidar@4.0.3) + '@angular-devkit/core': 19.2.17(chokidar@4.0.3) jsonc-parser: 3.3.1 magic-string: 0.30.17 ora: 5.4.1 @@ -10707,8 +10920,6 @@ snapshots: package-manager-detector: 1.3.0 tinyexec: 1.0.1 - '@antfu/utils@9.2.0': {} - '@asamuzakjp/css-color@2.8.3': dependencies: '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) @@ -10720,20 +10931,20 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.4 + '@aws-sdk/types': 3.973.6 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.4 + '@aws-sdk/types': 3.973.6 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.4 + '@aws-sdk/types': 3.973.6 '@aws-sdk/util-locate-window': 3.535.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -10743,7 +10954,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.4 + '@aws-sdk/types': 3.973.6 '@aws-sdk/util-locate-window': 3.535.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -10751,7 +10962,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.4 + '@aws-sdk/types': 3.973.6 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -10760,430 +10971,432 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.4 + '@aws-sdk/types': 3.973.6 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.1000.0': + '@aws-sdk/client-s3@3.1014.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.15 - '@aws-sdk/credential-provider-node': 3.972.14 - '@aws-sdk/middleware-bucket-endpoint': 3.972.6 - '@aws-sdk/middleware-expect-continue': 3.972.6 - '@aws-sdk/middleware-flexible-checksums': 3.973.1 - '@aws-sdk/middleware-host-header': 3.972.6 - '@aws-sdk/middleware-location-constraint': 3.972.6 - '@aws-sdk/middleware-logger': 3.972.6 - '@aws-sdk/middleware-recursion-detection': 3.972.6 - '@aws-sdk/middleware-sdk-s3': 3.972.15 - '@aws-sdk/middleware-ssec': 3.972.6 - '@aws-sdk/middleware-user-agent': 3.972.15 - '@aws-sdk/region-config-resolver': 3.972.6 - '@aws-sdk/signature-v4-multi-region': 3.996.3 - '@aws-sdk/types': 3.973.4 - '@aws-sdk/util-endpoints': 3.996.3 - '@aws-sdk/util-user-agent-browser': 3.972.6 - '@aws-sdk/util-user-agent-node': 3.973.0 - '@smithy/config-resolver': 4.4.9 - '@smithy/core': 3.23.6 - '@smithy/eventstream-serde-browser': 4.2.10 - '@smithy/eventstream-serde-config-resolver': 4.3.10 - '@smithy/eventstream-serde-node': 4.2.10 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/hash-blob-browser': 4.2.11 - '@smithy/hash-node': 4.2.10 - '@smithy/hash-stream-node': 4.2.10 - '@smithy/invalid-dependency': 4.2.10 - '@smithy/md5-js': 4.2.10 - '@smithy/middleware-content-length': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-retry': 4.4.37 - '@smithy/middleware-serde': 4.2.11 - '@smithy/middleware-stack': 4.2.10 - '@smithy/node-config-provider': 4.3.10 - '@smithy/node-http-handler': 4.4.12 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-base64': 4.3.1 - '@smithy/util-body-length-browser': 4.2.1 - '@smithy/util-body-length-node': 4.2.2 - '@smithy/util-defaults-mode-browser': 4.3.36 - '@smithy/util-defaults-mode-node': 4.2.39 - '@smithy/util-endpoints': 3.3.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-retry': 4.2.10 - '@smithy/util-stream': 4.5.15 - '@smithy/util-utf8': 4.2.1 - '@smithy/util-waiter': 4.2.10 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/credential-provider-node': 3.972.24 + '@aws-sdk/middleware-bucket-endpoint': 3.972.8 + '@aws-sdk/middleware-expect-continue': 3.972.8 + '@aws-sdk/middleware-flexible-checksums': 3.974.3 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-location-constraint': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.8 + '@aws-sdk/middleware-sdk-s3': 3.972.23 + '@aws-sdk/middleware-ssec': 3.972.8 + '@aws-sdk/middleware-user-agent': 3.972.24 + '@aws-sdk/region-config-resolver': 3.972.9 + '@aws-sdk/signature-v4-multi-region': 3.996.11 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.10 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 + '@smithy/eventstream-serde-browser': 4.2.12 + '@smithy/eventstream-serde-config-resolver': 4.3.12 + '@smithy/eventstream-serde-node': 4.2.12 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-blob-browser': 4.2.13 + '@smithy/hash-node': 4.2.12 + '@smithy/hash-stream-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/md5-js': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.13 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.15': + '@aws-sdk/core@3.973.23': dependencies: - '@aws-sdk/types': 3.973.4 - '@aws-sdk/xml-builder': 3.972.8 - '@smithy/core': 3.23.6 - '@smithy/node-config-provider': 4.3.10 - '@smithy/property-provider': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/signature-v4': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-utf8': 4.2.1 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/xml-builder': 3.972.15 + '@smithy/core': 3.23.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/crc64-nvme@3.972.3': + '@aws-sdk/crc64-nvme@3.972.5': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.13': + '@aws-sdk/credential-provider-env@3.972.21': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/types': 3.973.4 - '@smithy/property-provider': 4.2.10 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.15': + '@aws-sdk/credential-provider-http@3.972.23': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/types': 3.973.4 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/node-http-handler': 4.4.12 - '@smithy/property-provider': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/util-stream': 4.5.15 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/types': 3.973.6 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.13': + '@aws-sdk/credential-provider-ini@3.972.23': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/credential-provider-env': 3.972.13 - '@aws-sdk/credential-provider-http': 3.972.15 - '@aws-sdk/credential-provider-login': 3.972.13 - '@aws-sdk/credential-provider-process': 3.972.13 - '@aws-sdk/credential-provider-sso': 3.972.13 - '@aws-sdk/credential-provider-web-identity': 3.972.13 - '@aws-sdk/nested-clients': 3.996.3 - '@aws-sdk/types': 3.973.4 - '@smithy/credential-provider-imds': 4.2.10 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/credential-provider-env': 3.972.21 + '@aws-sdk/credential-provider-http': 3.972.23 + '@aws-sdk/credential-provider-login': 3.972.23 + '@aws-sdk/credential-provider-process': 3.972.21 + '@aws-sdk/credential-provider-sso': 3.972.23 + '@aws-sdk/credential-provider-web-identity': 3.972.23 + '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.13': + '@aws-sdk/credential-provider-login@3.972.23': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 - '@aws-sdk/types': 3.973.4 - '@smithy/property-provider': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.14': + '@aws-sdk/credential-provider-node@3.972.24': dependencies: - '@aws-sdk/credential-provider-env': 3.972.13 - '@aws-sdk/credential-provider-http': 3.972.15 - '@aws-sdk/credential-provider-ini': 3.972.13 - '@aws-sdk/credential-provider-process': 3.972.13 - '@aws-sdk/credential-provider-sso': 3.972.13 - '@aws-sdk/credential-provider-web-identity': 3.972.13 - '@aws-sdk/types': 3.973.4 - '@smithy/credential-provider-imds': 4.2.10 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/credential-provider-env': 3.972.21 + '@aws-sdk/credential-provider-http': 3.972.23 + '@aws-sdk/credential-provider-ini': 3.972.23 + '@aws-sdk/credential-provider-process': 3.972.21 + '@aws-sdk/credential-provider-sso': 3.972.23 + '@aws-sdk/credential-provider-web-identity': 3.972.23 + '@aws-sdk/types': 3.973.6 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.13': + '@aws-sdk/credential-provider-process@3.972.21': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/types': 3.973.4 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.13': + '@aws-sdk/credential-provider-sso@3.972.23': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 - '@aws-sdk/token-providers': 3.999.0 - '@aws-sdk/types': 3.973.4 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/token-providers': 3.1014.0 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.13': + '@aws-sdk/credential-provider-web-identity@3.972.23': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 - '@aws-sdk/types': 3.973.4 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/lib-storage@3.1000.0(@aws-sdk/client-s3@3.1000.0)': + '@aws-sdk/lib-storage@3.1014.0(@aws-sdk/client-s3@3.1014.0)': dependencies: - '@aws-sdk/client-s3': 3.1000.0 - '@smithy/abort-controller': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/smithy-client': 4.12.0 + '@aws-sdk/client-s3': 3.1014.0 + '@smithy/abort-controller': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/smithy-client': 4.12.7 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.972.6': + '@aws-sdk/middleware-bucket-endpoint@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@aws-sdk/util-arn-parser': 3.972.2 - '@smithy/node-config-provider': 4.3.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-config-provider': 4.2.1 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.972.6': + '@aws-sdk/middleware-expect-continue@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.973.1': + '@aws-sdk/middleware-flexible-checksums@3.974.3': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.973.15 - '@aws-sdk/crc64-nvme': 3.972.3 - '@aws-sdk/types': 3.973.4 - '@smithy/is-array-buffer': 4.2.1 - '@smithy/node-config-provider': 4.3.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-stream': 4.5.15 - '@smithy/util-utf8': 4.2.1 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/crc64-nvme': 3.972.5 + '@aws-sdk/types': 3.973.6 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.972.6': + '@aws-sdk/middleware-host-header@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.972.6': + '@aws-sdk/middleware-location-constraint@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.972.6': + '@aws-sdk/middleware-logger@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.972.6': + '@aws-sdk/middleware-recursion-detection@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 + '@aws-sdk/types': 3.973.6 '@aws/lambda-invoke-store': 0.2.3 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.15': + '@aws-sdk/middleware-sdk-s3@3.972.23': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/types': 3.973.4 - '@aws-sdk/util-arn-parser': 3.972.2 - '@smithy/core': 3.23.6 - '@smithy/node-config-provider': 4.3.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/signature-v4': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/util-config-provider': 4.2.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-stream': 4.5.15 - '@smithy/util-utf8': 4.2.1 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/core': 3.23.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.972.6': + '@aws-sdk/middleware-ssec@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.15': + '@aws-sdk/middleware-user-agent@3.972.24': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/types': 3.973.4 - '@aws-sdk/util-endpoints': 3.996.3 - '@smithy/core': 3.23.6 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-retry': 4.2.12 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.3': + '@aws-sdk/nested-clients@3.996.13': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.15 - '@aws-sdk/middleware-host-header': 3.972.6 - '@aws-sdk/middleware-logger': 3.972.6 - '@aws-sdk/middleware-recursion-detection': 3.972.6 - '@aws-sdk/middleware-user-agent': 3.972.15 - '@aws-sdk/region-config-resolver': 3.972.6 - '@aws-sdk/types': 3.973.4 - '@aws-sdk/util-endpoints': 3.996.3 - '@aws-sdk/util-user-agent-browser': 3.972.6 - '@aws-sdk/util-user-agent-node': 3.973.0 - '@smithy/config-resolver': 4.4.9 - '@smithy/core': 3.23.6 - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/hash-node': 4.2.10 - '@smithy/invalid-dependency': 4.2.10 - '@smithy/middleware-content-length': 4.2.10 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-retry': 4.4.37 - '@smithy/middleware-serde': 4.2.11 - '@smithy/middleware-stack': 4.2.10 - '@smithy/node-config-provider': 4.3.10 - '@smithy/node-http-handler': 4.4.12 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-base64': 4.3.1 - '@smithy/util-body-length-browser': 4.2.1 - '@smithy/util-body-length-node': 4.2.2 - '@smithy/util-defaults-mode-browser': 4.3.36 - '@smithy/util-defaults-mode-node': 4.2.39 - '@smithy/util-endpoints': 3.3.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-retry': 4.2.10 - '@smithy/util-utf8': 4.2.1 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/middleware-host-header': 3.972.8 + '@aws-sdk/middleware-logger': 3.972.8 + '@aws-sdk/middleware-recursion-detection': 3.972.8 + '@aws-sdk/middleware-user-agent': 3.972.24 + '@aws-sdk/region-config-resolver': 3.972.9 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-endpoints': 3.996.5 + '@aws-sdk/util-user-agent-browser': 3.972.8 + '@aws-sdk/util-user-agent-node': 3.973.10 + '@smithy/config-resolver': 4.4.13 + '@smithy/core': 3.23.12 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/hash-node': 4.2.12 + '@smithy/invalid-dependency': 4.2.12 + '@smithy/middleware-content-length': 4.2.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-retry': 4.4.44 + '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-stack': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/node-http-handler': 4.5.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.43 + '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.972.6': + '@aws-sdk/region-config-resolver@3.972.9': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/config-resolver': 4.4.9 - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/config-resolver': 4.4.13 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/s3-request-presigner@3.1000.0': + '@aws-sdk/s3-request-presigner@3.1014.0': dependencies: - '@aws-sdk/signature-v4-multi-region': 3.996.3 - '@aws-sdk/types': 3.973.4 - '@aws-sdk/util-format-url': 3.972.6 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/protocol-http': 5.3.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 + '@aws-sdk/signature-v4-multi-region': 3.996.11 + '@aws-sdk/types': 3.973.6 + '@aws-sdk/util-format-url': 3.972.8 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/protocol-http': 5.3.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.3': + '@aws-sdk/signature-v4-multi-region@3.996.11': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.15 - '@aws-sdk/types': 3.973.4 - '@smithy/protocol-http': 5.3.10 - '@smithy/signature-v4': 5.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/middleware-sdk-s3': 3.972.23 + '@aws-sdk/types': 3.973.6 + '@smithy/protocol-http': 5.3.12 + '@smithy/signature-v4': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.999.0': + '@aws-sdk/token-providers@3.1014.0': dependencies: - '@aws-sdk/core': 3.973.15 - '@aws-sdk/nested-clients': 3.996.3 - '@aws-sdk/types': 3.973.4 - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@aws-sdk/core': 3.973.23 + '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/types': 3.973.6 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.973.4': + '@aws-sdk/types@3.973.6': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.972.2': + '@aws-sdk/util-arn-parser@3.972.3': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.996.3': + '@aws-sdk/util-endpoints@3.996.5': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-endpoints': 3.3.1 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-endpoints': 3.3.3 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.972.6': + '@aws-sdk/util-format-url@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/querystring-builder': 4.2.10 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.535.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.6': + '@aws-sdk/util-user-agent-browser@3.972.8': dependencies: - '@aws-sdk/types': 3.973.4 - '@smithy/types': 4.13.0 + '@aws-sdk/types': 3.973.6 + '@smithy/types': 4.13.1 bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.0': + '@aws-sdk/util-user-agent-node@3.973.10': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.15 - '@aws-sdk/types': 3.973.4 - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 + '@aws-sdk/middleware-user-agent': 3.972.24 + '@aws-sdk/types': 3.973.6 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.8': + '@aws-sdk/xml-builder@3.972.15': dependencies: - '@smithy/types': 4.13.0 - fast-xml-parser: 5.3.6 + '@smithy/types': 4.13.1 + fast-xml-parser: 5.5.8 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.3': {} @@ -11209,7 +11422,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -11236,7 +11449,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.24.2 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -11308,10 +11521,6 @@ snapshots: dependencies: '@babel/types': 7.28.5 - '@babel/helper-plugin-utils@7.25.9': {} - - '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-plugin-utils@7.28.6': {} '@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.28.5)': @@ -11340,12 +11549,8 @@ snapshots: dependencies: '@babel/types': 7.28.5 - '@babel/helper-string-parser@7.25.9': {} - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.27.1': {} @@ -11363,7 +11568,7 @@ snapshots: '@babel/parser@7.27.0': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.28.5 '@babel/parser@7.28.5': dependencies: @@ -11401,7 +11606,7 @@ snapshots: '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': dependencies: @@ -11411,12 +11616,12 @@ snapshots: '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-decorators@7.23.3(@babel/core@7.28.5)': dependencies: @@ -11446,12 +11651,12 @@ snapshots: '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.28.5)': dependencies: @@ -11461,42 +11666,42 @@ snapshots: '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.28.5)': dependencies: @@ -11751,16 +11956,6 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -11951,12 +12146,26 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@babel/runtime@7.29.2': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 '@babel/parser': 7.28.5 '@babel/types': 7.28.5 + '@babel/traverse@7.27.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -11969,11 +12178,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/types@7.27.0': - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -11985,9 +12189,7 @@ snapshots: '@braintree/sanitize-url@6.0.2': {} - '@braintree/sanitize-url@7.1.0': {} - - '@braintree/sanitize-url@7.1.1': {} + '@braintree/sanitize-url@7.1.2': {} '@cacheable/utils@2.3.4': dependencies: @@ -11998,7 +12200,7 @@ snapshots: dependencies: '@ucast/mongo2js': 1.3.4 - '@casl/react@4.0.0(@casl/ability@6.8.0)(react@18.3.1)': + '@casl/react@5.0.1(@casl/ability@6.8.0)(react@18.3.1)': dependencies: '@casl/ability': 6.8.0 react: 18.3.1 @@ -12011,22 +12213,39 @@ snapshots: '@chevrotain/types': 11.0.3 lodash-es: 4.17.23 + '@chevrotain/cst-dts-gen@11.1.2': + dependencies: + '@chevrotain/gast': 11.1.2 + '@chevrotain/types': 11.1.2 + lodash-es: 4.17.23 + '@chevrotain/gast@11.0.3': dependencies: '@chevrotain/types': 11.0.3 lodash-es: 4.17.23 + '@chevrotain/gast@11.1.2': + dependencies: + '@chevrotain/types': 11.1.2 + lodash-es: 4.17.23 + '@chevrotain/regexp-to-ast@11.0.3': {} + '@chevrotain/regexp-to-ast@11.1.2': {} + '@chevrotain/types@11.0.3': {} + '@chevrotain/types@11.1.2': {} + '@chevrotain/utils@11.0.3': {} - '@clickhouse/client-common@1.17.0': {} + '@chevrotain/utils@11.1.2': {} - '@clickhouse/client@1.17.0': + '@clickhouse/client-common@1.18.2': {} + + '@clickhouse/client@1.18.2': dependencies: - '@clickhouse/client-common': 1.17.0 + '@clickhouse/client-common': 1.18.2 '@colors/colors@1.5.0': optional: true @@ -12068,6 +12287,11 @@ snapshots: dependencies: tslib: 2.8.1 + '@emnapi/runtime@1.9.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.8.1 @@ -12079,98 +12303,176 @@ snapshots: emoji-mart: 5.6.0 react: 18.3.1 - '@esbuild/aix-ppc64@0.25.0': + '@epic-web/invariant@1.0.0': {} + + '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/android-arm64@0.25.0': + '@esbuild/aix-ppc64@0.27.4': optional: true - '@esbuild/android-arm@0.25.0': + '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-x64@0.25.0': + '@esbuild/android-arm64@0.27.4': optional: true - '@esbuild/darwin-arm64@0.25.0': + '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/darwin-x64@0.25.0': + '@esbuild/android-arm@0.27.4': optional: true - '@esbuild/freebsd-arm64@0.25.0': + '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.25.0': + '@esbuild/android-x64@0.27.4': optional: true - '@esbuild/linux-arm64@0.25.0': + '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/linux-arm@0.25.0': + '@esbuild/darwin-arm64@0.27.4': optional: true - '@esbuild/linux-ia32@0.25.0': + '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/linux-loong64@0.25.0': + '@esbuild/darwin-x64@0.27.4': optional: true - '@esbuild/linux-mips64el@0.25.0': + '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/linux-ppc64@0.25.0': + '@esbuild/freebsd-arm64@0.27.4': optional: true - '@esbuild/linux-riscv64@0.25.0': + '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/linux-s390x@0.25.0': + '@esbuild/freebsd-x64@0.27.4': optional: true - '@esbuild/linux-x64@0.25.0': + '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.25.0': + '@esbuild/linux-arm64@0.27.4': optional: true - '@esbuild/netbsd-x64@0.25.0': + '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.25.0': + '@esbuild/linux-arm@0.27.4': optional: true - '@esbuild/openbsd-x64@0.25.0': + '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/sunos-x64@0.25.0': + '@esbuild/linux-ia32@0.27.4': optional: true - '@esbuild/win32-arm64@0.25.0': + '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/win32-ia32@0.25.0': + '@esbuild/linux-loong64@0.27.4': optional: true - '@esbuild/win32-x64@0.25.0': + '@esbuild/linux-mips64el@0.27.3': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.39.2(jiti@2.4.2))': + '@esbuild/linux-mips64el@0.27.4': + optional: true + + '@esbuild/linux-ppc64@0.27.3': + optional: true + + '@esbuild/linux-ppc64@0.27.4': + optional: true + + '@esbuild/linux-riscv64@0.27.3': + optional: true + + '@esbuild/linux-riscv64@0.27.4': + optional: true + + '@esbuild/linux-s390x@0.27.3': + optional: true + + '@esbuild/linux-s390x@0.27.4': + optional: true + + '@esbuild/linux-x64@0.27.3': + optional: true + + '@esbuild/linux-x64@0.27.4': + optional: true + + '@esbuild/netbsd-arm64@0.27.3': + optional: true + + '@esbuild/netbsd-arm64@0.27.4': + optional: true + + '@esbuild/netbsd-x64@0.27.3': + optional: true + + '@esbuild/netbsd-x64@0.27.4': + optional: true + + '@esbuild/openbsd-arm64@0.27.3': + optional: true + + '@esbuild/openbsd-arm64@0.27.4': + optional: true + + '@esbuild/openbsd-x64@0.27.3': + optional: true + + '@esbuild/openbsd-x64@0.27.4': + optional: true + + '@esbuild/openharmony-arm64@0.27.3': + optional: true + + '@esbuild/openharmony-arm64@0.27.4': + optional: true + + '@esbuild/sunos-x64@0.27.3': + optional: true + + '@esbuild/sunos-x64@0.27.4': + optional: true + + '@esbuild/win32-arm64@0.27.3': + optional: true + + '@esbuild/win32-arm64@0.27.4': + optional: true + + '@esbuild/win32-ia32@0.27.3': + optional: true + + '@esbuild/win32-ia32@0.27.4': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.4': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.4.2))': dependencies: - eslint: 9.39.2(jiti@2.4.2) + eslint: 9.39.4(jiti@2.4.2) eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.4.2))': - dependencies: - eslint: 9.39.2(jiti@2.4.2) - eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} - '@eslint-community/regexpp@4.12.1': {} - - '@eslint/config-array@0.21.1': + '@eslint/config-array@0.21.2': dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 - minimatch: 3.1.2 + minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -12182,25 +12484,21 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.3': + '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.12.6 + ajv: 6.14.0 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.1 - minimatch: 3.1.2 + minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.16.0': {} - - '@eslint/js@9.20.0': {} - - '@eslint/js@9.39.2': {} + '@eslint/js@9.39.4': {} '@eslint/object-schema@2.1.7': {} @@ -12239,7 +12537,7 @@ snapshots: jotai-scope: 0.7.2(jotai@2.11.0(@types/react@18.3.12)(react@18.3.1))(react@18.3.1) lodash.debounce: 4.0.8 lodash.throttle: 4.1.1 - nanoid: 3.3.3 + nanoid: 3.3.8 pako: 2.0.3 perfect-freehand: 1.2.0 pica: 7.1.1 @@ -12258,7 +12556,6 @@ snapshots: - '@types/react' - '@types/react-dom' - immer - - supports-color '@excalidraw/laser-pointer@1.3.1': {} @@ -12272,10 +12569,8 @@ snapshots: dependencies: '@excalidraw/markdown-to-text': 0.1.2 '@mermaid-js/parser': 0.6.3 - mermaid: 11.12.2 + mermaid: 11.13.0 nanoid: 4.0.2 - transitivePeerDependencies: - - supports-color '@excalidraw/random-username@1.1.0': {} @@ -12283,8 +12578,8 @@ snapshots: '@fastify/ajv-compiler@4.0.5': dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) fast-uri: 3.0.6 '@fastify/busboy@3.1.1': {} @@ -12346,7 +12641,7 @@ snapshots: content-disposition: 1.0.1 fastify-plugin: 5.1.0 fastq: 1.17.1 - glob: 10.5.0 + glob: 13.0.6 '@floating-ui/core@1.7.3': dependencies: @@ -12382,19 +12677,19 @@ snapshots: dependencies: lib0: 0.2.117 - '@hocuspocus/provider@3.4.4(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29)': + '@hocuspocus/provider@3.4.4(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30)': dependencies: '@hocuspocus/common': 3.4.4 '@lifeomic/attempt': 3.0.3 lib0: 0.2.117 ws: 8.19.0 - y-protocols: 1.0.6(yjs@13.6.29) - yjs: 13.6.29 + y-protocols: 1.0.6(yjs@13.6.30) + yjs: 13.6.30 transitivePeerDependencies: - bufferutil - utf-8-validate - '@hocuspocus/server@3.4.4(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29)': + '@hocuspocus/server@3.4.4(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30)': dependencies: '@hocuspocus/common': 3.4.4 async-lock: 1.4.1 @@ -12402,23 +12697,23 @@ snapshots: kleur: 4.1.5 lib0: 0.2.117 ws: 8.19.0 - y-protocols: 1.0.6(yjs@13.6.29) - yjs: 13.6.29 + y-protocols: 1.0.6(yjs@13.6.30) + yjs: 13.6.30 transitivePeerDependencies: - bufferutil - utf-8-validate - '@hocuspocus/transformer@3.4.4(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29))(yjs@13.6.29)': + '@hocuspocus/transformer@3.4.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 - '@tiptap/starter-kit': 3.17.1 - y-prosemirror: 1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29) - yjs: 13.6.29 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 + '@tiptap/starter-kit': 3.20.4 + y-prosemirror: 1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) + yjs: 13.6.30 - '@hono/node-server@1.19.9(hono@4.12.3)': + '@hono/node-server@1.19.10(hono@4.12.8)': dependencies: - hono: 4.12.3 + hono: 4.12.8 '@humanfs/core@0.19.1': {} @@ -12435,59 +12730,52 @@ snapshots: '@iconify/types@2.0.0': {} - '@iconify/utils@3.0.1': + '@iconify/utils@3.1.0': dependencies: '@antfu/install-pkg': 1.1.0 - '@antfu/utils': 9.2.0 '@iconify/types': 2.0.0 - debug: 4.4.3 - globals: 15.15.0 - kolorist: 1.8.0 - local-pkg: 1.1.2 mlly: 1.8.0 - transitivePeerDependencies: - - supports-color '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.1.2(@types/node@22.13.4)': + '@inquirer/checkbox@4.1.2(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/type': 3.0.4(@types/node@25.5.0) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/checkbox@4.3.2(@types/node@22.13.4)': + '@inquirer/checkbox@4.3.2(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/type': 3.0.10(@types/node@25.5.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/confirm@5.1.21(@types/node@22.13.4)': + '@inquirer/confirm@5.1.21(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.13.4) - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/confirm@5.1.6(@types/node@22.13.4)': + '@inquirer/confirm@5.1.6(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) + '@inquirer/type': 3.0.4(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/core@10.1.7(@types/node@22.13.4)': + '@inquirer/core@10.1.7(@types/node@25.5.0)': dependencies: '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/type': 3.0.4(@types/node@25.5.0) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -12495,212 +12783,203 @@ snapshots: wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/core@10.3.2(@types/node@22.13.4)': + '@inquirer/core@10.3.2(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/type': 3.0.10(@types/node@25.5.0) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/editor@4.2.23(@types/node@22.13.4)': + '@inquirer/editor@4.2.23(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.13.4) - '@inquirer/external-editor': 1.0.3(@types/node@22.13.4) - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) + '@inquirer/external-editor': 1.0.3(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/editor@4.2.7(@types/node@22.13.4)': + '@inquirer/editor@4.2.7(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) + '@inquirer/type': 3.0.4(@types/node@25.5.0) external-editor: 3.1.0 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/expand@4.0.23(@types/node@22.13.4)': + '@inquirer/expand@4.0.23(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.13.4) - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/expand@4.0.9(@types/node@22.13.4)': + '@inquirer/expand@4.0.9(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) + '@inquirer/type': 3.0.4(@types/node@25.5.0) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/external-editor@1.0.3(@types/node@22.13.4)': + '@inquirer/external-editor@1.0.3(@types/node@25.5.0)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 '@inquirer/figures@1.0.10': {} '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.1.6(@types/node@22.13.4)': + '@inquirer/input@4.1.6(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) + '@inquirer/type': 3.0.4(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/input@4.3.1(@types/node@22.13.4)': + '@inquirer/input@4.3.1(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.13.4) - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/number@3.0.23(@types/node@22.13.4)': + '@inquirer/number@3.0.23(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.13.4) - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/number@3.0.9(@types/node@22.13.4)': + '@inquirer/number@3.0.9(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) + '@inquirer/type': 3.0.4(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/password@4.0.23(@types/node@22.13.4)': + '@inquirer/password@4.0.23(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.13.4) - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/password@4.0.9(@types/node@22.13.4)': + '@inquirer/password@4.0.9(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) + '@inquirer/type': 3.0.4(@types/node@25.5.0) ansi-escapes: 4.3.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/prompts@7.10.1(@types/node@22.13.4)': + '@inquirer/prompts@7.10.1(@types/node@25.5.0)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@22.13.4) - '@inquirer/confirm': 5.1.21(@types/node@22.13.4) - '@inquirer/editor': 4.2.23(@types/node@22.13.4) - '@inquirer/expand': 4.0.23(@types/node@22.13.4) - '@inquirer/input': 4.3.1(@types/node@22.13.4) - '@inquirer/number': 3.0.23(@types/node@22.13.4) - '@inquirer/password': 4.0.23(@types/node@22.13.4) - '@inquirer/rawlist': 4.1.11(@types/node@22.13.4) - '@inquirer/search': 3.2.2(@types/node@22.13.4) - '@inquirer/select': 4.4.2(@types/node@22.13.4) + '@inquirer/checkbox': 4.3.2(@types/node@25.5.0) + '@inquirer/confirm': 5.1.21(@types/node@25.5.0) + '@inquirer/editor': 4.2.23(@types/node@25.5.0) + '@inquirer/expand': 4.0.23(@types/node@25.5.0) + '@inquirer/input': 4.3.1(@types/node@25.5.0) + '@inquirer/number': 3.0.23(@types/node@25.5.0) + '@inquirer/password': 4.0.23(@types/node@25.5.0) + '@inquirer/rawlist': 4.1.11(@types/node@25.5.0) + '@inquirer/search': 3.2.2(@types/node@25.5.0) + '@inquirer/select': 4.4.2(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/prompts@7.3.2(@types/node@22.13.4)': + '@inquirer/prompts@7.3.2(@types/node@25.5.0)': dependencies: - '@inquirer/checkbox': 4.1.2(@types/node@22.13.4) - '@inquirer/confirm': 5.1.6(@types/node@22.13.4) - '@inquirer/editor': 4.2.7(@types/node@22.13.4) - '@inquirer/expand': 4.0.9(@types/node@22.13.4) - '@inquirer/input': 4.1.6(@types/node@22.13.4) - '@inquirer/number': 3.0.9(@types/node@22.13.4) - '@inquirer/password': 4.0.9(@types/node@22.13.4) - '@inquirer/rawlist': 4.0.9(@types/node@22.13.4) - '@inquirer/search': 3.0.9(@types/node@22.13.4) - '@inquirer/select': 4.0.9(@types/node@22.13.4) + '@inquirer/checkbox': 4.1.2(@types/node@25.5.0) + '@inquirer/confirm': 5.1.6(@types/node@25.5.0) + '@inquirer/editor': 4.2.7(@types/node@25.5.0) + '@inquirer/expand': 4.0.9(@types/node@25.5.0) + '@inquirer/input': 4.1.6(@types/node@25.5.0) + '@inquirer/number': 3.0.9(@types/node@25.5.0) + '@inquirer/password': 4.0.9(@types/node@25.5.0) + '@inquirer/rawlist': 4.0.9(@types/node@25.5.0) + '@inquirer/search': 3.0.9(@types/node@25.5.0) + '@inquirer/select': 4.0.9(@types/node@25.5.0) optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/rawlist@4.0.9(@types/node@22.13.4)': + '@inquirer/rawlist@4.0.9(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) + '@inquirer/type': 3.0.4(@types/node@25.5.0) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/rawlist@4.1.11(@types/node@22.13.4)': + '@inquirer/rawlist@4.1.11(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.13.4) - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/search@3.0.9(@types/node@22.13.4)': + '@inquirer/search@3.0.9(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/type': 3.0.4(@types/node@25.5.0) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/search@3.2.2(@types/node@22.13.4)': + '@inquirer/search@3.2.2(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/type': 3.0.10(@types/node@25.5.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/select@4.0.9(@types/node@22.13.4)': + '@inquirer/select@4.0.9(@types/node@25.5.0)': dependencies: - '@inquirer/core': 10.1.7(@types/node@22.13.4) + '@inquirer/core': 10.1.7(@types/node@25.5.0) '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@22.13.4) + '@inquirer/type': 3.0.4(@types/node@25.5.0) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/select@4.4.2(@types/node@22.13.4)': + '@inquirer/select@4.4.2(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.13.4) + '@inquirer/core': 10.3.2(@types/node@25.5.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.13.4) + '@inquirer/type': 3.0.10(@types/node@25.5.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/type@3.0.10(@types/node@22.13.4)': + '@inquirer/type@3.0.10(@types/node@25.5.0)': optionalDependencies: - '@types/node': 22.13.4 + '@types/node': 25.5.0 - '@inquirer/type@3.0.4(@types/node@22.13.4)': + '@inquirer/type@3.0.4(@types/node@25.5.0)': optionalDependencies: - '@types/node': 22.13.4 - - '@ioredis/commands@1.2.0': {} + '@types/node': 25.5.0 '@ioredis/commands@1.5.0': {} - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 + '@ioredis/commands@1.5.1': {} '@istanbuljs/load-nyc-config@1.1.0': dependencies: @@ -12712,44 +12991,43 @@ snapshots: '@istanbuljs/schema@0.1.3': {} - '@jest/console@30.2.0': + '@jest/console@30.3.0': dependencies: - '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 chalk: 4.1.2 - jest-message-util: 30.2.0 - jest-util: 30.2.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 slash: 3.0.0 - '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3))': + '@jest/core@30.3.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3))': dependencies: - '@jest/console': 30.2.0 + '@jest/console': 30.3.0 '@jest/pattern': 30.0.1 - '@jest/reporters': 30.2.0 - '@jest/test-result': 30.2.0 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@jest/reporters': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.4.0 exit-x: 0.2.2 graceful-fs: 4.2.11 - jest-changed-files: 30.2.0 - jest-config: 30.2.0(@types/node@22.19.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)) - jest-haste-map: 30.2.0 - jest-message-util: 30.2.0 + jest-changed-files: 30.3.0 + jest-config: 30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 jest-regex-util: 30.0.1 - jest-resolve: 30.2.0 - jest-resolve-dependencies: 30.2.0 - jest-runner: 30.2.0 - jest-runtime: 30.2.0 - jest-snapshot: 30.2.0 - jest-util: 30.2.0 - jest-validate: 30.2.0 - jest-watcher: 30.2.0 - micromatch: 4.0.8 - pretty-format: 30.2.0 + jest-resolve: 30.3.0 + jest-resolve-dependencies: 30.3.0 + jest-runner: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + jest-watcher: 30.3.0 + pretty-format: 30.3.0 slash: 3.0.0 transitivePeerDependencies: - babel-plugin-macros @@ -12759,71 +13037,77 @@ snapshots: '@jest/diff-sequences@30.0.1': {} - '@jest/environment@30.2.0': + '@jest/diff-sequences@30.3.0': {} + + '@jest/environment@30.3.0': dependencies: - '@jest/fake-timers': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.1 - jest-mock: 30.2.0 + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 + jest-mock: 30.3.0 '@jest/expect-utils@30.2.0': dependencies: '@jest/get-type': 30.1.0 - '@jest/expect@30.2.0': + '@jest/expect-utils@30.3.0': dependencies: - expect: 30.2.0 - jest-snapshot: 30.2.0 + '@jest/get-type': 30.1.0 + + '@jest/expect@30.3.0': + dependencies: + expect: 30.3.0 + jest-snapshot: 30.3.0 transitivePeerDependencies: - supports-color - '@jest/fake-timers@30.2.0': + '@jest/fake-timers@30.3.0': dependencies: - '@jest/types': 30.2.0 - '@sinonjs/fake-timers': 13.0.5 - '@types/node': 22.19.1 - jest-message-util: 30.2.0 - jest-mock: 30.2.0 - jest-util: 30.2.0 + '@jest/types': 30.3.0 + '@sinonjs/fake-timers': 15.1.1 + '@types/node': 25.5.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 '@jest/get-type@30.1.0': {} - '@jest/globals@30.2.0': + '@jest/globals@30.3.0': dependencies: - '@jest/environment': 30.2.0 - '@jest/expect': 30.2.0 - '@jest/types': 30.2.0 - jest-mock: 30.2.0 + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/types': 30.3.0 + jest-mock: 30.3.0 transitivePeerDependencies: - supports-color '@jest/pattern@30.0.1': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 jest-regex-util: 30.0.1 - '@jest/reporters@30.2.0': + '@jest/reporters@30.3.0': dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 30.2.0 - '@jest/test-result': 30.2.0 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 + '@jest/console': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 22.19.1 + '@types/node': 25.5.0 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit-x: 0.2.2 - glob: 10.5.0 + glob: 13.0.6 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.2 istanbul-lib-instrument: 6.0.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - jest-message-util: 30.2.0 - jest-util: 30.2.0 - jest-worker: 30.2.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + jest-worker: 30.3.0 slash: 3.0.0 string-length: 4.0.2 v8-to-istanbul: 9.2.0 @@ -12834,9 +13118,9 @@ snapshots: dependencies: '@sinclair/typebox': 0.34.48 - '@jest/snapshot-utils@30.2.0': + '@jest/snapshot-utils@30.3.0': dependencies: - '@jest/types': 30.2.0 + '@jest/types': 30.3.0 chalk: 4.1.2 graceful-fs: 4.2.11 natural-compare: 1.4.0 @@ -12847,34 +13131,33 @@ snapshots: callsites: 3.1.0 graceful-fs: 4.2.11 - '@jest/test-result@30.2.0': + '@jest/test-result@30.3.0': dependencies: - '@jest/console': 30.2.0 - '@jest/types': 30.2.0 + '@jest/console': 30.3.0 + '@jest/types': 30.3.0 '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.2 - '@jest/test-sequencer@30.2.0': + '@jest/test-sequencer@30.3.0': dependencies: - '@jest/test-result': 30.2.0 + '@jest/test-result': 30.3.0 graceful-fs: 4.2.11 - jest-haste-map: 30.2.0 + jest-haste-map: 30.3.0 slash: 3.0.0 - '@jest/transform@30.2.0': + '@jest/transform@30.3.0': dependencies: '@babel/core': 7.28.5 - '@jest/types': 30.2.0 + '@jest/types': 30.3.0 '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 7.0.1 chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 - jest-haste-map: 30.2.0 + jest-haste-map: 30.3.0 jest-regex-util: 30.0.1 - jest-util: 30.2.0 - micromatch: 4.0.8 + jest-util: 30.3.0 pirates: 4.0.7 slash: 3.0.0 write-file-atomic: 5.0.1 @@ -12887,17 +13170,27 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/yargs': 17.0.35 chalk: 4.1.2 - '@joplin/turndown-plugin-gfm@1.0.56': {} - - '@joplin/turndown@4.0.74': + '@jest/types@30.3.0': dependencies: - '@adobe/css-tools': 4.3.3 + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 25.5.0 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@joplin/turndown-plugin-gfm@1.0.64': {} + + '@joplin/turndown@4.0.82': + dependencies: + '@adobe/css-tools': 4.4.3 html-entities: 1.4.0 - jsdom: 25.0.1 + jsdom: 26.1.0 transitivePeerDependencies: - bufferutil - canvas @@ -12909,21 +13202,13 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/gen-mapping@0.3.5': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/remapping@2.3.5': dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - '@jridgewell/source-map@0.3.6': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -12952,9 +13237,10 @@ snapshots: '@keyv/serialize@1.1.1': {} - '@langchain/core@1.1.29(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6))': + '@langchain/core@1.1.34(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6))': dependencies: '@cfworker/json-schema': 4.1.1 + '@standard-schema/spec': 1.1.0 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 @@ -12970,21 +13256,23 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/textsplitters@1.0.1(@langchain/core@1.1.29(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6)))': + '@langchain/textsplitters@1.0.1(@langchain/core@1.1.34(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6)))': dependencies: - '@langchain/core': 1.1.29(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6)) + '@langchain/core': 1.1.34(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6)) js-tiktoken: 1.0.21 '@lifeomic/attempt@3.0.3': {} + '@ltd/j-toml@1.38.0': {} + '@lukeed/csprng@1.1.0': {} '@lukeed/ms@2.0.2': {} - '@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react': 0.27.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mantine/hooks': 8.3.14(react@18.3.1) + '@mantine/hooks': 8.3.18(react@18.3.1) clsx: 2.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -12995,50 +13283,50 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@mantine/dates@8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(dayjs@1.11.19)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mantine/dates@8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(dayjs@1.11.19)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@mantine/core': 8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mantine/hooks': 8.3.14(react@18.3.1) + '@mantine/core': 8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mantine/hooks': 8.3.18(react@18.3.1) clsx: 2.1.1 dayjs: 1.11.19 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@mantine/form@8.3.14(react@18.3.1)': + '@mantine/form@8.3.18(react@18.3.1)': dependencies: fast-deep-equal: 3.1.3 klona: 2.0.6 react: 18.3.1 - '@mantine/hooks@8.3.14(react@18.3.1)': + '@mantine/hooks@8.3.18(react@18.3.1)': dependencies: react: 18.3.1 - '@mantine/modals@8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mantine/modals@8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@mantine/core': 8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mantine/hooks': 8.3.14(react@18.3.1) + '@mantine/core': 8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mantine/hooks': 8.3.18(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@mantine/notifications@8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mantine/notifications@8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@mantine/core': 8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mantine/hooks': 8.3.14(react@18.3.1) - '@mantine/store': 8.3.14(react@18.3.1) + '@mantine/core': 8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mantine/hooks': 8.3.18(react@18.3.1) + '@mantine/store': 8.3.18(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mantine/spotlight@8.3.14(@mantine/core@8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mantine/spotlight@8.3.18(@mantine/core@8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@8.3.18(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@mantine/core': 8.3.14(@mantine/hooks@8.3.14(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mantine/hooks': 8.3.14(react@18.3.1) - '@mantine/store': 8.3.14(react@18.3.1) + '@mantine/core': 8.3.18(@mantine/hooks@8.3.18(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mantine/hooks': 8.3.18(react@18.3.1) + '@mantine/store': 8.3.18(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@mantine/store@8.3.14(react@18.3.1)': + '@mantine/store@8.3.18(react@18.3.1)': dependencies: react: 18.3.1 @@ -13046,19 +13334,23 @@ snapshots: dependencies: langium: 3.3.1 + '@mermaid-js/parser@1.0.1': + dependencies: + langium: 4.2.1 + '@modelcontextprotocol/sdk@1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.9(hono@4.12.3) - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + '@hono/node-server': 1.19.10(hono@4.12.8) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.5 eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.12.3 + express-rate-limit: 8.2.2(express@5.2.1) + hono: 4.12.8 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -13088,48 +13380,52 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2': optional: true - '@napi-rs/canvas-android-arm64@0.1.82': + '@napi-rs/canvas-android-arm64@0.1.97': optional: true - '@napi-rs/canvas-darwin-arm64@0.1.82': + '@napi-rs/canvas-darwin-arm64@0.1.97': optional: true - '@napi-rs/canvas-darwin-x64@0.1.82': + '@napi-rs/canvas-darwin-x64@0.1.97': optional: true - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.82': + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.97': optional: true - '@napi-rs/canvas-linux-arm64-gnu@0.1.82': + '@napi-rs/canvas-linux-arm64-gnu@0.1.97': optional: true - '@napi-rs/canvas-linux-arm64-musl@0.1.82': + '@napi-rs/canvas-linux-arm64-musl@0.1.97': optional: true - '@napi-rs/canvas-linux-riscv64-gnu@0.1.82': + '@napi-rs/canvas-linux-riscv64-gnu@0.1.97': optional: true - '@napi-rs/canvas-linux-x64-gnu@0.1.82': + '@napi-rs/canvas-linux-x64-gnu@0.1.97': optional: true - '@napi-rs/canvas-linux-x64-musl@0.1.82': + '@napi-rs/canvas-linux-x64-musl@0.1.97': optional: true - '@napi-rs/canvas-win32-x64-msvc@0.1.82': + '@napi-rs/canvas-win32-arm64-msvc@0.1.97': optional: true - '@napi-rs/canvas@0.1.82': + '@napi-rs/canvas-win32-x64-msvc@0.1.97': + optional: true + + '@napi-rs/canvas@0.1.97': optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.82 - '@napi-rs/canvas-darwin-arm64': 0.1.82 - '@napi-rs/canvas-darwin-x64': 0.1.82 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.82 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.82 - '@napi-rs/canvas-linux-arm64-musl': 0.1.82 - '@napi-rs/canvas-linux-riscv64-gnu': 0.1.82 - '@napi-rs/canvas-linux-x64-gnu': 0.1.82 - '@napi-rs/canvas-linux-x64-musl': 0.1.82 - '@napi-rs/canvas-win32-x64-msvc': 0.1.82 + '@napi-rs/canvas-android-arm64': 0.1.97 + '@napi-rs/canvas-darwin-arm64': 0.1.97 + '@napi-rs/canvas-darwin-x64': 0.1.97 + '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.97 + '@napi-rs/canvas-linux-arm64-gnu': 0.1.97 + '@napi-rs/canvas-linux-arm64-musl': 0.1.97 + '@napi-rs/canvas-linux-riscv64-gnu': 0.1.97 + '@napi-rs/canvas-linux-x64-gnu': 0.1.97 + '@napi-rs/canvas-linux-x64-musl': 0.1.97 + '@napi-rs/canvas-win32-arm64-msvc': 0.1.97 + '@napi-rs/canvas-win32-x64-msvc': 0.1.97 optional: true '@napi-rs/wasm-runtime@0.2.12': @@ -13145,48 +13441,55 @@ snapshots: '@emnapi/runtime': 1.5.0 '@tybys/wasm-util': 0.9.0 - '@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(ioredis@5.4.1)': + '@napi-rs/wasm-runtime@1.1.1': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) - ioredis: 5.4.1 + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.9.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1)': + dependencies: + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + ioredis: 5.10.1 tslib: 2.8.1 - '@nestjs/bull-shared@11.0.4(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)': + '@nestjs/bull-shared@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/bullmq@11.0.4(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(bullmq@5.70.1)': + '@nestjs/bullmq@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(bullmq@5.71.0)': dependencies: - '@nestjs/bull-shared': 11.0.4(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) - bullmq: 5.70.1 + '@nestjs/bull-shared': 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + bullmq: 5.71.0 tslib: 2.8.1 - '@nestjs/cache-manager@3.1.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2)': + '@nestjs/cache-manager@3.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) cache-manager: 7.2.8 keyv: 5.6.0 rxjs: 7.8.2 - '@nestjs/cli@11.0.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)': + '@nestjs/cli@11.0.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)': dependencies: '@angular-devkit/core': 19.2.19(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics-cli': 19.2.19(@types/node@22.13.4)(chokidar@4.0.3) - '@inquirer/prompts': 7.10.1(@types/node@22.13.4) - '@nestjs/schematics': 11.0.1(chokidar@4.0.3)(typescript@5.9.3) + '@angular-devkit/schematics-cli': 19.2.19(@types/node@25.5.0)(chokidar@4.0.3) + '@inquirer/prompts': 7.10.1(@types/node@25.5.0) + '@nestjs/schematics': 11.0.9(chokidar@4.0.3)(typescript@5.9.3) ansis: 4.2.0 chokidar: 4.0.3 cli-table3: 0.6.5 commander: 4.1.1 fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))) - glob: 10.5.0 + glob: 13.0.6 node-emoji: 1.11.0 ora: 5.4.1 tsconfig-paths: 4.2.0 @@ -13202,9 +13505,9 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - file-type: 21.3.0 + file-type: 21.3.2 iterare: 1.2.1 load-esm: 1.0.3 reflect-metadata: 0.2.2 @@ -13217,17 +13520,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/config@4.0.3(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': + '@nestjs/config@4.0.3(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) dotenv: 17.2.3 dotenv-expand: 12.0.3 lodash: 4.17.23 rxjs: 7.8.2 - '@nestjs/core@11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -13237,43 +13540,43 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/websockets': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-socket.io@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/websockets': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/event-emitter@3.0.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)': + '@nestjs/event-emitter@3.0.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) eventemitter2: 6.4.9 - '@nestjs/jwt@11.0.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))': + '@nestjs/jwt@11.0.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@types/jsonwebtoken': 9.0.7 + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@types/jsonwebtoken': 9.0.10 jsonwebtoken: 9.0.3 - '@nestjs/mapped-types@2.1.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': + '@nestjs/mapped-types@2.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.15.1 - '@nestjs/passport@11.0.5(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': + '@nestjs/passport@11.0.5(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) passport: 0.7.0 - '@nestjs/platform-fastify@11.1.14(@fastify/static@9.0.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)': + '@nestjs/platform-fastify@11.1.17(@fastify/static@9.0.0)(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': dependencies: '@fastify/cors': 11.2.0 '@fastify/formbody': 8.0.2 - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) fast-querystring: 1.1.2 - fastify: 5.7.4 + fastify: 5.8.2 fastify-plugin: 5.1.0 - find-my-way: 9.4.0 + find-my-way: 9.5.0 light-my-request: 6.6.0 path-to-regexp: 8.3.0 reusify: 1.1.0 @@ -13281,10 +13584,10 @@ snapshots: optionalDependencies: '@fastify/static': 9.0.0 - '@nestjs/platform-socket.io@11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(rxjs@7.8.2)': + '@nestjs/platform-socket.io@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/websockets': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-socket.io@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/websockets': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) rxjs: 7.8.2 socket.io: 4.8.3 tslib: 2.8.1 @@ -13293,63 +13596,54 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@6.1.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)': + '@nestjs/schedule@6.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) cron: 4.4.0 - '@nestjs/schematics@11.0.1(chokidar@4.0.3)(typescript@5.7.3)': + '@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 19.1.7(chokidar@4.0.3) - '@angular-devkit/schematics': 19.1.7(chokidar@4.0.3) - comment-json: 4.2.5 - jsonc-parser: 3.3.1 - pluralize: 8.0.0 - typescript: 5.7.3 - transitivePeerDependencies: - - chokidar - - '@nestjs/schematics@11.0.1(chokidar@4.0.3)(typescript@5.9.3)': - dependencies: - '@angular-devkit/core': 19.1.7(chokidar@4.0.3) - '@angular-devkit/schematics': 19.1.7(chokidar@4.0.3) - comment-json: 4.2.5 + '@angular-devkit/core': 19.2.17(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) + comment-json: 4.4.1 jsonc-parser: 3.3.1 pluralize: 8.0.0 typescript: 5.9.3 transitivePeerDependencies: - chokidar - '@nestjs/terminus@11.1.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/terminus@11.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) boxen: 5.1.2 check-disk-space: 3.4.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 - '@nestjs/testing@11.0.10(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)': + '@nestjs/testing@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/websockets@11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-socket.io@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/websockets@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) iterare: 1.2.1 object-hash: 3.0.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-socket.io': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(rxjs@7.8.2) + '@nestjs/platform-socket.io': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(rxjs@7.8.2) '@noble/hashes@1.8.0': {} + '@noble/hashes@2.0.1': {} + '@node-saml/node-saml@5.1.0': dependencies: '@types/debug': 4.1.12 @@ -13378,34 +13672,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - '@nuxt/opencollective@0.4.1': dependencies: consola: 3.4.2 - '@nx/devkit@22.5.3(nx@22.5.3)': + '@nx/devkit@22.6.1(nx@22.6.1)': dependencies: '@zkochan/js-yaml': 0.0.7 ejs: 3.1.10 enquirer: 2.3.6 - minimatch: 10.2.1 - nx: 22.5.3 + minimatch: 10.2.4 + nx: 22.6.1 semver: 7.7.4 tslib: 2.8.1 yargs-parser: 21.1.1 - '@nx/js@22.5.3(@babel/traverse@7.28.5)(nx@22.5.3)': + '@nx/js@22.6.1(@babel/traverse@7.28.5)(nx@22.6.1)': dependencies: '@babel/core': 7.28.5 '@babel/plugin-proposal-decorators': 7.23.7(@babel/core@7.28.5) @@ -13414,8 +13696,8 @@ snapshots: '@babel/preset-env': 7.23.8(@babel/core@7.28.5) '@babel/preset-typescript': 7.23.3(@babel/core@7.28.5) '@babel/runtime': 7.26.10 - '@nx/devkit': 22.5.3(nx@22.5.3) - '@nx/workspace': 22.5.3 + '@nx/devkit': 22.6.1(nx@22.6.1) + '@nx/workspace': 22.6.1 '@zkochan/js-yaml': 0.0.7 babel-plugin-const-enum: 1.2.0(@babel/core@7.28.5) babel-plugin-macros: 3.1.0 @@ -13441,43 +13723,43 @@ snapshots: - nx - supports-color - '@nx/nx-darwin-arm64@22.5.3': + '@nx/nx-darwin-arm64@22.6.1': optional: true - '@nx/nx-darwin-x64@22.5.3': + '@nx/nx-darwin-x64@22.6.1': optional: true - '@nx/nx-freebsd-x64@22.5.3': + '@nx/nx-freebsd-x64@22.6.1': optional: true - '@nx/nx-linux-arm-gnueabihf@22.5.3': + '@nx/nx-linux-arm-gnueabihf@22.6.1': optional: true - '@nx/nx-linux-arm64-gnu@22.5.3': + '@nx/nx-linux-arm64-gnu@22.6.1': optional: true - '@nx/nx-linux-arm64-musl@22.5.3': + '@nx/nx-linux-arm64-musl@22.6.1': optional: true - '@nx/nx-linux-x64-gnu@22.5.3': + '@nx/nx-linux-x64-gnu@22.6.1': optional: true - '@nx/nx-linux-x64-musl@22.5.3': + '@nx/nx-linux-x64-musl@22.6.1': optional: true - '@nx/nx-win32-arm64-msvc@22.5.3': + '@nx/nx-win32-arm64-msvc@22.6.1': optional: true - '@nx/nx-win32-x64-msvc@22.5.3': + '@nx/nx-win32-x64-msvc@22.6.1': optional: true - '@nx/workspace@22.5.3': + '@nx/workspace@22.6.1': dependencies: - '@nx/devkit': 22.5.3(nx@22.5.3) + '@nx/devkit': 22.6.1(nx@22.6.1) '@zkochan/js-yaml': 0.0.7 chalk: 4.1.2 enquirer: 2.3.6 - nx: 22.5.3 + nx: 22.6.1 picomatch: 4.0.2 semver: 7.7.4 tslib: 2.8.1 @@ -13563,22 +13845,21 @@ snapshots: '@opentelemetry/semantic-conventions@1.39.0': {} + '@oxc-project/types@0.120.0': {} + '@paralleldrive/cuid2@2.3.1': dependencies: '@noble/hashes': 1.8.0 '@pinojs/redact@0.4.0': {} - '@pkgjs/parseargs@0.11.0': - optional: true - '@pkgr/core@0.2.9': {} - '@posthog/core@1.22.0': + '@posthog/core@1.24.1': dependencies: - cross-spawn: 7.0.5 + cross-spawn: 7.0.6 - '@posthog/types@1.345.5': {} + '@posthog/types@1.363.1': {} '@protobufjs/aspromise@1.1.2': {} @@ -14356,7 +14637,7 @@ snapshots: '@react-dnd/shallowequal@2.0.0': {} - '@react-email/body@0.2.1(react@18.3.1)': + '@react-email/body@0.3.0(react@18.3.1)': dependencies: react: 18.3.1 @@ -14377,9 +14658,9 @@ snapshots: dependencies: react: 18.3.1 - '@react-email/components@1.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@react-email/components@1.0.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@react-email/body': 0.2.1(react@18.3.1) + '@react-email/body': 0.3.0(react@18.3.1) '@react-email/button': 0.2.1(react@18.3.1) '@react-email/code-block': 0.2.1(react@18.3.1) '@react-email/code-inline': 0.0.6(react@18.3.1) @@ -14397,7 +14678,7 @@ snapshots: '@react-email/render': 2.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-email/row': 0.0.13(react@18.3.1) '@react-email/section': 0.0.17(react@18.3.1) - '@react-email/tailwind': 2.0.4(@react-email/body@0.2.1(react@18.3.1))(@react-email/button@0.2.1(react@18.3.1))(@react-email/code-block@0.2.1(react@18.3.1))(@react-email/code-inline@0.0.6(react@18.3.1))(@react-email/container@0.0.16(react@18.3.1))(@react-email/heading@0.0.16(react@18.3.1))(@react-email/hr@0.0.12(react@18.3.1))(@react-email/img@0.0.12(react@18.3.1))(@react-email/link@0.0.13(react@18.3.1))(@react-email/preview@0.0.14(react@18.3.1))(@react-email/text@0.1.6(react@18.3.1))(react@18.3.1) + '@react-email/tailwind': 2.0.6(@react-email/body@0.3.0(react@18.3.1))(@react-email/button@0.2.1(react@18.3.1))(@react-email/code-block@0.2.1(react@18.3.1))(@react-email/code-inline@0.0.6(react@18.3.1))(@react-email/container@0.0.16(react@18.3.1))(@react-email/heading@0.0.16(react@18.3.1))(@react-email/hr@0.0.12(react@18.3.1))(@react-email/img@0.0.12(react@18.3.1))(@react-email/link@0.0.13(react@18.3.1))(@react-email/preview@0.0.14(react@18.3.1))(@react-email/text@0.1.6(react@18.3.1))(react@18.3.1) '@react-email/text': 0.1.6(react@18.3.1) react: 18.3.1 transitivePeerDependencies: @@ -14459,13 +14740,13 @@ snapshots: dependencies: react: 18.3.1 - '@react-email/tailwind@2.0.4(@react-email/body@0.2.1(react@18.3.1))(@react-email/button@0.2.1(react@18.3.1))(@react-email/code-block@0.2.1(react@18.3.1))(@react-email/code-inline@0.0.6(react@18.3.1))(@react-email/container@0.0.16(react@18.3.1))(@react-email/heading@0.0.16(react@18.3.1))(@react-email/hr@0.0.12(react@18.3.1))(@react-email/img@0.0.12(react@18.3.1))(@react-email/link@0.0.13(react@18.3.1))(@react-email/preview@0.0.14(react@18.3.1))(@react-email/text@0.1.6(react@18.3.1))(react@18.3.1)': + '@react-email/tailwind@2.0.6(@react-email/body@0.3.0(react@18.3.1))(@react-email/button@0.2.1(react@18.3.1))(@react-email/code-block@0.2.1(react@18.3.1))(@react-email/code-inline@0.0.6(react@18.3.1))(@react-email/container@0.0.16(react@18.3.1))(@react-email/heading@0.0.16(react@18.3.1))(@react-email/hr@0.0.12(react@18.3.1))(@react-email/img@0.0.12(react@18.3.1))(@react-email/link@0.0.13(react@18.3.1))(@react-email/preview@0.0.14(react@18.3.1))(@react-email/text@0.1.6(react@18.3.1))(react@18.3.1)': dependencies: '@react-email/text': 0.1.6(react@18.3.1) react: 18.3.1 tailwindcss: 4.1.18 optionalDependencies: - '@react-email/body': 0.2.1(react@18.3.1) + '@react-email/body': 0.3.0(react@18.3.1) '@react-email/button': 0.2.1(react@18.3.1) '@react-email/code-block': 0.2.1(react@18.3.1) '@react-email/code-inline': 0.0.6(react@18.3.1) @@ -14486,73 +14767,56 @@ snapshots: '@remirror/core-constants@3.0.0': {} - '@rolldown/pluginutils@1.0.0-beta.47': {} - - '@rollup/rollup-android-arm-eabi@4.53.3': + '@rolldown/binding-android-arm64@1.0.0-rc.10': optional: true - '@rollup/rollup-android-arm64@4.53.3': + '@rolldown/binding-darwin-arm64@1.0.0-rc.10': optional: true - '@rollup/rollup-darwin-arm64@4.53.3': + '@rolldown/binding-darwin-x64@1.0.0-rc.10': optional: true - '@rollup/rollup-darwin-x64@4.53.3': + '@rolldown/binding-freebsd-x64@1.0.0-rc.10': optional: true - '@rollup/rollup-freebsd-arm64@4.53.3': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': optional: true - '@rollup/rollup-freebsd-x64@4.53.3': + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.3': + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.3': + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.3': + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.3': + '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.3': + '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.3': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.3': + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.3': + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.3': - optional: true + '@rolldown/pluginutils@1.0.0-rc.10': {} - '@rollup/rollup-linux-x64-musl@4.53.3': - optional: true - - '@rollup/rollup-openharmony-arm64@4.53.3': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.53.3': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.53.3': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.53.3': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.53.3': - optional: true + '@rolldown/pluginutils@1.0.0-rc.7': {} '@selderee/plugin-htmlparser2@0.11.0': dependencies: @@ -14561,272 +14825,270 @@ snapshots: '@sinclair/typebox@0.34.48': {} - '@sindresorhus/slugify@1.1.0': + '@sindresorhus/slugify@3.0.0': dependencies: - '@sindresorhus/transliterate': 0.1.2 - escape-string-regexp: 4.0.0 + '@sindresorhus/transliterate': 2.3.1 + escape-string-regexp: 5.0.0 - '@sindresorhus/transliterate@0.1.2': - dependencies: - escape-string-regexp: 2.0.0 - lodash.deburr: 4.1.0 + '@sindresorhus/transliterate@2.3.1': {} '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - '@sinonjs/fake-timers@13.0.5': + '@sinonjs/fake-timers@15.1.1': dependencies: '@sinonjs/commons': 3.0.1 - '@smithy/abort-controller@4.2.10': + '@smithy/abort-controller@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/chunked-blob-reader-native@4.2.2': + '@smithy/chunked-blob-reader-native@4.2.3': dependencies: - '@smithy/util-base64': 4.3.1 + '@smithy/util-base64': 4.3.2 tslib: 2.8.1 - '@smithy/chunked-blob-reader@5.2.1': + '@smithy/chunked-blob-reader@5.2.2': dependencies: tslib: 2.8.1 - '@smithy/config-resolver@4.4.9': + '@smithy/config-resolver@4.4.13': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-config-provider': 4.2.1 - '@smithy/util-endpoints': 3.3.1 - '@smithy/util-middleware': 4.2.10 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.3 + '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/core@3.23.6': + '@smithy/core@3.23.12': dependencies: - '@smithy/middleware-serde': 4.2.11 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 - '@smithy/util-body-length-browser': 4.2.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-stream': 4.5.15 - '@smithy/util-utf8': 4.2.1 - '@smithy/uuid': 1.1.1 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-stream': 4.5.20 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/credential-provider-imds@4.2.10': + '@smithy/credential-provider-imds@4.2.12': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/property-provider': 4.2.10 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 tslib: 2.8.1 - '@smithy/eventstream-codec@4.2.10': + '@smithy/eventstream-codec@4.2.12': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.13.0 - '@smithy/util-hex-encoding': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.2.10': + '@smithy/eventstream-serde-browser@4.2.12': dependencies: - '@smithy/eventstream-serde-universal': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/eventstream-serde-universal': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.3.10': + '@smithy/eventstream-serde-config-resolver@4.3.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/eventstream-serde-node@4.2.10': + '@smithy/eventstream-serde-node@4.2.12': dependencies: - '@smithy/eventstream-serde-universal': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/eventstream-serde-universal': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@4.2.10': + '@smithy/eventstream-serde-universal@4.2.12': dependencies: - '@smithy/eventstream-codec': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/eventstream-codec': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.11': + '@smithy/fetch-http-handler@5.3.15': dependencies: - '@smithy/protocol-http': 5.3.10 - '@smithy/querystring-builder': 4.2.10 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 tslib: 2.8.1 - '@smithy/hash-blob-browser@4.2.11': + '@smithy/hash-blob-browser@4.2.13': dependencies: - '@smithy/chunked-blob-reader': 5.2.1 - '@smithy/chunked-blob-reader-native': 4.2.2 - '@smithy/types': 4.13.0 + '@smithy/chunked-blob-reader': 5.2.2 + '@smithy/chunked-blob-reader-native': 4.2.3 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/hash-node@4.2.10': + '@smithy/hash-node@4.2.12': dependencies: - '@smithy/types': 4.13.0 - '@smithy/util-buffer-from': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/hash-stream-node@4.2.10': + '@smithy/hash-stream-node@4.2.12': dependencies: - '@smithy/types': 4.13.0 - '@smithy/util-utf8': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/invalid-dependency@4.2.10': + '@smithy/invalid-dependency@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.2.1': + '@smithy/is-array-buffer@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/md5-js@4.2.10': + '@smithy/md5-js@4.2.12': dependencies: - '@smithy/types': 4.13.0 - '@smithy/util-utf8': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/middleware-content-length@4.2.10': + '@smithy/middleware-content-length@4.2.12': dependencies: - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.20': + '@smithy/middleware-endpoint@4.4.27': dependencies: - '@smithy/core': 3.23.6 - '@smithy/middleware-serde': 4.2.11 - '@smithy/node-config-provider': 4.3.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 - '@smithy/url-parser': 4.2.10 - '@smithy/util-middleware': 4.2.10 + '@smithy/core': 3.23.12 + '@smithy/middleware-serde': 4.2.15 + '@smithy/node-config-provider': 4.3.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 + '@smithy/url-parser': 4.2.12 + '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.37': + '@smithy/middleware-retry@4.4.44': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/service-error-classification': 4.2.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-retry': 4.2.10 - '@smithy/uuid': 1.1.1 + '@smithy/node-config-provider': 4.3.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/service-error-classification': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-retry': 4.2.12 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.11': + '@smithy/middleware-serde@4.2.15': dependencies: - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 + '@smithy/core': 3.23.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/middleware-stack@4.2.10': + '@smithy/middleware-stack@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/node-config-provider@4.3.10': + '@smithy/node-config-provider@4.3.12': dependencies: - '@smithy/property-provider': 4.2.10 - '@smithy/shared-ini-file-loader': 4.4.5 - '@smithy/types': 4.13.0 + '@smithy/property-provider': 4.2.12 + '@smithy/shared-ini-file-loader': 4.4.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/node-http-handler@4.4.12': + '@smithy/node-http-handler@4.5.0': dependencies: - '@smithy/abort-controller': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/querystring-builder': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/abort-controller': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/querystring-builder': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/property-provider@4.2.10': + '@smithy/property-provider@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/protocol-http@5.3.10': + '@smithy/protocol-http@5.3.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/querystring-builder@4.2.10': + '@smithy/querystring-builder@4.2.12': dependencies: - '@smithy/types': 4.13.0 - '@smithy/util-uri-escape': 4.2.1 + '@smithy/types': 4.13.1 + '@smithy/util-uri-escape': 4.2.2 tslib: 2.8.1 - '@smithy/querystring-parser@4.2.10': + '@smithy/querystring-parser@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/service-error-classification@4.2.10': + '@smithy/service-error-classification@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 - '@smithy/shared-ini-file-loader@4.4.5': + '@smithy/shared-ini-file-loader@4.4.7': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/signature-v4@5.3.10': + '@smithy/signature-v4@5.3.12': dependencies: - '@smithy/is-array-buffer': 4.2.1 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-hex-encoding': 4.2.1 - '@smithy/util-middleware': 4.2.10 - '@smithy/util-uri-escape': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.12 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/smithy-client@4.12.0': + '@smithy/smithy-client@4.12.7': dependencies: - '@smithy/core': 3.23.6 - '@smithy/middleware-endpoint': 4.4.20 - '@smithy/middleware-stack': 4.2.10 - '@smithy/protocol-http': 5.3.10 - '@smithy/types': 4.13.0 - '@smithy/util-stream': 4.5.15 + '@smithy/core': 3.23.12 + '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-stack': 4.2.12 + '@smithy/protocol-http': 5.3.12 + '@smithy/types': 4.13.1 + '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@smithy/types@4.13.0': + '@smithy/types@4.13.1': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.2.10': + '@smithy/url-parser@4.2.12': dependencies: - '@smithy/querystring-parser': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/querystring-parser': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-base64@4.3.1': + '@smithy/util-base64@4.3.2': dependencies: - '@smithy/util-buffer-from': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-body-length-browser@4.2.1': + '@smithy/util-body-length-browser@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@4.2.2': + '@smithy/util-body-length-node@4.2.3': dependencies: tslib: 2.8.1 @@ -14835,65 +15097,65 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.1': + '@smithy/util-buffer-from@4.2.2': dependencies: - '@smithy/is-array-buffer': 4.2.1 + '@smithy/is-array-buffer': 4.2.2 tslib: 2.8.1 - '@smithy/util-config-provider@4.2.1': + '@smithy/util-config-provider@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.36': + '@smithy/util-defaults-mode-browser@4.3.43': dependencies: - '@smithy/property-provider': 4.2.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.39': + '@smithy/util-defaults-mode-node@4.2.47': dependencies: - '@smithy/config-resolver': 4.4.9 - '@smithy/credential-provider-imds': 4.2.10 - '@smithy/node-config-provider': 4.3.10 - '@smithy/property-provider': 4.2.10 - '@smithy/smithy-client': 4.12.0 - '@smithy/types': 4.13.0 + '@smithy/config-resolver': 4.4.13 + '@smithy/credential-provider-imds': 4.2.12 + '@smithy/node-config-provider': 4.3.12 + '@smithy/property-provider': 4.2.12 + '@smithy/smithy-client': 4.12.7 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-endpoints@3.3.1': + '@smithy/util-endpoints@3.3.3': dependencies: - '@smithy/node-config-provider': 4.3.10 - '@smithy/types': 4.13.0 + '@smithy/node-config-provider': 4.3.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.2.1': + '@smithy/util-hex-encoding@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.2.10': + '@smithy/util-middleware@4.2.12': dependencies: - '@smithy/types': 4.13.0 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-retry@4.2.10': + '@smithy/util-retry@4.2.12': dependencies: - '@smithy/service-error-classification': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/service-error-classification': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-stream@4.5.15': + '@smithy/util-stream@4.5.20': dependencies: - '@smithy/fetch-http-handler': 5.3.11 - '@smithy/node-http-handler': 4.4.12 - '@smithy/types': 4.13.0 - '@smithy/util-base64': 4.3.1 - '@smithy/util-buffer-from': 4.2.1 - '@smithy/util-hex-encoding': 4.2.1 - '@smithy/util-utf8': 4.2.1 + '@smithy/fetch-http-handler': 5.3.15 + '@smithy/node-http-handler': 4.5.0 + '@smithy/types': 4.13.1 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-uri-escape@4.2.1': + '@smithy/util-uri-escape@4.2.2': dependencies: tslib: 2.8.1 @@ -14902,18 +15164,18 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.2.1': + '@smithy/util-utf8@4.2.2': dependencies: - '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-buffer-from': 4.2.2 tslib: 2.8.1 - '@smithy/util-waiter@4.2.10': + '@smithy/util-waiter@4.2.13': dependencies: - '@smithy/abort-controller': 4.2.10 - '@smithy/types': 4.13.0 + '@smithy/abort-controller': 4.2.12 + '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/uuid@1.1.1': + '@smithy/uuid@1.1.2': dependencies: tslib: 2.8.1 @@ -14992,20 +15254,21 @@ snapshots: '@swc/counter': 0.1.3 optional: true - '@tabler/icons-react@3.36.1(react@18.3.1)': + '@tabler/icons-react@3.40.0(react@18.3.1)': dependencies: - '@tabler/icons': 3.36.1 + '@tabler/icons': 3.40.0 react: 18.3.1 - '@tabler/icons@3.36.1': {} + '@tabler/icons@3.40.0': {} - '@tanstack/eslint-plugin-query@5.62.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2)': + '@tanstack/eslint-plugin-query@5.94.4(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) - eslint: 9.39.2(jiti@2.4.2) + '@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.4.2) + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - supports-color - - typescript '@tanstack/query-core@5.90.17': {} @@ -15014,197 +15277,197 @@ snapshots: '@tanstack/query-core': 5.90.17 react: 18.3.1 - '@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)': + '@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/pm': 3.17.1 + '@tiptap/pm': 3.20.4 - '@tiptap/extension-blockquote@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-blockquote@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-bold@3.17.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-bold@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-bubble-menu@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-bubble-menu@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: '@floating-ui/dom': 1.7.4 - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 optional: true - '@tiptap/extension-bullet-list@3.17.1(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-bullet-list@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-code-block@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-code-block@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/extension-code@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-code@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-collaboration-caret@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29))': + '@tiptap/extension-collaboration-caret@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 - '@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 + '@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) - '@tiptap/extension-collaboration@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29))(yjs@13.6.29)': + '@tiptap/extension-collaboration@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 - '@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29) - yjs: 13.6.29 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 + '@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) + yjs: 13.6.30 - '@tiptap/extension-color@3.17.1(@tiptap/extension-text-style@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)))': + '@tiptap/extension-color@3.20.4(@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)))': dependencies: - '@tiptap/extension-text-style': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) + '@tiptap/extension-text-style': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-document@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-document@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-dropcursor@3.19.0(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-dropcursor@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-floating-menu@3.19.0(@floating-ui/dom@1.7.3)(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-floating-menu@3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: '@floating-ui/dom': 1.7.3 - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 optional: true - '@tiptap/extension-gapcursor@3.19.0(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-gapcursor@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-hard-break@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-hard-break@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-heading@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-heading@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-highlight@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-highlight@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-history@3.17.1(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-history@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-horizontal-rule@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-horizontal-rule@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/extension-image@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-image@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-italic@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-italic@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-link@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-link@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 linkifyjs: 4.3.2 - '@tiptap/extension-list-item@3.19.0(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-list-item@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-list-keymap@3.19.0(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-list-keymap@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/extension-ordered-list@3.19.0(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-ordered-list@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-paragraph@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-paragraph@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-placeholder@3.17.1(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1))': + '@tiptap/extension-placeholder@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-strike@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-strike@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-subscript@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-subscript@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/extension-superscript@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-superscript@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/extension-table@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-table@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/extension-text-align@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-text-align@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-text-style@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-text@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-text@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-typography@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-typography@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-underline@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-underline@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-unique-id@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extension-unique-id@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 uuid: 10.0.0 - '@tiptap/extension-youtube@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))': + '@tiptap/extension-youtube@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/html@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(happy-dom@20.1.0)': + '@tiptap/html@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 - happy-dom: 20.1.0 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 + happy-dom: 20.8.4 - '@tiptap/pm@3.17.1': + '@tiptap/pm@3.20.4': dependencies: - prosemirror-changeset: 2.3.1 + prosemirror-changeset: 2.4.0 prosemirror-collab: 1.3.1 prosemirror-commands: 1.6.2 prosemirror-dropcursor: 1.8.1 @@ -15223,10 +15486,10 @@ snapshots: prosemirror-transform: 1.10.4 prosemirror-view: 1.40.0 - '@tiptap/react@3.17.1(@floating-ui/dom@1.7.3)(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tiptap/react@3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 '@types/use-sync-external-store': 0.0.6 @@ -15235,51 +15498,51 @@ snapshots: react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: - '@tiptap/extension-bubble-menu': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) - '@tiptap/extension-floating-menu': 3.19.0(@floating-ui/dom@1.7.3)(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) + '@tiptap/extension-bubble-menu': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-floating-menu': 3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) transitivePeerDependencies: - '@floating-ui/dom' - '@tiptap/starter-kit@3.17.1': + '@tiptap/starter-kit@3.20.4': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/extension-blockquote': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-bold': 3.17.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-bullet-list': 3.17.1(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) - '@tiptap/extension-code': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-code-block': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) - '@tiptap/extension-document': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-dropcursor': 3.19.0(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) - '@tiptap/extension-gapcursor': 3.19.0(@tiptap/extensions@3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) - '@tiptap/extension-hard-break': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-heading': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-horizontal-rule': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) - '@tiptap/extension-italic': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-link': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) - '@tiptap/extension-list': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) - '@tiptap/extension-list-item': 3.19.0(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) - '@tiptap/extension-list-keymap': 3.19.0(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) - '@tiptap/extension-ordered-list': 3.19.0(@tiptap/extension-list@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)) - '@tiptap/extension-paragraph': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-strike': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-text': 3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extension-underline': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1)) - '@tiptap/extensions': 3.19.0(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/extension-blockquote': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-bold': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-bullet-list': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-code': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-code-block': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-document': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-dropcursor': 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-gapcursor': 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-hard-break': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-heading': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-horizontal-rule': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-italic': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-link': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list-item': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-list-keymap': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-ordered-list': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-paragraph': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-strike': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-text': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-underline': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/suggestion@3.17.1(@tiptap/core@3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1))(@tiptap/pm@3.17.1)': + '@tiptap/suggestion@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.17.1(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.17.1) - '@tiptap/pm': 3.17.1 + '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/pm': 3.20.4 - '@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29)': + '@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30)': dependencies: lib0: 0.2.117 prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 prosemirror-view: 1.40.0 - y-protocols: 1.0.6(yjs@13.6.29) - yjs: 13.6.29 + y-protocols: 1.0.6(yjs@13.6.30) + yjs: 13.6.30 '@tokenizer/inflate@0.4.1': dependencies: @@ -15307,47 +15570,43 @@ snapshots: dependencies: tslib: 2.8.1 - '@types/asn1@0.2.4': - dependencies: - '@types/node': 22.19.1 - '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.5 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.28.5 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__traverse@7.20.5': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.28.5 - '@types/bcrypt@5.0.2': + '@types/bcrypt@6.0.0': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/blueimp-load-image@5.16.6': {} '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/bytes@3.1.5': {} '@types/connect@3.4.38': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/cookie@0.4.1': {} @@ -15355,7 +15614,7 @@ snapshots: '@types/cors@2.8.17': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/d3-array@3.2.1': {} @@ -15494,7 +15753,7 @@ snapshots: '@types/express-serve-static-core@4.17.43': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/qs': 6.9.14 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -15518,7 +15777,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/geojson@7946.0.14': {} @@ -15549,17 +15808,18 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 + + '@types/jsonwebtoken@9.0.10': + dependencies: + '@types/ms': 2.1.0 + '@types/node': 25.5.0 '@types/jsonwebtoken@9.0.6': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 - '@types/jsonwebtoken@9.0.7': - dependencies: - '@types/node': 22.19.1 - - '@types/katex@0.16.7': {} + '@types/katex@0.16.8': {} '@types/linkify-it@5.0.0': {} @@ -15574,7 +15834,7 @@ snapshots: '@types/methods@1.1.4': {} - '@types/mime-types@2.1.4': {} + '@types/mime-types@3.0.1': {} '@types/mime@1.3.5': {} @@ -15582,32 +15842,28 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@20.19.19': - dependencies: - undici-types: 6.21.0 - - '@types/node@22.13.4': - dependencies: - undici-types: 6.20.0 - '@types/node@22.19.1': dependencies: undici-types: 6.21.0 - '@types/nodemailer@6.4.17': + '@types/node@25.5.0': dependencies: - '@types/node': 22.19.1 + undici-types: 7.18.2 + + '@types/nodemailer@7.0.11': + dependencies: + '@types/node': 25.5.0 '@types/oauth@0.9.6': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/parse-json@4.0.2': {} - '@types/passport-google-oauth20@2.0.16': + '@types/passport-google-oauth20@2.0.17': dependencies: - '@types/express': 4.17.21 - '@types/passport': 1.0.16 + '@types/express': 4.17.23 + '@types/passport': 1.0.17 '@types/passport-oauth2': 1.4.17 '@types/passport-jwt@4.0.1': @@ -15617,9 +15873,9 @@ snapshots: '@types/passport-oauth2@1.4.17': dependencies: - '@types/express': 4.17.21 + '@types/express': 4.17.23 '@types/oauth': 0.9.6 - '@types/passport': 1.0.16 + '@types/passport': 1.0.17 '@types/passport-strategy@0.2.38': dependencies: @@ -15628,7 +15884,7 @@ snapshots: '@types/passport@1.0.16': dependencies: - '@types/express': 4.17.21 + '@types/express': 4.17.23 '@types/passport@1.0.17': dependencies: @@ -15636,9 +15892,9 @@ snapshots: '@types/prop-types@15.7.11': {} - '@types/qrcode@1.5.5': + '@types/qrcode@1.5.6': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/qs@6.14.0': {} @@ -15658,13 +15914,13 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/serve-static@1.15.5': dependencies: '@types/http-errors': 2.0.4 '@types/mime': 3.0.4 - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/stack-utils@2.0.3': {} @@ -15672,7 +15928,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/supertest@6.0.3': dependencies: @@ -15696,15 +15952,15 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/xml-encryption@1.2.4': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/xml2js@0.4.14': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@types/yargs-parser@21.0.3': {} @@ -15714,166 +15970,98 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 - '@typescript-eslint/eslint-plugin@8.17.0(@typescript-eslint/parser@8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2))(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2)': + '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3))(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) - '@typescript-eslint/scope-manager': 8.17.0 - '@typescript-eslint/type-utils': 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) - '@typescript-eslint/utils': 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.17.0 - eslint: 9.39.2(jiti@2.4.2) - graphemer: 1.4.0 - ignore: 5.3.1 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.57.1 + '@typescript-eslint/type-utils': 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.1 + eslint: 9.39.4(jiti@2.4.2) + ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.7.2) - optionalDependencies: - typescript: 5.7.2 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3))(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3)': + '@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.24.1 - '@typescript-eslint/type-utils': 8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.24.1 - eslint: 9.39.2(jiti@2.4.2) - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare: 1.4.0 - ts-api-utils: 2.0.1(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2)': - dependencies: - '@typescript-eslint/scope-manager': 8.17.0 - '@typescript-eslint/types': 8.17.0 - '@typescript-eslint/typescript-estree': 8.17.0(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.17.0 - debug: 4.4.0 - eslint: 9.39.2(jiti@2.4.2) - optionalDependencies: - typescript: 5.7.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.24.1 - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.24.1 - debug: 4.4.0 - eslint: 9.39.2(jiti@2.4.2) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.17.0': - dependencies: - '@typescript-eslint/types': 8.17.0 - '@typescript-eslint/visitor-keys': 8.17.0 - - '@typescript-eslint/scope-manager@8.24.1': - dependencies: - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/visitor-keys': 8.24.1 - - '@typescript-eslint/type-utils@8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2)': - dependencies: - '@typescript-eslint/typescript-estree': 8.17.0(typescript@5.7.2) - '@typescript-eslint/utils': 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.57.1 + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.1 debug: 4.4.3 - eslint: 9.39.2(jiti@2.4.2) - ts-api-utils: 1.3.0(typescript@5.7.2) - optionalDependencies: - typescript: 5.7.2 + eslint: 9.39.4(jiti@2.4.2) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3)': + '@typescript-eslint/project-service@8.57.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) + '@typescript-eslint/tsconfig-utils': 8.57.1(typescript@5.9.3) + '@typescript-eslint/types': 8.57.1 debug: 4.4.3 - eslint: 9.39.2(jiti@2.4.2) - ts-api-utils: 2.0.1(typescript@5.7.3) - typescript: 5.7.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.17.0': {} - - '@typescript-eslint/types@8.24.1': {} - - '@typescript-eslint/typescript-estree@8.17.0(typescript@5.7.2)': + '@typescript-eslint/scope-manager@8.57.1': dependencies: - '@typescript-eslint/types': 8.17.0 - '@typescript-eslint/visitor-keys': 8.17.0 + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/visitor-keys': 8.57.1 + + '@typescript-eslint/tsconfig-utils@8.57.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) debug: 4.4.3 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.7.3 - ts-api-utils: 1.3.0(typescript@5.7.2) - optionalDependencies: - typescript: 5.7.2 + eslint: 9.39.4(jiti@2.4.2) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.24.1(typescript@5.7.3)': + '@typescript-eslint/types@8.57.1': {} + + '@typescript-eslint/typescript-estree@8.57.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/visitor-keys': 8.24.1 + '@typescript-eslint/project-service': 8.57.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.57.1(typescript@5.9.3) + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/visitor-keys': 8.57.1 debug: 4.4.3 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.4 - semver: 7.7.3 - ts-api-utils: 2.0.1(typescript@5.7.3) - typescript: 5.7.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2)': + '@typescript-eslint/utils@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.39.2(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.17.0 - '@typescript-eslint/types': 8.17.0 - '@typescript-eslint/typescript-estree': 8.17.0(typescript@5.7.2) - eslint: 9.39.2(jiti@2.4.2) - optionalDependencies: - typescript: 5.7.2 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.57.1 + '@typescript-eslint/types': 8.57.1 + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) + eslint: 9.39.4(jiti@2.4.2) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3)': + '@typescript-eslint/visitor-keys@8.57.1': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.39.2(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.24.1 - '@typescript-eslint/types': 8.24.1 - '@typescript-eslint/typescript-estree': 8.24.1(typescript@5.7.3) - eslint: 9.39.2(jiti@2.4.2) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.17.0': - dependencies: - '@typescript-eslint/types': 8.17.0 - eslint-visitor-keys: 4.2.0 - - '@typescript-eslint/visitor-keys@8.24.1': - dependencies: - '@typescript-eslint/types': 8.24.1 - eslint-visitor-keys: 4.2.0 + '@typescript-eslint/types': 8.57.1 + eslint-visitor-keys: 5.0.1 '@ucast/core@1.10.2': {} @@ -15952,19 +16140,17 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@upsetjs/venn.js@2.0.0': + optionalDependencies: + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + '@vercel/oidc@3.1.0': {} - '@vitejs/plugin-react@5.1.1(vite@7.2.4(@types/node@22.19.1)(jiti@2.4.2)(less@4.2.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))': + '@vitejs/plugin-react@6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0))': dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) - '@rolldown/pluginutils': 1.0.0-beta.47 - '@types/babel__core': 7.20.5 - react-refresh: 0.18.0 - vite: 7.2.4(@types/node@22.19.1)(jiti@2.4.2)(less@4.2.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0) - transitivePeerDependencies: - - supports-color + '@rolldown/pluginutils': 1.0.0-rc.7 + vite: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0) '@webassemblyjs/ast@1.14.1': dependencies: @@ -16077,14 +16263,16 @@ snapshots: dependencies: acorn: 8.15.0 - acorn-jsx@5.3.2(acorn@8.15.0): + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn-walk@8.3.2: {} acorn@8.15.0: {} + acorn@8.16.0: {} + address@1.2.2: {} agent-base@7.1.1: @@ -16093,49 +16281,51 @@ snapshots: transitivePeerDependencies: - supports-color - ai-sdk-ollama@3.7.0(ai@6.0.86(zod@4.3.6))(zod@4.3.6): + agent-base@7.1.4: {} + + ai-sdk-ollama@3.8.1(ai@6.0.134(zod@4.3.6))(zod@4.3.6): dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) - ai: 6.0.86(zod@4.3.6) - jsonrepair: 3.13.2 + '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) + ai: 6.0.134(zod@4.3.6) + jsonrepair: 3.13.3 ollama: 0.6.3 transitivePeerDependencies: - zod - ai@6.0.86(zod@4.3.6): + ai@6.0.134(zod@4.3.6): dependencies: - '@ai-sdk/gateway': 3.0.46(zod@4.3.6) + '@ai-sdk/gateway': 3.0.77(zod@4.3.6) '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.15(zod@4.3.6) + '@ai-sdk/provider-utils': 4.0.21(zod@4.3.6) '@opentelemetry/api': 1.9.0 zod: 4.3.6 - ajv-formats@2.1.1(ajv@8.17.1): + ajv-formats@2.1.1(ajv@8.18.0): optionalDependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv-formats@3.0.1(ajv@8.17.1): + ajv-formats@3.0.1(ajv@8.18.0): optionalDependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv-keywords@3.5.2(ajv@6.12.6): + ajv-keywords@3.5.2(ajv@6.14.0): dependencies: - ajv: 6.12.6 + ajv: 6.14.0 - ajv-keywords@5.1.0(ajv@8.17.1): + ajv-keywords@5.1.0(ajv@8.18.0): dependencies: - ajv: 8.17.1 + ajv: 8.18.0 fast-deep-equal: 3.1.3 - ajv@6.12.6: + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.0.6 @@ -16164,8 +16354,6 @@ snapshots: ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} - ansis@4.2.0: {} anymatch@3.1.3: @@ -16190,13 +16378,18 @@ snapshots: call-bind: 1.0.7 is-array-buffer: 3.0.4 + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + array-includes@3.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.5 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 is-string: 1.1.0 array-timsort@1.0.3: {} @@ -16207,7 +16400,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.5 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 es-shim-unscopables: 1.0.2 array.prototype.flat@1.3.2: @@ -16217,9 +16410,9 @@ snapshots: es-abstract: 1.23.5 es-shim-unscopables: 1.0.2 - array.prototype.flatmap@1.3.2: + array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.5 es-shim-unscopables: 1.0.2 @@ -16243,11 +16436,17 @@ snapshots: is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 - asap@2.0.6: {} - - asn1@0.2.6: + arraybuffer.prototype.slice@1.0.4: dependencies: - safer-buffer: 2.1.2 + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + asap@2.0.6: {} async-lock@1.4.1: {} @@ -16275,7 +16474,7 @@ snapshots: '@fastify/error': 4.0.0 fastq: 1.17.1 - axios@1.13.5: + axios@1.13.6: dependencies: follow-redirects: 1.15.11 form-data: 4.0.5 @@ -16283,13 +16482,13 @@ snapshots: transitivePeerDependencies: - debug - babel-jest@30.2.0(@babel/core@7.28.5): + babel-jest@30.3.0(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 - '@jest/transform': 30.2.0 + '@jest/transform': 30.3.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.2.0(@babel/core@7.28.5) + babel-preset-jest: 30.3.0(@babel/core@7.28.5) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -16315,7 +16514,7 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-jest-hoist@30.2.0: + babel-plugin-jest-hoist@30.3.0: dependencies: '@types/babel__core': 7.20.5 @@ -16375,10 +16574,10 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.5) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.5) - babel-preset-jest@30.2.0(@babel/core@7.28.5): + babel-preset-jest@30.3.0(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 - babel-plugin-jest-hoist: 30.2.0 + babel-plugin-jest-hoist: 30.3.0 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) balanced-match@1.0.2: {} @@ -16458,13 +16657,6 @@ snapshots: browser-fs-access@0.29.1: {} - browserslist@4.24.2: - dependencies: - caniuse-lite: 1.0.30001684 - electron-to-chromium: 1.5.65 - node-releases: 2.0.18 - update-browserslist-db: 1.1.1(browserslist@4.24.2) - browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.19 @@ -16497,7 +16689,7 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - bullmq@5.70.1: + bullmq@5.71.0: dependencies: cron-parser: 4.9.0 ioredis: 5.9.3 @@ -16523,12 +16715,19 @@ snapshots: call-bind@1.0.7: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.3.0 set-function-length: 1.2.2 + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -16542,8 +16741,6 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001684: {} - caniuse-lite@1.0.30001769: {} canvas-roundrect-polyfill@0.0.1: {} @@ -16583,7 +16780,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.22.0 + undici: 7.24.0 whatwg-mimetype: 4.0.0 chevrotain-allstar@0.3.1(chevrotain@11.0.3): @@ -16591,6 +16788,11 @@ snapshots: chevrotain: 11.0.3 lodash-es: 4.17.23 + chevrotain-allstar@0.3.1(chevrotain@11.1.2): + dependencies: + chevrotain: 11.1.2 + lodash-es: 4.17.23 + chevrotain@11.0.3: dependencies: '@chevrotain/cst-dts-gen': 11.0.3 @@ -16600,6 +16802,15 @@ snapshots: '@chevrotain/utils': 11.0.3 lodash-es: 4.17.23 + chevrotain@11.1.2: + dependencies: + '@chevrotain/cst-dts-gen': 11.1.2 + '@chevrotain/gast': 11.1.2 + '@chevrotain/regexp-to-ast': 11.1.2 + '@chevrotain/types': 11.1.2 + '@chevrotain/utils': 11.1.2 + lodash-es: 4.17.23 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -16709,38 +16920,35 @@ snapshots: commander@8.3.0: {} - comment-json@4.2.5: + comment-json@4.4.1: dependencies: array-timsort: 1.0.3 core-util-is: 1.0.3 esprima: 4.0.1 - has-own-prop: 2.0.0 - repeat-string: 1.6.1 component-emitter@1.3.1: {} concat-map@0.0.1: {} - concurrently@9.1.2: + concurrently@9.2.1: dependencies: chalk: 4.1.2 - lodash: 4.17.23 rxjs: 7.8.2 - shell-quote: 1.8.1 + shell-quote: 1.8.3 supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 conf@15.1.0: dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) atomically: 2.1.1 debounce-fn: 6.0.0 dot-prop: 10.1.0 env-paths: 3.0.0 json-schema-typed: 8.0.2 - semver: 7.7.3 + semver: 7.7.4 uint8array-extras: 1.5.0 confbox@0.1.8: {} @@ -16810,14 +17018,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - cosmiconfig@9.0.0(typescript@5.7.3): + cosmiconfig@9.0.0(typescript@5.9.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: - typescript: 5.7.3 + typescript: 5.9.3 crc-32@0.3.0: {} @@ -16834,9 +17042,14 @@ snapshots: '@types/luxon': 3.7.1 luxon: 3.7.2 + cross-env@10.1.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 + cross-env@7.0.3: dependencies: - cross-spawn: 7.0.5 + cross-spawn: 7.0.6 cross-fetch@4.0.0: dependencies: @@ -16850,6 +17063,12 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css-select@5.1.0: dependencies: boolbase: 1.0.0 @@ -16869,17 +17088,17 @@ snapshots: csstype@3.1.3: {} - cytoscape-cose-bilkent@4.1.0(cytoscape@3.30.2): + cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1): dependencies: cose-base: 1.0.3 - cytoscape: 3.30.2 + cytoscape: 3.33.1 - cytoscape-fcose@2.2.0(cytoscape@3.30.2): + cytoscape-fcose@2.2.0(cytoscape@3.33.1): dependencies: cose-base: 2.2.0 - cytoscape: 3.30.2 + cytoscape: 3.33.1 - cytoscape@3.30.2: {} + cytoscape@3.33.1: {} d3-array@2.12.1: dependencies: @@ -17048,7 +17267,7 @@ snapshots: d3-transition: 3.0.1(d3-selection@3.0.0) d3-zoom: 3.0.0 - dagre-d3-es@7.0.13: + dagre-d3-es@7.0.14: dependencies: d3: 7.9.0 lodash-es: 4.17.23 @@ -17056,7 +17275,7 @@ snapshots: data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 + whatwg-url: 14.2.0 data-view-buffer@1.0.1: dependencies: @@ -17064,18 +17283,36 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + date-fns@4.1.0: {} dateformat@4.6.3: {} @@ -17110,7 +17347,7 @@ snapshots: decamelize@1.2.0: {} - decimal.js@10.4.3: {} + decimal.js@10.6.0: {} dedent@1.7.1(babel-plugin-macros@3.1.0): optionalDependencies: @@ -17150,6 +17387,8 @@ snapshots: dequal@2.0.3: {} + detect-libc@2.1.2: {} + detect-newline@3.1.0: {} detect-node-es@1.1.0: {} @@ -17190,7 +17429,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 csstype: 3.1.3 dom-serializer@2.0.0: @@ -17235,7 +17474,7 @@ snapshots: duck@0.1.12: dependencies: - underscore: 1.13.7 + underscore: 1.13.8 dunder-proto@1.0.1: dependencies: @@ -17243,8 +17482,6 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - eastasianwidth@0.2.0: {} - ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 @@ -17257,8 +17494,6 @@ snapshots: electron-to-chromium@1.5.286: {} - electron-to-chromium@1.5.65: {} - emittery@0.13.1: {} emoji-mart@5.6.0: {} @@ -17267,8 +17502,6 @@ snapshots: emoji-regex@8.0.0: {} - emoji-regex@9.2.2: {} - encodeurl@2.0.0: {} encoding-sniffer@0.2.1: @@ -17298,7 +17531,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.19.1 + '@types/node': 25.5.0 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -17353,9 +17586,9 @@ snapshots: data-view-buffer: 1.0.1 data-view-byte-length: 1.0.1 data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 es-set-tostringtag: 2.1.0 es-to-primitive: 1.3.0 function.prototype.name: 1.1.6 @@ -17365,7 +17598,7 @@ snapshots: gopd: 1.2.0 has-property-descriptors: 1.0.2 has-proto: 1.0.3 - has-symbols: 1.0.3 + has-symbols: 1.1.0 hasown: 2.0.2 internal-slot: 1.0.7 is-array-buffer: 3.0.4 @@ -17393,48 +17626,93 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.16 - es-define-property@1.0.0: + es-abstract@1.24.1: dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-iterator-helpers@1.2.0: + es-iterator-helpers@1.3.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.23.5 + es-abstract: 1.24.1 es-errors: 1.3.0 - es-set-tostringtag: 2.0.3 + es-set-tostringtag: 2.1.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 globalthis: 1.0.4 gopd: 1.2.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - iterator.prototype: 1.1.3 - safe-array-concat: 1.1.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + safe-array-concat: 1.1.3 es-module-lexer@2.0.0: {} - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - es-set-tostringtag@2.1.0: dependencies: es-errors: 1.3.0 @@ -17454,35 +17732,63 @@ snapshots: es6-promise-pool@2.5.0: {} - esbuild@0.25.0: + esbuild@0.27.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.0 - '@esbuild/android-arm': 0.25.0 - '@esbuild/android-arm64': 0.25.0 - '@esbuild/android-x64': 0.25.0 - '@esbuild/darwin-arm64': 0.25.0 - '@esbuild/darwin-x64': 0.25.0 - '@esbuild/freebsd-arm64': 0.25.0 - '@esbuild/freebsd-x64': 0.25.0 - '@esbuild/linux-arm': 0.25.0 - '@esbuild/linux-arm64': 0.25.0 - '@esbuild/linux-ia32': 0.25.0 - '@esbuild/linux-loong64': 0.25.0 - '@esbuild/linux-mips64el': 0.25.0 - '@esbuild/linux-ppc64': 0.25.0 - '@esbuild/linux-riscv64': 0.25.0 - '@esbuild/linux-s390x': 0.25.0 - '@esbuild/linux-x64': 0.25.0 - '@esbuild/netbsd-arm64': 0.25.0 - '@esbuild/netbsd-x64': 0.25.0 - '@esbuild/openbsd-arm64': 0.25.0 - '@esbuild/openbsd-x64': 0.25.0 - '@esbuild/sunos-x64': 0.25.0 - '@esbuild/win32-arm64': 0.25.0 - '@esbuild/win32-ia32': 0.25.0 - '@esbuild/win32-x64': 0.25.0 + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 - escalade@3.1.1: {} + esbuild@0.27.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 escalade@3.2.0: {} @@ -17494,38 +17800,47 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.0.1(eslint@9.39.2(jiti@2.4.2)): - dependencies: - eslint: 9.39.2(jiti@2.4.2) + escape-string-regexp@5.0.0: {} - eslint-plugin-react-hooks@5.1.0(eslint@9.39.2(jiti@2.4.2)): + eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.4.2)): dependencies: - eslint: 9.39.2(jiti@2.4.2) + eslint: 9.39.4(jiti@2.4.2) - eslint-plugin-react-refresh@0.4.16(eslint@9.39.2(jiti@2.4.2)): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.4.2)): dependencies: - eslint: 9.39.2(jiti@2.4.2) + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + eslint: 9.39.4(jiti@2.4.2) + hermes-parser: 0.25.1 + zod: 4.3.6 + zod-validation-error: 4.0.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color - eslint-plugin-react@7.37.2(eslint@9.39.2(jiti@2.4.2)): + eslint-plugin-react-refresh@0.5.2(eslint@9.39.4(jiti@2.4.2)): + dependencies: + eslint: 9.39.4(jiti@2.4.2) + + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.4.2)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.2 + array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.0 - eslint: 9.39.2(jiti@2.4.2) + es-iterator-helpers: 1.3.1 + eslint: 9.39.4(jiti@2.4.2) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.8 + minimatch: 3.1.5 + object.entries: 1.1.9 object.fromentries: 2.0.8 - object.values: 1.2.0 + object.values: 1.2.1 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.11 + string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 eslint-scope@5.1.1: @@ -17540,33 +17855,33 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.0: {} - eslint-visitor-keys@4.2.1: {} - eslint@9.39.2(jiti@2.4.2): + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.4.2)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.1 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.2 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - ajv: 6.12.6 + ajv: 6.14.0 chalk: 4.1.2 - cross-spawn: 7.0.5 + cross-spawn: 7.0.6 debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 - esquery: 1.5.0 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -17577,7 +17892,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.3 optionalDependencies: @@ -17587,13 +17902,13 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 esprima@4.0.1: {} - esquery@1.5.0: + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -17644,10 +17959,19 @@ snapshots: jest-mock: 30.2.0 jest-util: 30.2.0 - express-rate-limit@8.2.1(express@5.2.1): + expect@30.3.0: + dependencies: + '@jest/expect-utils': 30.3.0 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + express-rate-limit@8.2.2(express@5.2.1): dependencies: express: 5.2.1 - ip-address: 10.0.1 + ip-address: 10.1.0 express@5.2.1: dependencies: @@ -17698,21 +18022,13 @@ snapshots: fast-equals@5.3.4: {} - fast-glob@3.3.2: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-json-stringify@6.0.1: dependencies: '@fastify/merge-json-schemas': 0.2.1 - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) fast-uri: 3.0.6 json-schema-ref-resolver: 2.0.1 rfdc: 1.3.1 @@ -17727,15 +18043,21 @@ snapshots: fast-uri@3.0.6: {} - fast-xml-parser@5.3.6: + fast-xml-builder@1.1.4: dependencies: - strnum: 2.1.2 + path-expression-matcher: 1.2.0 + + fast-xml-parser@5.5.8: + dependencies: + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.2.0 + strnum: 2.2.1 fastify-plugin@5.0.1: {} fastify-plugin@5.1.0: {} - fastify@5.7.4: + fastify@5.8.2: dependencies: '@fastify/ajv-compiler': 4.0.5 '@fastify/error': 4.0.0 @@ -17744,13 +18066,13 @@ snapshots: abstract-logging: 2.0.1 avvio: 9.1.0 fast-json-stringify: 6.0.1 - find-my-way: 9.4.0 + find-my-way: 9.5.0 light-my-request: 6.6.0 pino: 10.1.0 process-warning: 5.0.0 rfdc: 1.3.1 secure-json-parse: 4.0.0 - semver: 7.7.3 + semver: 7.7.4 toad-cache: 3.7.0 fastq@1.17.1: @@ -17777,7 +18099,7 @@ snapshots: file-saver@2.0.5: {} - file-type@21.3.0: + file-type@21.3.2: dependencies: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.4 @@ -17788,7 +18110,7 @@ snapshots: filelist@1.0.4: dependencies: - minimatch: 5.1.6 + minimatch: 5.1.8 fill-range@7.1.1: dependencies: @@ -17805,7 +18127,7 @@ snapshots: transitivePeerDependencies: - supports-color - find-my-way@9.4.0: + find-my-way@9.5.0: dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 @@ -17823,12 +18145,12 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.2.9 + flatted: 3.4.2 keyv: 4.5.4 flat@5.0.2: {} - flatted@3.2.9: {} + flatted@3.4.2: {} follow-redirects@1.15.11: {} @@ -17836,10 +18158,9 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.1.1: + for-each@0.3.5: dependencies: - cross-spawn: 7.0.5 - signal-exit: 4.1.0 + is-callable: 1.2.7 fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))): dependencies: @@ -17850,10 +18171,10 @@ snapshots: deepmerge: 4.3.1 fs-extra: 10.1.0 memfs: 3.5.3 - minimatch: 3.1.2 + minimatch: 3.1.5 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.7.3 + semver: 7.7.4 tapable: 2.2.1 typescript: 5.9.3 webpack: 5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)) @@ -17892,7 +18213,7 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 - fs-extra@11.3.3: + fs-extra@11.3.4: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 @@ -17912,6 +18233,15 @@ snapshots: es-abstract: 1.23.5 functions-have-names: 1.2.3 + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + functions-have-names@1.2.3: {} fuzzy@0.1.3: {} @@ -17922,14 +18252,6 @@ snapshots: get-east-asian-width@1.4.0: {} - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -17960,6 +18282,12 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + get-tsconfig@4.7.5: dependencies: resolve-pkg-maps: 1.0.0 @@ -17974,14 +18302,11 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.5.0: + glob@13.0.6: dependencies: - foreground-child: 3.1.1 - jackspeak: 3.4.3 - minimatch: 9.0.4 - minipass: 7.1.2 - package-json-from-dist: 1.0.0 - path-scurry: 1.11.1 + minimatch: 10.2.4 + minipass: 7.1.3 + path-scurry: 2.0.2 globals@11.12.0: {} @@ -17989,7 +18314,7 @@ snapshots: globals@15.13.0: {} - globals@15.15.0: {} + globals@17.4.0: {} globalthis@1.0.4: dependencies: @@ -18002,8 +18327,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - hachure-fill@0.5.2: {} handlebars@4.7.8: @@ -18015,11 +18338,12 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - happy-dom@20.1.0: + happy-dom@20.8.4: dependencies: - '@types/node': 20.19.19 + '@types/node': 25.5.0 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 + entities: 7.0.1 whatwg-mimetype: 3.0.0 ws: 8.19.0 transitivePeerDependencies: @@ -18030,15 +18354,15 @@ snapshots: has-flag@4.0.0: {} - has-own-prop@2.0.0: {} - has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 has-proto@1.0.3: {} - has-symbols@1.0.3: {} + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 has-symbols@1.1.0: {} @@ -18056,6 +18380,12 @@ snapshots: help-me@5.0.0: {} + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + highlight.js@11.11.1: {} highlightjs-sap-abap@0.3.0: {} @@ -18064,7 +18394,7 @@ snapshots: dependencies: react-is: 16.13.1 - hono@4.12.3: {} + hono@4.12.8: {} hookified@1.15.1: {} @@ -18125,24 +18455,26 @@ snapshots: transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.5: + https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.1 + agent-base: 7.1.4 debug: 4.4.3 transitivePeerDependencies: - supports-color human-signals@2.1.0: {} - i18next-http-backend@2.7.3: + i18next-http-backend@3.0.2: dependencies: cross-fetch: 4.0.0 transitivePeerDependencies: - encoding - i18next@23.16.8: + i18next@25.10.1(typescript@5.9.3): dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 + optionalDependencies: + typescript: 5.9.3 iconv-lite@0.4.24: dependencies: @@ -18173,7 +18505,7 @@ snapshots: immediate@3.0.6: {} - immutable@4.3.7: {} + immutable@4.3.8: {} import-fresh@3.3.0: dependencies: @@ -18195,6 +18527,12 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + internmap@1.0.1: {} internmap@2.0.3: {} @@ -18203,11 +18541,11 @@ snapshots: dependencies: loose-envify: 1.4.0 - ioredis@5.4.1: + ioredis@5.10.1: dependencies: - '@ioredis/commands': 1.2.0 + '@ioredis/commands': 1.5.1 cluster-key-slot: 1.1.2 - debug: 4.3.4 + debug: 4.4.3 denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -18231,7 +18569,7 @@ snapshots: transitivePeerDependencies: - supports-color - ip-address@10.0.1: {} + ip-address@10.1.0: {} ipaddr.js@1.9.1: {} @@ -18242,6 +18580,12 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.3.0 + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} is-async-function@2.0.0: @@ -18261,6 +18605,11 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-callable@1.2.7: {} is-core-module@2.13.1: @@ -18271,17 +18620,28 @@ snapshots: dependencies: is-typed-array: 1.1.13 + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-docker@2.2.1: {} is-extglob@2.1.1: {} is-finalizationregistry@1.1.0: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 is-fullwidth-code-point@3.0.0: {} @@ -18308,6 +18668,11 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-number@7.0.0: {} is-potential-custom-element-name@1.0.1: {} @@ -18321,12 +18686,23 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + is-set@2.0.3: {} is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + is-stream@2.0.1: {} is-string@1.1.0: @@ -18334,16 +18710,31 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-symbol@1.1.0: dependencies: call-bind: 1.0.7 has-symbols: 1.1.0 safe-regex-test: 1.0.3 + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.16 + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + is-unicode-supported@0.1.0: {} is-unicode-supported@1.3.0: {} @@ -18356,9 +18747,13 @@ snapshots: dependencies: call-bind: 1.0.7 + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + is-weakset@2.0.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 get-intrinsic: 1.3.0 is-what@3.14.1: @@ -18384,7 +18779,7 @@ snapshots: '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.7.3 + semver: 7.7.4 transitivePeerDependencies: - supports-color @@ -18409,52 +18804,47 @@ snapshots: iterare@1.2.1: {} - iterator.prototype@1.1.3: + iterator.prototype@1.1.5: dependencies: - define-properties: 1.2.1 + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.7 + get-proto: 1.0.1 + has-symbols: 1.1.0 set-function-name: 2.0.2 - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jake@10.8.7: dependencies: async: 3.2.5 chalk: 4.1.2 filelist: 1.0.4 - minimatch: 3.1.2 + minimatch: 3.1.5 - jest-changed-files@30.2.0: + jest-changed-files@30.3.0: dependencies: execa: 5.1.1 - jest-util: 30.2.0 + jest-util: 30.3.0 p-limit: 3.1.0 - jest-circus@30.2.0(babel-plugin-macros@3.1.0): + jest-circus@30.3.0(babel-plugin-macros@3.1.0): dependencies: - '@jest/environment': 30.2.0 - '@jest/expect': 30.2.0 - '@jest/test-result': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.1(babel-plugin-macros@3.1.0) is-generator-fn: 2.1.0 - jest-each: 30.2.0 - jest-matcher-utils: 30.2.0 - jest-message-util: 30.2.0 - jest-runtime: 30.2.0 - jest-snapshot: 30.2.0 - jest-util: 30.2.0 + jest-each: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 p-limit: 3.1.0 - pretty-format: 30.2.0 + pretty-format: 30.3.0 pure-rand: 7.0.1 slash: 3.0.0 stack-utils: 2.0.6 @@ -18462,17 +18852,17 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)): + jest-cli@30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)) - '@jest/test-result': 30.2.0 - '@jest/types': 30.2.0 + '@jest/core': 30.3.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) + '@jest/test-result': 30.3.0 + '@jest/types': 30.3.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)) - jest-util: 30.2.0 - jest-validate: 30.2.0 + jest-config: 30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) + jest-util: 30.3.0 + jest-validate: 30.3.0 yargs: 17.7.2 transitivePeerDependencies: - '@types/node' @@ -18481,68 +18871,34 @@ snapshots: - supports-color - ts-node - jest-config@30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)): + jest-config@30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 '@jest/get-type': 30.1.0 '@jest/pattern': 30.0.1 - '@jest/test-sequencer': 30.2.0 - '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.28.5) + '@jest/test-sequencer': 30.3.0 + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.28.5) chalk: 4.1.2 ci-info: 4.4.0 deepmerge: 4.3.1 - glob: 10.5.0 + glob: 13.0.6 graceful-fs: 4.2.11 - jest-circus: 30.2.0(babel-plugin-macros@3.1.0) + jest-circus: 30.3.0(babel-plugin-macros@3.1.0) jest-docblock: 30.2.0 - jest-environment-node: 30.2.0 + jest-environment-node: 30.3.0 jest-regex-util: 30.0.1 - jest-resolve: 30.2.0 - jest-runner: 30.2.0 - jest-util: 30.2.0 - jest-validate: 30.2.0 - micromatch: 4.0.8 + jest-resolve: 30.3.0 + jest-runner: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 parse-json: 5.2.0 - pretty-format: 30.2.0 + pretty-format: 30.3.0 slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.13.4 - ts-node: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@30.2.0(@types/node@22.19.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)): - dependencies: - '@babel/core': 7.28.5 - '@jest/get-type': 30.1.0 - '@jest/pattern': 30.0.1 - '@jest/test-sequencer': 30.2.0 - '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.28.5) - chalk: 4.1.2 - ci-info: 4.4.0 - deepmerge: 4.3.1 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-circus: 30.2.0(babel-plugin-macros@3.1.0) - jest-docblock: 30.2.0 - jest-environment-node: 30.2.0 - jest-regex-util: 30.0.1 - jest-resolve: 30.2.0 - jest-runner: 30.2.0 - jest-util: 30.2.0 - jest-validate: 30.2.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 30.2.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.19.1 - ts-node: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3) + '@types/node': 25.5.0 + ts-node: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -18554,47 +18910,54 @@ snapshots: chalk: 4.1.2 pretty-format: 30.2.0 + jest-diff@30.3.0: + dependencies: + '@jest/diff-sequences': 30.3.0 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.3.0 + jest-docblock@30.2.0: dependencies: detect-newline: 3.1.0 - jest-each@30.2.0: + jest-each@30.3.0: dependencies: '@jest/get-type': 30.1.0 - '@jest/types': 30.2.0 + '@jest/types': 30.3.0 chalk: 4.1.2 - jest-util: 30.2.0 - pretty-format: 30.2.0 + jest-util: 30.3.0 + pretty-format: 30.3.0 - jest-environment-node@30.2.0: + jest-environment-node@30.3.0: dependencies: - '@jest/environment': 30.2.0 - '@jest/fake-timers': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.1 - jest-mock: 30.2.0 - jest-util: 30.2.0 - jest-validate: 30.2.0 + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 - jest-haste-map@30.2.0: + jest-haste-map@30.3.0: dependencies: - '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 jest-regex-util: 30.0.1 - jest-util: 30.2.0 - jest-worker: 30.2.0 - micromatch: 4.0.8 + jest-util: 30.3.0 + jest-worker: 30.3.0 + picomatch: 4.0.3 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - jest-leak-detector@30.2.0: + jest-leak-detector@30.3.0: dependencies: '@jest/get-type': 30.1.0 - pretty-format: 30.2.0 + pretty-format: 30.3.0 jest-matcher-utils@30.2.0: dependencies: @@ -18603,6 +18966,13 @@ snapshots: jest-diff: 30.2.0 pretty-format: 30.2.0 + jest-matcher-utils@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.3.0 + pretty-format: 30.3.0 + jest-message-util@30.2.0: dependencies: '@babel/code-frame': 7.27.1 @@ -18615,112 +18985,130 @@ snapshots: slash: 3.0.0 stack-utils: 2.0.6 + jest-message-util@30.3.0: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 30.3.0 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + picomatch: 4.0.3 + pretty-format: 30.3.0 + slash: 3.0.0 + stack-utils: 2.0.6 + jest-mock@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@types/node': 25.5.0 jest-util: 30.2.0 - jest-pnp-resolver@1.2.3(jest-resolve@30.2.0): + jest-mock@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 25.5.0 + jest-util: 30.3.0 + + jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): optionalDependencies: - jest-resolve: 30.2.0 + jest-resolve: 30.3.0 jest-regex-util@30.0.1: {} - jest-resolve-dependencies@30.2.0: + jest-resolve-dependencies@30.3.0: dependencies: jest-regex-util: 30.0.1 - jest-snapshot: 30.2.0 + jest-snapshot: 30.3.0 transitivePeerDependencies: - supports-color - jest-resolve@30.2.0: + jest-resolve@30.3.0: dependencies: chalk: 4.1.2 graceful-fs: 4.2.11 - jest-haste-map: 30.2.0 - jest-pnp-resolver: 1.2.3(jest-resolve@30.2.0) - jest-util: 30.2.0 - jest-validate: 30.2.0 + jest-haste-map: 30.3.0 + jest-pnp-resolver: 1.2.3(jest-resolve@30.3.0) + jest-util: 30.3.0 + jest-validate: 30.3.0 slash: 3.0.0 unrs-resolver: 1.11.1 - jest-runner@30.2.0: + jest-runner@30.3.0: dependencies: - '@jest/console': 30.2.0 - '@jest/environment': 30.2.0 - '@jest/test-result': 30.2.0 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@jest/console': 30.3.0 + '@jest/environment': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-docblock: 30.2.0 - jest-environment-node: 30.2.0 - jest-haste-map: 30.2.0 - jest-leak-detector: 30.2.0 - jest-message-util: 30.2.0 - jest-resolve: 30.2.0 - jest-runtime: 30.2.0 - jest-util: 30.2.0 - jest-watcher: 30.2.0 - jest-worker: 30.2.0 + jest-environment-node: 30.3.0 + jest-haste-map: 30.3.0 + jest-leak-detector: 30.3.0 + jest-message-util: 30.3.0 + jest-resolve: 30.3.0 + jest-runtime: 30.3.0 + jest-util: 30.3.0 + jest-watcher: 30.3.0 + jest-worker: 30.3.0 p-limit: 3.1.0 source-map-support: 0.5.13 transitivePeerDependencies: - supports-color - jest-runtime@30.2.0: + jest-runtime@30.3.0: dependencies: - '@jest/environment': 30.2.0 - '@jest/fake-timers': 30.2.0 - '@jest/globals': 30.2.0 + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/globals': 30.3.0 '@jest/source-map': 30.0.1 - '@jest/test-result': 30.2.0 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 chalk: 4.1.2 cjs-module-lexer: 2.2.0 collect-v8-coverage: 1.0.2 - glob: 10.5.0 + glob: 13.0.6 graceful-fs: 4.2.11 - jest-haste-map: 30.2.0 - jest-message-util: 30.2.0 - jest-mock: 30.2.0 + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 jest-regex-util: 30.0.1 - jest-resolve: 30.2.0 - jest-snapshot: 30.2.0 - jest-util: 30.2.0 + jest-resolve: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - jest-snapshot@30.2.0: + jest-snapshot@30.3.0: dependencies: '@babel/core': 7.28.5 '@babel/generator': 7.28.5 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.5) '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.28.5) '@babel/types': 7.28.5 - '@jest/expect-utils': 30.2.0 + '@jest/expect-utils': 30.3.0 '@jest/get-type': 30.1.0 - '@jest/snapshot-utils': 30.2.0 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 + '@jest/snapshot-utils': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) chalk: 4.1.2 - expect: 30.2.0 + expect: 30.3.0 graceful-fs: 4.2.11 - jest-diff: 30.2.0 - jest-matcher-utils: 30.2.0 - jest-message-util: 30.2.0 - jest-util: 30.2.0 - pretty-format: 30.2.0 - semver: 7.7.3 + jest-diff: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + pretty-format: 30.3.0 + semver: 7.7.4 synckit: 0.11.12 transitivePeerDependencies: - supports-color @@ -18728,52 +19116,61 @@ snapshots: jest-util@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@types/node': 25.5.0 chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 picomatch: 4.0.3 - jest-validate@30.2.0: + jest-util@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 25.5.0 + chalk: 4.1.2 + ci-info: 4.4.0 + graceful-fs: 4.2.11 + picomatch: 4.0.3 + + jest-validate@30.3.0: dependencies: '@jest/get-type': 30.1.0 - '@jest/types': 30.2.0 + '@jest/types': 30.3.0 camelcase: 6.3.0 chalk: 4.1.2 leven: 3.1.0 - pretty-format: 30.2.0 + pretty-format: 30.3.0 - jest-watcher@30.2.0: + jest-watcher@30.3.0: dependencies: - '@jest/test-result': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.1 + '@jest/test-result': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 25.5.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 - jest-util: 30.2.0 + jest-util: 30.3.0 string-length: 4.0.2 jest-worker@27.5.1: dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest-worker@30.2.0: + jest-worker@30.3.0: dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 '@ungap/structured-clone': 1.3.0 - jest-util: 30.2.0 + jest-util: 30.3.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)): + jest@30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)) - '@jest/types': 30.2.0 + '@jest/core': 30.3.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) + '@jest/types': 30.3.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)) + jest-cli: 30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -18783,13 +19180,11 @@ snapshots: jiti@2.4.2: {} - jose@4.15.9: {} - jose@6.1.3: {} - jotai-optics@0.4.0(jotai@2.16.2(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1))(optics-ts@2.4.1): + jotai-optics@0.4.0(jotai@2.18.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1))(optics-ts@2.4.1): dependencies: - jotai: 2.16.2(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1) + jotai: 2.18.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1) optics-ts: 2.4.1 jotai-scope@0.7.2(jotai@2.11.0(@types/react@18.3.12)(react@18.3.1))(react@18.3.1): @@ -18802,7 +19197,7 @@ snapshots: '@types/react': 18.3.12 react: 18.3.1 - jotai@2.16.2(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1): + jotai@2.18.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@18.3.12)(react@18.3.1): optionalDependencies: '@babel/core': 7.28.5 '@babel/template': 7.27.2 @@ -18828,27 +19223,26 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@25.0.1: + jsdom@26.1.0: dependencies: cssstyle: 4.2.1 data-urls: 5.0.0 - decimal.js: 10.4.3 - form-data: 4.0.5 + decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 nwsapi: 2.2.16 - parse5: 7.1.2 - rrweb-cssom: 0.7.1 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 5.1.0 + tough-cookie: 5.1.2 w3c-xmlserializer: 5.0.0 webidl-conversions: 7.0.0 whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 + whatwg-url: 14.2.0 ws: 8.19.0 xml-name-validator: 5.0.0 transitivePeerDependencies: @@ -18890,7 +19284,7 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonrepair@3.13.2: {} + jsonrepair@3.13.3: {} jsonwebtoken@9.0.3: dependencies: @@ -18903,14 +19297,14 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.7.2 + semver: 7.7.4 jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 - object.values: 1.2.0 + object.values: 1.2.1 jszip@3.10.1: dependencies: @@ -18932,7 +19326,7 @@ snapshots: jwt-decode@4.0.0: {} - katex@0.16.27: + katex@0.16.40: dependencies: commander: 8.3.0 @@ -18952,16 +19346,14 @@ snapshots: klona@2.0.6: {} - kolorist@1.8.0: {} - - kysely-codegen@0.20.0(kysely@0.28.2)(pg@8.16.3)(typescript@5.7.3): + kysely-codegen@0.20.0(kysely@0.28.14)(pg@8.16.3)(typescript@5.9.3): dependencies: chalk: 4.1.2 - cosmiconfig: 9.0.0(typescript@5.7.3) + cosmiconfig: 9.0.0(typescript@5.9.3) diff: 8.0.3 dotenv: 17.2.4 dotenv-expand: 12.0.3 - kysely: 0.28.2 + kysely: 0.28.14 micromatch: 4.0.8 minimist: 1.2.8 pluralize: 8.0.0 @@ -18976,13 +19368,13 @@ snapshots: '@commander-js/extra-typings': 11.1.0(commander@11.1.0) commander: 11.1.0 - kysely-postgres-js@3.0.0(kysely@0.28.2)(postgres@3.4.8): + kysely-postgres-js@3.0.0(kysely@0.28.14)(postgres@3.4.8): dependencies: - kysely: 0.28.2 + kysely: 0.28.14 optionalDependencies: postgres: 3.4.8 - kysely@0.28.2: {} + kysely@0.28.14: {} langium@3.3.1: dependencies: @@ -18992,13 +19384,21 @@ snapshots: vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.0.8 + langium@4.2.1: + dependencies: + chevrotain: 11.1.2 + chevrotain-allstar: 0.3.1(chevrotain@11.1.2) + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + langsmith@0.5.7(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.19.0)(zod@4.3.6)): dependencies: '@types/uuid': 10.0.0 chalk: 5.6.2 console-table-printer: 2.14.6 p-queue: 6.6.2 - semver: 7.7.3 + semver: 7.7.4 uuid: 10.0.0 optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -19009,16 +19409,9 @@ snapshots: layout-base@2.0.1: {} - ldapts@7.4.0: + ldapts@8.1.7: dependencies: - '@types/asn1': 0.2.4 - asn1: 0.2.6 - debug: 4.4.0 strict-event-emitter-types: 2.0.0 - uuid: 11.1.0 - whatwg-url: 14.2.0 - transitivePeerDependencies: - - supports-color leac@0.6.0: {} @@ -19060,6 +19453,55 @@ snapshots: process-warning: 4.0.0 set-cookie-parser: 2.6.0 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lines-and-columns@1.2.4: {} lines-and-columns@2.0.3: {} @@ -19074,12 +19516,6 @@ snapshots: loader-runner@4.3.1: {} - local-pkg@1.1.2: - dependencies: - mlly: 1.8.0 - pkg-types: 2.3.0 - quansync: 0.2.11 - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -19092,8 +19528,6 @@ snapshots: lodash.debounce@4.0.8: {} - lodash.deburr@4.1.0: {} - lodash.defaults@4.2.0: {} lodash.includes@4.3.0: {} @@ -19135,7 +19569,7 @@ snapshots: is-unicode-supported: 2.1.0 yoctocolors: 2.1.2 - loglevel@1.9.1: {} + loglevel@1.9.2: {} long@5.3.2: {} @@ -19147,7 +19581,7 @@ snapshots: dependencies: duck: 0.1.12 option: 0.2.4 - underscore: 1.13.7 + underscore: 1.13.8 lowlight@3.3.0: dependencies: @@ -19157,14 +19591,12 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.2.7: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - luxon@3.7.2: {} magic-string@0.30.17: @@ -19179,7 +19611,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 make-error@1.3.6: {} @@ -19187,7 +19619,7 @@ snapshots: dependencies: tmpl: 1.0.5 - mammoth@1.11.0: + mammoth@1.12.0: dependencies: '@xmldom/xmldom': 0.8.10 argparse: 1.0.10 @@ -19197,12 +19629,12 @@ snapshots: jszip: 3.10.1 lop: 0.4.2 path-is-absolute: 1.0.1 - underscore: 1.13.7 + underscore: 1.13.8 xmlbuilder: 10.1.1 - mantine-form-zod-resolver@1.3.0(@mantine/form@8.3.14(react@18.3.1))(zod@4.3.6): + mantine-form-zod-resolver@1.3.0(@mantine/form@8.3.18(react@18.3.1))(zod@4.3.6): dependencies: - '@mantine/form': 8.3.14(react@18.3.1) + '@mantine/form': 8.3.18(react@18.3.1) zod: 4.3.6 markdown-it@14.1.1: @@ -19214,12 +19646,12 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - marked@13.0.3: {} - marked@15.0.12: {} marked@16.4.2: {} + marked@17.0.5: {} + math-intrinsics@1.1.0: {} mdurl@2.0.0: {} @@ -19236,23 +19668,22 @@ snapshots: merge-stream@2.0.0: {} - merge2@1.4.1: {} - - mermaid@11.12.2: + mermaid@11.13.0: dependencies: - '@braintree/sanitize-url': 7.1.1 - '@iconify/utils': 3.0.1 - '@mermaid-js/parser': 0.6.3 + '@braintree/sanitize-url': 7.1.2 + '@iconify/utils': 3.1.0 + '@mermaid-js/parser': 1.0.1 '@types/d3': 7.4.3 - cytoscape: 3.30.2 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.30.2) - cytoscape-fcose: 2.2.0(cytoscape@3.30.2) + '@upsetjs/venn.js': 2.0.0 + cytoscape: 3.33.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1) + cytoscape-fcose: 2.2.0(cytoscape@3.33.1) d3: 7.9.0 d3-sankey: 0.12.3 - dagre-d3-es: 7.0.13 + dagre-d3-es: 7.0.14 dayjs: 1.11.19 dompurify: 3.3.3 - katex: 0.16.27 + katex: 0.16.40 khroma: 2.1.0 lodash-es: 4.17.23 marked: 16.4.2 @@ -19260,8 +19691,6 @@ snapshots: stylis: 4.3.6 ts-dedent: 2.2.0 uuid: 11.1.0 - transitivePeerDependencies: - - supports-color methods@1.1.2: {} @@ -19293,31 +19722,27 @@ snapshots: mimic-function@5.0.1: {} - minimatch@10.2.1: + minimatch@10.2.4: dependencies: brace-expansion: 5.0.4 - minimatch@3.1.2: + minimatch@3.1.5: dependencies: brace-expansion: 1.1.12 - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.2 - - minimatch@9.0.4: + minimatch@5.1.8: dependencies: brace-expansion: 2.0.2 minimist@1.2.8: {} - minipass@7.1.2: {} + minipass@7.1.3: {} mitt@3.0.1: {} mlly@1.8.0: dependencies: - acorn: 8.15.0 + acorn: 8.16.0 pathe: 2.0.3 pkg-types: 1.3.1 ufo: 1.6.1 @@ -19344,7 +19769,7 @@ snapshots: optionalDependencies: msgpackr-extract: 3.0.2 - msgpackr@1.11.8: + msgpackr@1.11.9: optionalDependencies: msgpackr-extract: 3.0.2 @@ -19357,14 +19782,12 @@ snapshots: mute-stream@2.0.0: {} - nanoid@3.3.11: {} - - nanoid@3.3.3: {} - - nanoid@3.3.7: {} + nanoid@3.3.8: {} nanoid@4.0.2: {} + nanoid@5.1.7: {} + napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -19381,23 +19804,23 @@ snapshots: neo-async@2.6.2: {} - nestjs-cls@6.2.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2): + nestjs-cls@6.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2): dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 rxjs: 7.8.2 - nestjs-kysely@1.2.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(kysely@0.28.2)(reflect-metadata@0.2.2): + nestjs-kysely@3.1.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(kysely@0.28.14)(reflect-metadata@0.2.2): dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) - kysely: 0.28.2 + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + kysely: 0.28.14 reflect-metadata: 0.2.2 - nestjs-pino@4.5.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2): + nestjs-pino@4.6.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2): dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) pino: 10.1.0 pino-http: 11.0.0 rxjs: 7.8.2 @@ -19421,13 +19844,12 @@ snapshots: node-int64@0.4.0: {} - node-machine-id@1.1.12: {} - - node-releases@2.0.18: {} + node-readable-to-web-readable-stream@0.4.2: + optional: true node-releases@2.0.27: {} - nodemailer@7.0.12: {} + nodemailer@8.0.3: {} normalize-path@3.0.0: {} @@ -19443,13 +19865,14 @@ snapshots: nwsapi@2.2.16: {} - nx@22.5.3: + nx@22.6.1: dependencies: + '@ltd/j-toml': 1.38.0 '@napi-rs/wasm-runtime': 0.2.4 '@yarnpkg/lockfile': 1.1.0 '@yarnpkg/parsers': 3.0.2 '@zkochan/js-yaml': 0.0.7 - axios: 1.13.5 + axios: 1.13.6 cli-cursor: 3.1.0 cli-spinners: 2.6.1 cliui: 8.0.1 @@ -19464,8 +19887,7 @@ snapshots: jest-diff: 30.2.0 jsonc-parser: 3.2.0 lines-and-columns: 2.0.3 - minimatch: 10.2.1 - node-machine-id: 1.1.12 + minimatch: 10.2.4 npm-run-path: 4.0.1 open: 8.4.2 ora: 5.3.0 @@ -19482,16 +19904,16 @@ snapshots: yargs: 17.7.2 yargs-parser: 21.1.1 optionalDependencies: - '@nx/nx-darwin-arm64': 22.5.3 - '@nx/nx-darwin-x64': 22.5.3 - '@nx/nx-freebsd-x64': 22.5.3 - '@nx/nx-linux-arm-gnueabihf': 22.5.3 - '@nx/nx-linux-arm64-gnu': 22.5.3 - '@nx/nx-linux-arm64-musl': 22.5.3 - '@nx/nx-linux-x64-gnu': 22.5.3 - '@nx/nx-linux-x64-musl': 22.5.3 - '@nx/nx-win32-arm64-msvc': 22.5.3 - '@nx/nx-win32-x64-msvc': 22.5.3 + '@nx/nx-darwin-arm64': 22.6.1 + '@nx/nx-darwin-x64': 22.6.1 + '@nx/nx-freebsd-x64': 22.6.1 + '@nx/nx-linux-arm-gnueabihf': 22.6.1 + '@nx/nx-linux-arm64-gnu': 22.6.1 + '@nx/nx-linux-arm64-musl': 22.6.1 + '@nx/nx-linux-x64-gnu': 22.6.1 + '@nx/nx-linux-x64-musl': 22.6.1 + '@nx/nx-win32-arm64-msvc': 22.6.1 + '@nx/nx-win32-x64-msvc': 22.6.1 transitivePeerDependencies: - debug @@ -19503,45 +19925,56 @@ snapshots: pkg-types: 2.3.0 tinyexec: 1.0.1 + oauth4webapi@3.8.5: {} + oauth@0.10.0: {} object-assign@4.1.1: {} - object-hash@2.2.0: {} - object-hash@3.0.0: {} object-inspect@1.13.3: {} + object-inspect@1.13.4: {} + object-keys@1.1.1: {} object.assign@4.1.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - has-symbols: 1.0.3 + has-symbols: 1.1.0 object-keys: 1.1.1 - object.entries@1.1.8: + object.assign@4.1.7: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.5 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 - object.values@1.2.0: + object.values@1.2.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - oidc-token-hash@5.0.3: {} + es-object-atoms: 1.1.1 ollama@0.6.3: dependencies: @@ -19577,12 +20010,10 @@ snapshots: zod: 4.3.6 optional: true - openid-client@5.7.1: + openid-client@6.8.2: dependencies: - jose: 4.15.9 - lru-cache: 6.0.0 - object-hash: 2.2.0 - oidc-token-hash: 5.0.3 + jose: 6.1.3 + oauth4webapi: 3.8.5 optics-ts@2.4.1: {} @@ -19634,9 +20065,15 @@ snapshots: orderedmap@2.1.1: {} - otpauth@9.4.1: + otpauth@9.5.0: dependencies: - '@noble/hashes': 1.8.0 + '@noble/hashes': 2.0.1 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 p-finally@1.0.0: {} @@ -19648,7 +20085,7 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@6.2.0: + p-limit@7.3.0: dependencies: yocto-queue: 1.2.1 @@ -19671,8 +20108,6 @@ snapshots: p-try@2.2.0: {} - package-json-from-dist@1.0.0: {} - package-manager-detector@1.3.0: {} pako@1.0.11: {} @@ -19702,10 +20137,6 @@ snapshots: dependencies: parse5: 7.3.0 - parse5@7.1.2: - dependencies: - entities: 4.5.0 - parse5@7.3.0: dependencies: entities: 6.0.1 @@ -19746,16 +20177,18 @@ snapshots: path-exists@4.0.0: {} + path-expression-matcher@1.2.0: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} path-parse@1.0.7: {} - path-scurry@1.11.1: + path-scurry@2.0.2: dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 + lru-cache: 11.2.7 + minipass: 7.1.3 path-to-regexp@8.3.0: {} @@ -19765,9 +20198,10 @@ snapshots: pause@0.0.1: {} - pdfjs-dist@5.4.394: + pdfjs-dist@5.5.207: optionalDependencies: - '@napi-rs/canvas': 0.1.82 + '@napi-rs/canvas': 0.1.97 + node-readable-to-web-readable-stream: 0.4.2 peberminta@0.9.0: {} @@ -19933,48 +20367,42 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-js@4.0.1(postcss@8.4.49): + postcss-js@4.0.1(postcss@8.5.8): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.49 + postcss: 8.5.8 - postcss-mixins@9.0.4(postcss@8.4.49): + postcss-mixins@12.1.2(postcss@8.5.8): dependencies: - fast-glob: 3.3.2 - postcss: 8.4.49 - postcss-js: 4.0.1(postcss@8.4.49) - postcss-simple-vars: 7.0.1(postcss@8.4.49) - sugarss: 4.0.1(postcss@8.4.49) + postcss: 8.5.8 + postcss-js: 4.0.1(postcss@8.5.8) + postcss-simple-vars: 7.0.1(postcss@8.5.8) + sugarss: 5.0.1(postcss@8.5.8) + tinyglobby: 0.2.15 - postcss-nested@6.0.1(postcss@8.4.49): + postcss-nested@7.0.2(postcss@8.5.8): dependencies: - postcss: 8.4.49 - postcss-selector-parser: 6.0.15 + postcss: 8.5.8 + postcss-selector-parser: 7.1.1 - postcss-preset-mantine@1.17.0(postcss@8.4.49): + postcss-preset-mantine@1.18.0(postcss@8.5.8): dependencies: - postcss: 8.4.49 - postcss-mixins: 9.0.4(postcss@8.4.49) - postcss-nested: 6.0.1(postcss@8.4.49) + postcss: 8.5.8 + postcss-mixins: 12.1.2(postcss@8.5.8) + postcss-nested: 7.0.2(postcss@8.5.8) - postcss-selector-parser@6.0.15: + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-simple-vars@7.0.1(postcss@8.4.49): + postcss-simple-vars@7.0.1(postcss@8.5.8): dependencies: - postcss: 8.4.49 + postcss: 8.5.8 - postcss@8.4.49: + postcss@8.5.8: dependencies: - nanoid: 3.3.7 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 + nanoid: 3.3.8 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -19994,15 +20422,15 @@ snapshots: postgres@3.4.8: {} - posthog-js@1.345.5: + posthog-js@1.363.1: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) - '@posthog/core': 1.22.0 - '@posthog/types': 1.345.5 + '@posthog/core': 1.24.1 + '@posthog/types': 1.363.1 core-js: 3.43.0 dompurify: 3.3.3 fflate: 0.4.8 @@ -20010,9 +20438,9 @@ snapshots: query-selector-shadow-dom: 1.0.1 web-vitals: 5.1.0 - postmark@4.0.5: + postmark@4.0.7: dependencies: - axios: 1.13.5 + axios: 1.13.6 transitivePeerDependencies: - debug @@ -20020,10 +20448,6 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.4.1: {} - - prettier@3.5.1: {} - prettier@3.8.1: {} pretty-format@30.2.0: @@ -20032,6 +20456,12 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-format@30.3.0: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + prismjs@1.30.0: {} process-nextick-args@2.0.1: {} @@ -20051,7 +20481,7 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - prosemirror-changeset@2.3.1: + prosemirror-changeset@2.4.0: dependencies: prosemirror-transform: 1.10.4 @@ -20166,7 +20596,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.19.1 + '@types/node': 25.5.0 long: 5.3.2 proxy-addr@2.0.7: @@ -20202,12 +20632,8 @@ snapshots: dependencies: side-channel: 1.1.0 - quansync@0.2.11: {} - query-selector-shadow-dom@1.0.1: {} - queue-microtask@1.2.3: {} - quick-format-unescaped@4.0.4: {} radix-ui@1.4.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -20273,10 +20699,6 @@ snapshots: '@types/react': 18.3.12 '@types/react-dom': 18.3.1 - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - range-parser@1.2.1: {} raw-body@3.0.2: @@ -20300,7 +20722,7 @@ snapshots: - '@types/node' - '@types/react' - react-clear-modal@2.0.17(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-clear-modal@2.0.18(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -20333,16 +20755,16 @@ snapshots: dependencies: react: 18.3.1 - react-email@5.2.8: + react-email@5.2.10: dependencies: - '@babel/parser': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/parser': 7.27.0 + '@babel/traverse': 7.27.0 chokidar: 4.0.3 commander: 13.1.0 conf: 15.1.0 debounce: 2.0.0 - esbuild: 0.25.0 - glob: 10.5.0 + esbuild: 0.27.3 + glob: 13.0.6 jiti: 2.4.2 log-symbols: 7.0.1 mime-types: 3.0.2 @@ -20357,28 +20779,29 @@ snapshots: - supports-color - utf-8-validate - react-error-boundary@4.1.2(react@18.3.1): + react-error-boundary@6.1.1(react@18.3.1): dependencies: - '@babel/runtime': 7.26.10 react: 18.3.1 react-fast-compare@3.2.2: {} - react-helmet-async@2.0.5(react@18.3.1): + react-helmet-async@3.0.0(react@18.3.1): dependencies: invariant: 2.2.4 react: 18.3.1 react-fast-compare: 3.2.2 shallowequal: 1.1.0 - react-i18next@15.0.1(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-i18next@16.5.8(i18next@25.10.1(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3): dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 html-parse-stringify: 3.0.1 - i18next: 23.16.8 + i18next: 25.10.1(typescript@5.9.3) react: 18.3.1 + use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: react-dom: 18.3.1(react@18.3.1) + typescript: 5.9.3 react-is@16.13.1: {} @@ -20389,8 +20812,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-refresh@0.18.0: {} - react-remove-scroll-bar@2.3.8(@types/react@18.3.12)(react@18.3.1): dependencies: react: 18.3.1 @@ -20410,13 +20831,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 - react-router-dom@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router-dom@7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-router: 7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: 7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-router@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router@7.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: cookie: 1.1.1 react: 18.3.1 @@ -20434,7 +20855,7 @@ snapshots: react-textarea-autosize@8.5.9(@types/react@18.3.12)(react@18.3.1): dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 react: 18.3.1 use-composed-ref: 1.3.0(react@18.3.1) use-latest: 1.2.1(@types/react@18.3.12)(react@18.3.1) @@ -20443,7 +20864,7 @@ snapshots: react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -20493,12 +20914,23 @@ snapshots: redux@4.2.1: dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 redux@5.0.1: {} reflect-metadata@0.2.2: {} + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + reflect.getprototypeof@1.0.7: dependencies: call-bind: 1.0.7 @@ -20519,15 +20951,24 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 regexp.prototype.flags@1.5.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + regexpu-core@5.3.2: dependencies: '@babel/regjsgen': 0.8.0 @@ -20541,8 +20982,6 @@ snapshots: dependencies: jsesc: 0.5.0 - repeat-string@1.6.1: {} - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -20589,39 +21028,32 @@ snapshots: reusify@1.1.0: {} - rfc6902@5.1.2: {} + rfc6902@5.2.0: {} rfdc@1.3.1: {} robust-predicates@3.0.2: {} - rollup@4.53.3: + rolldown@1.0.0-rc.10: dependencies: - '@types/estree': 1.0.8 + '@oxc-project/types': 0.120.0 + '@rolldown/pluginutils': 1.0.0-rc.10 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.3 - '@rollup/rollup-android-arm64': 4.53.3 - '@rollup/rollup-darwin-arm64': 4.53.3 - '@rollup/rollup-darwin-x64': 4.53.3 - '@rollup/rollup-freebsd-arm64': 4.53.3 - '@rollup/rollup-freebsd-x64': 4.53.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 - '@rollup/rollup-linux-arm-musleabihf': 4.53.3 - '@rollup/rollup-linux-arm64-gnu': 4.53.3 - '@rollup/rollup-linux-arm64-musl': 4.53.3 - '@rollup/rollup-linux-loong64-gnu': 4.53.3 - '@rollup/rollup-linux-ppc64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-musl': 4.53.3 - '@rollup/rollup-linux-s390x-gnu': 4.53.3 - '@rollup/rollup-linux-x64-gnu': 4.53.3 - '@rollup/rollup-linux-x64-musl': 4.53.3 - '@rollup/rollup-openharmony-arm64': 4.53.3 - '@rollup/rollup-win32-arm64-msvc': 4.53.3 - '@rollup/rollup-win32-ia32-msvc': 4.53.3 - '@rollup/rollup-win32-x64-gnu': 4.53.3 - '@rollup/rollup-win32-x64-msvc': 4.53.3 - fsevents: 2.3.3 + '@rolldown/binding-android-arm64': 1.0.0-rc.10 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.10 + '@rolldown/binding-darwin-x64': 1.0.0-rc.10 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.10 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.10 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.10 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.10 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.10 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.10 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.10 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.10 rope-sequence@1.3.4: {} @@ -20649,14 +21081,8 @@ snapshots: transitivePeerDependencies: - supports-color - rrweb-cssom@0.7.1: {} - rrweb-cssom@0.8.0: {} - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - rw@1.3.3: {} rxjs@7.8.1: @@ -20671,19 +21097,38 @@ snapshots: dependencies: call-bind: 1.0.7 get-intrinsic: 1.3.0 - has-symbols: 1.0.3 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 isarray: 2.0.5 safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.2.0 + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + safe-regex2@5.0.0: dependencies: ret: 0.5.0 @@ -20699,7 +21144,7 @@ snapshots: sass@1.51.0: dependencies: chokidar: 3.6.0 - immutable: 4.3.7 + immutable: 4.3.8 source-map-js: 1.2.1 sax@1.4.1: {} @@ -20718,15 +21163,15 @@ snapshots: schema-utils@3.3.0: dependencies: '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) - ajv-keywords: 5.1.0(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 2.1.1(ajv@8.18.0) + ajv-keywords: 5.1.0(ajv@8.18.0) secure-json-parse@4.0.0: {} @@ -20739,10 +21184,6 @@ snapshots: semver@6.3.1: {} - semver@7.7.2: {} - - semver@7.7.3: {} - semver@7.7.4: {} send@1.2.1: @@ -20761,9 +21202,7 @@ snapshots: transitivePeerDependencies: - supports-color - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 + serialize-javascript@7.0.3: {} serve-static@2.2.1: dependencies: @@ -20794,6 +21233,12 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + setimmediate@1.0.5: {} setprototypeof@1.2.0: {} @@ -20806,7 +21251,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.1: {} + shell-quote@1.8.3: {} side-channel-list@1.0.0: dependencies: @@ -20828,13 +21273,6 @@ snapshots: object-inspect: 1.13.3 side-channel-map: 1.0.1 - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.3 - side-channel@1.1.0: dependencies: es-errors: 1.3.0 @@ -20869,16 +21307,16 @@ snapshots: '@socket.io/component-emitter': 3.1.0 debug: 4.4.3 engine.io-client: 6.6.2 - socket.io-parser: 4.2.4 + socket.io-parser: 4.2.6 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-parser@4.2.4: + socket.io-parser@4.2.6: dependencies: '@socket.io/component-emitter': 3.1.0 - debug: 4.3.7 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -20890,7 +21328,7 @@ snapshots: debug: 4.4.1 engine.io: 6.6.2 socket.io-adapter: 2.5.4 - socket.io-parser: 4.2.4 + socket.io-parser: 4.2.6 transitivePeerDependencies: - bufferutil - supports-color @@ -20937,6 +21375,11 @@ snapshots: stdin-discarder@0.2.2: {} + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + stream-browserify@3.0.0: dependencies: inherits: 2.0.4 @@ -20955,38 +21398,43 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - string-width@7.2.0: dependencies: emoji-regex: 10.6.0 get-east-asian-width: 1.4.0 strip-ansi: 7.1.0 - string.prototype.matchall@4.0.11: + string.prototype.matchall@4.0.12: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.23.5 + es-abstract: 1.24.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 gopd: 1.2.0 - has-symbols: 1.0.3 - internal-slot: 1.0.7 + has-symbols: 1.1.0 + internal-slot: 1.1.0 regexp.prototype.flags: 1.5.3 set-function-name: 2.0.2 - side-channel: 1.0.6 + side-channel: 1.1.0 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 es-abstract: 1.23.5 + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 @@ -21000,6 +21448,13 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 @@ -21032,12 +21487,12 @@ snapshots: strip-json-comments@5.0.3: {} - stripe@17.5.0: + stripe@17.7.0: dependencies: - '@types/node': 22.19.1 + '@types/node': 25.5.0 qs: 6.14.2 - strnum@2.1.2: {} + strnum@2.2.1: {} strtok3@10.3.4: dependencies: @@ -21051,9 +21506,9 @@ snapshots: stylis@4.3.6: {} - sugarss@4.0.1(postcss@8.4.49): + sugarss@5.0.1(postcss@8.5.8): dependencies: - postcss: 8.4.49 + postcss: 8.5.8 superagent@10.3.0: dependencies: @@ -21118,7 +21573,7 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - serialize-javascript: 6.0.2 + serialize-javascript: 7.0.3 terser: 5.39.0 webpack: 5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)) optionalDependencies: @@ -21134,8 +21589,8 @@ snapshots: test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 - glob: 10.5.0 - minimatch: 3.1.2 + glob: 13.0.6 + minimatch: 3.1.5 thread-stream@3.0.2: dependencies: @@ -21182,16 +21637,12 @@ snapshots: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 - tough-cookie@5.1.0: + tough-cookie@5.1.2: dependencies: tldts: 6.1.72 tr46@0.0.3: {} - tr46@5.0.0: - dependencies: - punycode: 2.3.1 - tr46@5.1.1: dependencies: punycode: 2.3.1 @@ -21202,61 +21653,57 @@ snapshots: dependencies: utf8-byte-length: 1.0.4 - ts-api-utils@1.3.0(typescript@5.7.2): + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: - typescript: 5.7.2 - - ts-api-utils@2.0.1(typescript@5.7.3): - dependencies: - typescript: 5.7.3 + typescript: 5.9.3 ts-dedent@2.2.0: {} - ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)))(typescript@5.7.3): + ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 - jest: 30.2.0(@types/node@22.13.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3)) + jest: 30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.7.3 + semver: 7.7.4 type-fest: 4.41.0 - typescript: 5.7.3 + typescript: 5.9.3 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.28.5 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.28.5) - jest-util: 30.2.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.28.5) + jest-util: 30.3.0 - ts-loader@9.5.4(typescript@5.7.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))): + ts-loader@9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))): dependencies: chalk: 4.1.2 enhanced-resolve: 5.19.0 micromatch: 4.0.8 - semver: 7.7.3 + semver: 7.7.4 source-map: 0.7.4 - typescript: 5.7.3 + typescript: 5.9.3 webpack: 5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)) - ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.13.4)(typescript@5.7.3): + ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.13.4 + '@types/node': 25.5.0 acorn: 8.15.0 acorn-walk: 8.3.2 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.4 make-error: 1.3.6 - typescript: 5.7.3 + typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -21279,9 +21726,9 @@ snapshots: tslib@2.8.1: {} - tsx@4.19.3: + tsx@4.21.0: dependencies: - esbuild: 0.25.0 + esbuild: 0.27.4 get-tsconfig: 4.7.5 optionalDependencies: fsevents: 2.3.3 @@ -21322,6 +21769,12 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.13 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 @@ -21330,6 +21783,14 @@ snapshots: has-proto: 1.0.3 is-typed-array: 1.1.13 + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + typed-array-byte-offset@1.0.3: dependencies: available-typed-arrays: 1.0.7 @@ -21340,6 +21801,16 @@ snapshots: is-typed-array: 1.1.13 reflect.getprototypeof: 1.0.7 + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + typed-array-length@1.0.7: dependencies: call-bind: 1.0.7 @@ -21349,38 +21820,24 @@ snapshots: possible-typed-array-names: 1.0.0 reflect.getprototypeof: 1.0.7 - typescript-eslint@8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2): + typescript-eslint@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.17.0(@typescript-eslint/parser@8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2))(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) - '@typescript-eslint/parser': 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) - '@typescript-eslint/utils': 8.17.0(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.2) - eslint: 9.39.2(jiti@2.4.2) - optionalDependencies: - typescript: 5.7.2 + '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3))(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) + '@typescript-eslint/parser': 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.4.2) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - typescript-eslint@8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.24.1(@typescript-eslint/parser@8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3))(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) - '@typescript-eslint/parser': 8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) - '@typescript-eslint/utils': 8.24.1(eslint@9.39.2(jiti@2.4.2))(typescript@5.7.3) - eslint: 9.39.2(jiti@2.4.2) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - typescript@5.7.2: {} - - typescript@5.7.3: {} - typescript@5.9.3: {} - typesense@2.1.0(@babel/runtime@7.26.10): + typesense@3.0.3(@babel/runtime@7.29.2): dependencies: - '@babel/runtime': 7.26.10 - axios: 1.13.5 - loglevel: 1.9.1 + '@babel/runtime': 7.29.2 + axios: 1.13.6 + loglevel: 1.9.2 tslib: 2.8.1 transitivePeerDependencies: - debug @@ -21409,13 +21866,20 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.0 - underscore@1.13.7: {} + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.0.2 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 - undici-types@6.20.0: {} + underscore@1.13.8: {} undici-types@6.21.0: {} - undici@7.22.0: {} + undici-types@7.18.2: {} + + undici@7.24.0: {} unicode-canonical-property-names-ecmascript@2.0.0: {} @@ -21456,12 +21920,6 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.1(browserslist@4.24.2): - dependencies: - browserslist: 4.24.2 - escalade: 3.2.0 - picocolors: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -21522,6 +21980,8 @@ snapshots: uuid@11.1.0: {} + uuid@13.0.0: {} + v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.2.0: @@ -21534,21 +21994,22 @@ snapshots: vary@1.1.2: {} - vite@7.2.4(@types/node@22.19.1)(jiti@2.4.2)(less@4.2.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0): + vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0): dependencies: - esbuild: 0.25.0 - fdir: 6.5.0(picomatch@4.0.3) + lightningcss: 1.32.0 picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.3 + postcss: 8.5.8 + rolldown: 1.0.0-rc.10 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.1 + esbuild: 0.27.4 fsevents: 2.3.3 jiti: 2.4.2 less: 4.2.0 + sugarss: 5.0.1(postcss@8.5.8) terser: 5.39.0 - tsx: 4.19.3 + tsx: 4.21.0 yaml: 2.7.0 void-elements@3.1.0: {} @@ -21570,6 +22031,8 @@ snapshots: vscode-uri@3.0.8: {} + vscode-uri@3.1.0: {} + w3c-keyname@2.2.8: {} w3c-xmlserializer@5.0.0: @@ -21643,11 +22106,6 @@ snapshots: whatwg-mimetype@4.0.0: {} - whatwg-url@14.0.0: - dependencies: - tr46: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-url@14.2.0: dependencies: tr46: 5.1.1 @@ -21668,6 +22126,14 @@ snapshots: is-string: 1.1.0 is-symbol: 1.1.0 + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + which-builtin-type@1.2.0: dependencies: call-bind: 1.0.7 @@ -21684,6 +22150,22 @@ snapshots: which-collection: 1.0.2 which-typed-array: 1.1.16 + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.0.0 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.0 + is-generator-function: 1.0.10 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.0 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + which-collection@1.0.2: dependencies: is-map: 2.0.3 @@ -21701,6 +22183,16 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -21723,12 +22215,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - wrappy@1.0.2: {} write-file-atomic@5.0.1: @@ -21776,24 +22262,24 @@ snapshots: xtend@4.0.2: optional: true - y-indexeddb@9.0.12(yjs@13.6.29): + y-indexeddb@9.0.12(yjs@13.6.30): dependencies: lib0: 0.2.117 - yjs: 13.6.29 + yjs: 13.6.30 - y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.29))(yjs@13.6.29): + y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30): dependencies: lib0: 0.2.117 prosemirror-model: 1.25.1 prosemirror-state: 1.4.3 prosemirror-view: 1.40.0 - y-protocols: 1.0.6(yjs@13.6.29) - yjs: 13.6.29 + y-protocols: 1.0.6(yjs@13.6.30) + yjs: 13.6.30 - y-protocols@1.0.6(yjs@13.6.29): + y-protocols@1.0.6(yjs@13.6.30): dependencies: lib0: 0.2.117 - yjs: 13.6.29 + yjs: 13.6.30 y18n@4.0.3: {} @@ -21801,8 +22287,6 @@ snapshots: yallist@3.1.1: {} - yallist@4.0.0: {} - yaml@1.10.2: {} yaml@2.7.0: {} @@ -21831,19 +22315,19 @@ snapshots: yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.1 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - yauzl@3.2.0: + yauzl@3.2.1: dependencies: buffer-crc32: 0.2.13 pend: 1.2.0 - yjs@13.6.29: + yjs@13.6.30: dependencies: lib0: 0.2.117 @@ -21863,6 +22347,10 @@ snapshots: dependencies: zod: 4.3.6 + zod-validation-error@4.0.2(zod@4.3.6): + dependencies: + zod: 4.3.6 + zod@4.3.6: {} zustand@4.5.6(@types/react@18.3.12)(react@18.3.1): From 9125996e97e7792e20b5295b3663deb9696c566d Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:08:36 +0000 Subject: [PATCH 07/44] sync --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index c2755be3..73b69538 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit c2755be37c61e21f55c3974339388b6d18725ca2 +Subproject commit 73b6953888a626ef8a6c02cdc372d52b4ea031c6 From 0e4a1e7419812dddada6a01452cfa4e9d55fd6a8 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 00:41:38 +0000 Subject: [PATCH 08/44] enum validation --- .../src/core/workspace/dto/update-workspace-user-role.dto.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/server/src/core/workspace/dto/update-workspace-user-role.dto.ts b/apps/server/src/core/workspace/dto/update-workspace-user-role.dto.ts index 2dc52a3b..4df9222e 100644 --- a/apps/server/src/core/workspace/dto/update-workspace-user-role.dto.ts +++ b/apps/server/src/core/workspace/dto/update-workspace-user-role.dto.ts @@ -1,4 +1,5 @@ -import { IsNotEmpty, IsString, IsUUID } from 'class-validator'; +import { IsEnum, IsNotEmpty, IsUUID } from 'class-validator'; +import { UserRole } from '../../../common/helpers/types/permission'; export class UpdateWorkspaceUserRoleDto { @IsNotEmpty() @@ -6,6 +7,6 @@ export class UpdateWorkspaceUserRoleDto { userId: string; @IsNotEmpty() - @IsString() + @IsEnum(UserRole) role: string; } From 53608eae35b533bd7675e6d4ba91f27bc442a3b1 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:59:17 +0000 Subject: [PATCH 09/44] clean up ws --- apps/server/src/ws/ws.gateway.ts | 5 ++--- apps/server/src/ws/ws.service.ts | 9 +++++++++ apps/server/src/ws/ws.utils.ts | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/server/src/ws/ws.gateway.ts b/apps/server/src/ws/ws.gateway.ts index 231dfade..fd6810c8 100644 --- a/apps/server/src/ws/ws.gateway.ts +++ b/apps/server/src/ws/ws.gateway.ts @@ -65,12 +65,10 @@ export class WsGateway async handleMessage(client: Socket, data: any): Promise<void> { if (this.wsService.isTreeEvent(data)) { await this.wsService.handleTreeEvent(client, data); - return; } - - client.broadcast.emit('message', data); } + /* @SubscribeMessage('join-room') handleJoinRoom(client: Socket, @MessageBody() roomName: string): void { // if room is a space, check if user has permissions @@ -81,6 +79,7 @@ export class WsGateway handleLeaveRoom(client: Socket, @MessageBody() roomName: string): void { client.leave(roomName); } + */ onModuleDestroy() { if (this.server) { diff --git a/apps/server/src/ws/ws.service.ts b/apps/server/src/ws/ws.service.ts index 3cdccf6b..23d41909 100644 --- a/apps/server/src/ws/ws.service.ts +++ b/apps/server/src/ws/ws.service.ts @@ -27,6 +27,15 @@ export class WsService { async handleTreeEvent(client: Socket, data: any): Promise<void> { const room = getSpaceRoomName(data.spaceId); + if (!client.rooms.has(room)) { + return; + } + + if (data.operation === 'refetchRootTreeNodeEvent') { + client.broadcast.to(room).emit('message', data); + return; + } + const hasRestrictions = await this.spaceHasRestrictions(data.spaceId); if (!hasRestrictions) { client.broadcast.to(room).emit('message', data); diff --git a/apps/server/src/ws/ws.utils.ts b/apps/server/src/ws/ws.utils.ts index 0cf460f1..c0d48b54 100644 --- a/apps/server/src/ws/ws.utils.ts +++ b/apps/server/src/ws/ws.utils.ts @@ -14,4 +14,5 @@ export const TREE_EVENTS = new Set([ 'addTreeNode', 'moveTreeNode', 'deleteTreeNode', + 'refetchRootTreeNodeEvent', ]); From 2f92278a9d0eb7c94ad6e6884220e5d07e3cef27 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:35:05 +0000 Subject: [PATCH 10/44] sync --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index 73b69538..e7a6b77b 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 73b6953888a626ef8a6c02cdc372d52b4ea031c6 +Subproject commit e7a6b77b7b46ca88ab91b83d1f0267b5eb3538c1 From 7b0d8fe140269163a03142faf61047f458c8d39d Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:46:40 +0000 Subject: [PATCH 11/44] override --- package.json | 4 +++- pnpm-lock.yaml | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3bc5e0c1..32418295 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,9 @@ "express-rate-limit": "8.2.2", "minimatch@^3": "3.1.5", "minimatch@^5": "5.1.8", - "flatted": "3.4.2" + "flatted": "3.4.2", + "picomatch@<2.3.2": "2.3.2", + "fastify": "5.8.3" }, "neverBuiltDependencies": [] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b979f3c6..d3495aae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,6 +27,8 @@ overrides: minimatch@^3: 3.1.5 minimatch@^5: 5.1.8 flatted: 3.4.2 + picomatch@<2.3.2: 2.3.2 + fastify: 5.8.3 patchedDependencies: '@tiptap/core': @@ -7003,8 +7005,8 @@ packages: fastify-plugin@5.1.0: resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==} - fastify@5.8.2: - resolution: {integrity: sha512-lZmt3navvZG915IE+f7/TIVamxIwmBd+OMB+O9WBzcpIwOo6F0LTh0sluoMFk5VkrKTvvrwIaoJPkir4Z+jtAg==} + fastify@5.8.3: + resolution: {integrity: sha512-XJXpRQ41+rsJ/GLeP9vyDC+fBXilcTlEXokMSexkdEkla4uf7ZQNaI5xl3el+kW5TZQulqYxLr659ey/KX7XmQ==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -8975,8 +8977,8 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} picomatch@4.0.2: @@ -13574,7 +13576,7 @@ snapshots: '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) fast-querystring: 1.1.2 - fastify: 5.8.2 + fastify: 5.8.3 fastify-plugin: 5.1.0 find-my-way: 9.5.0 light-my-request: 6.6.0 @@ -16359,7 +16361,7 @@ snapshots: anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.1 + picomatch: 2.3.2 arg@4.1.3: {} @@ -18057,7 +18059,7 @@ snapshots: fastify-plugin@5.1.0: {} - fastify@5.8.2: + fastify@5.8.3: dependencies: '@fastify/ajv-compiler': 4.0.5 '@fastify/error': 4.0.0 @@ -19697,7 +19699,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 mime-db@1.52.0: {} @@ -20265,7 +20267,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} picomatch@4.0.2: {} @@ -20900,7 +20902,7 @@ snapshots: readdirp@3.6.0: dependencies: - picomatch: 2.3.1 + picomatch: 2.3.2 readdirp@4.0.2: {} From 4e8f533b9170931a8b469c54813cc09482fafef8 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:48:33 +0000 Subject: [PATCH 12/44] override --- package.json | 5 +++- pnpm-lock.yaml | 75 ++++++++++++++++++++++++-------------------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 32418295..63994abd 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,10 @@ "minimatch@^5": "5.1.8", "flatted": "3.4.2", "picomatch@<2.3.2": "2.3.2", - "fastify": "5.8.3" + "picomatch@>=4.0.0 <4.0.4": "4.0.4", + "fastify": "5.8.3", + "yaml@>=1.0.0 <1.10.3": "1.10.3", + "yaml@>=2.0.0 <2.8.3": "2.8.3" }, "neverBuiltDependencies": [] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3495aae..d7601191 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,7 +28,10 @@ overrides: minimatch@^5: 5.1.8 flatted: 3.4.2 picomatch@<2.3.2: 2.3.2 + picomatch@>=4.0.0 <4.0.4: 4.0.4 fastify: 5.8.3 + yaml@>=1.0.0 <1.10.3: 1.10.3 + yaml@>=2.0.0 <2.8.3: 2.8.3 patchedDependencies: '@tiptap/core': @@ -403,7 +406,7 @@ importers: version: 18.3.1 '@vitejs/plugin-react': specifier: ^6.0.0 - version: 6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0)) + version: 6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) eslint: specifier: ^9.28.0 version: 9.39.4(jiti@2.4.2) @@ -442,7 +445,7 @@ importers: version: 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) vite: specifier: ^8.0.1 - version: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0) + version: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) apps/server: dependencies: @@ -7018,7 +7021,7 @@ packages: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: - picomatch: ^3 || ^4 + picomatch: 4.0.4 peerDependenciesMeta: picomatch: optional: true @@ -8981,12 +8984,8 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pify@4.0.1: @@ -10455,7 +10454,7 @@ packages: sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 - yaml: ^2.4.2 + yaml: 2.8.3 peerDependenciesMeta: '@types/node': optional: true @@ -10735,13 +10734,13 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} engines: {node: '>= 6'} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} hasBin: true yargs-parser@18.1.3: @@ -10868,7 +10867,7 @@ snapshots: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) jsonc-parser: 3.3.1 - picomatch: 4.0.2 + picomatch: 4.0.4 rxjs: 7.8.1 source-map: 0.7.4 optionalDependencies: @@ -10879,7 +10878,7 @@ snapshots: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) jsonc-parser: 3.3.1 - picomatch: 4.0.2 + picomatch: 4.0.4 rxjs: 7.8.1 source-map: 0.7.4 optionalDependencies: @@ -13712,7 +13711,7 @@ snapshots: jsonc-parser: 3.2.0 npm-run-path: 4.0.1 picocolors: 1.1.1 - picomatch: 4.0.2 + picomatch: 4.0.4 semver: 7.7.4 source-map-support: 0.5.19 tinyglobby: 0.2.15 @@ -13762,7 +13761,7 @@ snapshots: chalk: 4.1.2 enquirer: 2.3.6 nx: 22.6.1 - picomatch: 4.0.2 + picomatch: 4.0.4 semver: 7.7.4 tslib: 2.8.1 yargs-parser: 21.1.1 @@ -16149,10 +16148,10 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vitejs/plugin-react@6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0))': + '@vitejs/plugin-react@6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0) + vite: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) '@webassemblyjs/ast@1.14.1': dependencies: @@ -17009,7 +17008,7 @@ snapshots: import-fresh: 3.3.0 parse-json: 5.2.0 path-type: 4.0.0 - yaml: 1.10.2 + yaml: 1.10.3 cosmiconfig@8.3.6(typescript@5.9.3): dependencies: @@ -18085,9 +18084,9 @@ snapshots: dependencies: bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 fflate@0.4.8: {} @@ -18951,7 +18950,7 @@ snapshots: jest-regex-util: 30.0.1 jest-util: 30.3.0 jest-worker: 30.3.0 - picomatch: 4.0.3 + picomatch: 4.0.4 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -18994,7 +18993,7 @@ snapshots: '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 - picomatch: 4.0.3 + picomatch: 4.0.4 pretty-format: 30.3.0 slash: 3.0.0 stack-utils: 2.0.6 @@ -19122,7 +19121,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 - picomatch: 4.0.3 + picomatch: 4.0.4 jest-util@30.3.0: dependencies: @@ -19131,7 +19130,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 - picomatch: 4.0.3 + picomatch: 4.0.4 jest-validate@30.3.0: dependencies: @@ -19902,7 +19901,7 @@ snapshots: tree-kill: 1.2.2 tsconfig-paths: 4.2.0 tslib: 2.8.1 - yaml: 2.7.0 + yaml: 2.8.3 yargs: 17.7.2 yargs-parser: 21.1.1 optionalDependencies: @@ -20269,9 +20268,7 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.2: {} - - picomatch@4.0.3: {} + picomatch@4.0.4: {} pify@4.0.1: optional: true @@ -21604,8 +21601,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tiptap-extension-global-drag-handle@0.1.18: {} @@ -21996,10 +21993,10 @@ snapshots: vary@1.1.2: {} - vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.0): + vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: lightningcss: 1.32.0 - picomatch: 4.0.3 + picomatch: 4.0.4 postcss: 8.5.8 rolldown: 1.0.0-rc.10 tinyglobby: 0.2.15 @@ -22012,7 +22009,7 @@ snapshots: sugarss: 5.0.1(postcss@8.5.8) terser: 5.39.0 tsx: 4.21.0 - yaml: 2.7.0 + yaml: 2.8.3 void-elements@3.1.0: {} @@ -22289,9 +22286,9 @@ snapshots: yallist@3.1.1: {} - yaml@1.10.2: {} + yaml@1.10.3: {} - yaml@2.7.0: {} + yaml@2.8.3: {} yargs-parser@18.1.3: dependencies: From 803f1f0b81b7d4450a9240e92558969633295c0b Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 20:00:04 +0000 Subject: [PATCH 13/44] feat: user session management (#2056) * user session management * WIP * cleanup * license * cleanup * don't cache index * rename current device property * fix --- .../public/locales/en-US/translation.json | 17 +- .../components/activate-license-modal.tsx | 82 ++++++--- .../src/ee/licence/components/oss-details.tsx | 6 +- .../session/components/session-list.tsx | 165 ++++++++++++++++++ .../features/session/queries/session-query.ts | 55 ++++++ .../session/services/session-service.ts | 17 ++ .../features/session/types/session.types.ts | 8 + .../user/components/account-name-form.tsx | 10 +- .../settings/account/account-settings.tsx | 5 + apps/server/package.json | 1 + .../middlewares/audit-context.middleware.ts | 5 + apps/server/src/core/auth/auth.controller.ts | 25 ++- apps/server/src/core/auth/dto/jwt-payload.ts | 1 + .../src/core/auth/services/auth.service.ts | 25 ++- .../src/core/auth/services/token.service.ts | 3 +- .../src/core/auth/strategies/jwt.strategy.ts | 14 ++ apps/server/src/core/core.module.ts | 2 + .../core/session/dto/revoke-session.dto.ts | 7 + .../core/session/session-activity.service.ts | 36 ++++ .../src/core/session/session.controller.ts | 80 +++++++++ .../server/src/core/session/session.module.ts | 14 ++ .../src/core/session/session.service.ts | 127 ++++++++++++++ .../services/workspace-invitation.service.ts | 4 +- .../workspace/services/workspace.service.ts | 18 +- apps/server/src/database/database.module.ts | 3 + .../20260326T121350-user-sessions.ts | 45 +++++ .../repos/session/user-session.repo.ts | 162 +++++++++++++++++ apps/server/src/database/types/db.d.ts | 16 ++ .../server/src/database/types/entity.types.ts | 6 + apps/server/src/ee | 2 +- .../src/integrations/static/static.module.ts | 5 +- pnpm-lock.yaml | 11 +- 32 files changed, 928 insertions(+), 49 deletions(-) create mode 100644 apps/client/src/features/session/components/session-list.tsx create mode 100644 apps/client/src/features/session/queries/session-query.ts create mode 100644 apps/client/src/features/session/services/session-service.ts create mode 100644 apps/client/src/features/session/types/session.types.ts create mode 100644 apps/server/src/core/session/dto/revoke-session.dto.ts create mode 100644 apps/server/src/core/session/session-activity.service.ts create mode 100644 apps/server/src/core/session/session.controller.ts create mode 100644 apps/server/src/core/session/session.module.ts create mode 100644 apps/server/src/core/session/session.service.ts create mode 100644 apps/server/src/database/migrations/20260326T121350-user-sessions.ts create mode 100644 apps/server/src/database/repos/session/user-session.repo.ts diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index f35ec179..98f8eefc 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -708,5 +708,20 @@ "Resend verification email": "Resend verification email", "Verification email sent. Please check your inbox.": "Verification email sent. Please check your inbox.", "Failed to resend verification email. Please try again.": "Failed to resend verification email. Please try again.", - "We've sent you an email with your associated workspaces.": "We've sent you an email with your associated workspaces." + "We've sent you an email with your associated workspaces.": "We've sent you an email with your associated workspaces.", + "Load more": "Load more", + "Log out of all devices": "Log out of all devices", + "Log out of all sessions except this device": "Log out of all sessions except this device", + "This Device": "This Device", + "Unknown device": "Unknown device", + "No active sessions": "No active sessions", + "Session revoked": "Session revoked", + "All other sessions revoked": "All other sessions revoked", + "Last used": "Last used", + "Created": "Created", + "Rename": "Rename", + "Publish": "Publish", + "Security": "Security", + "Enforce SSO": "Enforce SSO", + "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to login with email and password." } diff --git a/apps/client/src/ee/licence/components/activate-license-modal.tsx b/apps/client/src/ee/licence/components/activate-license-modal.tsx index 28b3d0d6..766d3f25 100644 --- a/apps/client/src/ee/licence/components/activate-license-modal.tsx +++ b/apps/client/src/ee/licence/components/activate-license-modal.tsx @@ -1,6 +1,6 @@ import { z } from "zod/v4"; -import React from "react"; -import { Button, Group, Modal, Textarea } from "@mantine/core"; +import React, { useRef } from "react"; +import { Button, Divider, Group, Modal, Stack, Textarea } from "@mantine/core"; import { useForm } from "@mantine/form"; import { zod4Resolver } from "mantine-form-zod-resolver"; import { useTranslation } from "react-i18next"; @@ -49,6 +49,7 @@ interface ActivateLicenseFormProps { export function ActivateLicenseForm({ onClose }: ActivateLicenseFormProps) { const { t } = useTranslation(); const activateLicenseMutation = useActivateMutation(); + const fileInputRef = useRef<HTMLInputElement>(null); const form = useForm<FormValues>({ validate: zod4Resolver(formSchema), @@ -63,29 +64,68 @@ export function ActivateLicenseForm({ onClose }: ActivateLicenseFormProps) { onClose?.(); } + function handleFileUpload(event: React.ChangeEvent<HTMLInputElement>) { + const file = event.target.files?.[0]; + if (!file) return; + + const reader = new FileReader(); + reader.onload = (e) => { + const content = (e.target?.result as string)?.trim(); + if (content) { + form.setFieldValue("licenseKey", content); + handleSubmit({ licenseKey: content }); + } + }; + reader.readAsText(file); + + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + } + return ( <form onSubmit={form.onSubmit(handleSubmit)}> - <Textarea - label={t("License key")} - description="Enter a valid enterprise license key. Contact sales@docmost.com to purchase one." - placeholder={t("e.g eyJhb.....")} - variant="filled" - autosize - minRows={3} - maxRows={5} - data-autofocus - {...form.getInputProps("licenseKey")} + <input + type="file" + accept=".txt" + ref={fileInputRef} + onChange={handleFileUpload} + hidden /> - <Group justify="flex-end" mt="md"> - <Button - type="submit" - disabled={activateLicenseMutation.isPending} - loading={activateLicenseMutation.isPending} - > - {t("Save")} - </Button> - </Group> + <Stack gap="xs"> + <Textarea + label={t("License key")} + placeholder={t("e.g eyJhb.....")} + variant="filled" + autosize + minRows={3} + maxRows={5} + data-autofocus + {...form.getInputProps("licenseKey")} + /> + + <Group justify="flex-end"> + <Button + type="submit" + disabled={activateLicenseMutation.isPending} + loading={activateLicenseMutation.isPending} + > + {t("Save")} + </Button> + </Group> + + <Divider label={t("Or")} labelPosition="center" /> + + <Group justify="center"> + <Button + variant="light" + onClick={() => fileInputRef.current?.click()} + > + {t("Upload license file")} + </Button> + </Group> + </Stack> </form> ); } diff --git a/apps/client/src/ee/licence/components/oss-details.tsx b/apps/client/src/ee/licence/components/oss-details.tsx index e08db649..1d02daf4 100644 --- a/apps/client/src/ee/licence/components/oss-details.tsx +++ b/apps/client/src/ee/licence/components/oss-details.tsx @@ -68,7 +68,11 @@ export default function OssDetails() { </List> <Text size="sm" c="dimmed"> - Contact <a href="mailto:sales@docmost.com?subject=Enterprise%20License%20Inquiry">sales@docmost.com </a> to purchase an enterprise license. + Get an enterprise trial key at <a href="https://customers.docmost.com/" target="_blank" rel="noopener noreferrer">customers.docmost.com</a>. + </Text> + + <Text size="sm" c="dimmed"> + Visit <a href="https://docmost.com/pricing" target="_blank" rel="noopener noreferrer">docmost.com/pricing</a> to purchase an enterprise license. </Text> </Stack> </Stack> diff --git a/apps/client/src/features/session/components/session-list.tsx b/apps/client/src/features/session/components/session-list.tsx new file mode 100644 index 00000000..18a30458 --- /dev/null +++ b/apps/client/src/features/session/components/session-list.tsx @@ -0,0 +1,165 @@ +import { useState } from "react"; +import { + Button, + Divider, + Group, + Skeleton, + Stack, + Table, + Text, +} from "@mantine/core"; +import { IconDevices } from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import { + useGetSessionsQuery, + useRevokeSessionMutation, + useRevokeAllSessionsMutation, +} from "@/features/session/queries/session-query"; +import { formattedDate } from "@/lib/time"; + +const PAGE_SIZE = 5; + +export default function SessionList() { + const { t } = useTranslation(); + const { data: sessions, isLoading } = useGetSessionsQuery(); + const revokeSessionMutation = useRevokeSessionMutation(); + const revokeAllSessionsMutation = useRevokeAllSessionsMutation(); + const [visibleCount, setVisibleCount] = useState(PAGE_SIZE); + + const otherSessions = sessions?.filter((s) => !s?.isCurrentDevice) ?? []; + const visibleSessions = sessions?.slice(0, visibleCount) ?? []; + const hasMore = sessions && visibleCount < sessions.length; + + if (isLoading) { + return ( + <Table verticalSpacing="md"> + <Table.Thead> + <Table.Tr> + <Table.Th>{t("Device Name")}</Table.Th> + <Table.Th>{t("Last Active")}</Table.Th> + <Table.Th /> + </Table.Tr> + </Table.Thead> + <Table.Tbody> + {[1, 2, 3].map((i) => ( + <Table.Tr key={i}> + <Table.Td> + <Group gap="xs"> + <Skeleton height={18} width={18} radius="sm" /> + <Skeleton height={14} width={140} radius="xs" /> + </Group> + </Table.Td> + <Table.Td> + <Skeleton height={14} width={120} radius="xs" /> + </Table.Td> + <Table.Td> + <Skeleton height={30} width={70} radius="sm" /> + </Table.Td> + </Table.Tr> + ))} + </Table.Tbody> + </Table> + ); + } + + return ( + <Stack> + {otherSessions.length > 0 && ( + <> + <div> + <Text fw={500}>{t("Log out of all devices")}</Text> + <Group justify="space-between" align="center" mt={4}> + <Text size="sm" c="dimmed"> + {t( + "Log out of all sessions except this device", + )} + </Text> + <Button + variant="outline" + color="red" + size="xs" + loading={revokeAllSessionsMutation.isPending} + onClick={() => revokeAllSessionsMutation.mutate()} + > + {t("Log out of all devices")} + </Button> + </Group> + </div> + <Divider /> + </> + )} + + <Table verticalSpacing="md"> + <Table.Thead> + <Table.Tr> + <Table.Th>{t("Device Name")}</Table.Th> + <Table.Th>{t("Last Active")}</Table.Th> + {otherSessions.length > 0 && <Table.Th />} + </Table.Tr> + </Table.Thead> + <Table.Tbody> + {visibleSessions.map((session) => ( + <Table.Tr key={session.id}> + <Table.Td> + <Group gap="xs"> + <IconDevices size={18} stroke={1.5} /> + <div> + <Text size="sm"> + {session.deviceName || t("Unknown device")} + </Text> + {session?.isCurrentDevice && ( + <Text size="xs" c="blue"> + {t("This Device")} + </Text> + )} + </div> + </Group> + </Table.Td> + <Table.Td> + <Text size="sm"> + {session?.isCurrentDevice + ? t("Now") + : formattedDate(new Date(session.lastActiveAt))} + </Text> + </Table.Td> + {otherSessions.length > 0 && ( + <Table.Td> + {!session?.isCurrentDevice && ( + <Button + variant="outline" + size="xs" + loading={revokeSessionMutation.isPending} + onClick={() => + revokeSessionMutation.mutate({ + sessionId: session.id, + }) + } + > + {t("Log out")} + </Button> + )} + </Table.Td> + )} + </Table.Tr> + ))} + </Table.Tbody> + </Table> + + {hasMore && ( + <Button + variant="subtle" + size="xs" + onClick={() => setVisibleCount((c) => c + PAGE_SIZE)} + > + {t("Load more")} + </Button> + )} + + {(!sessions || sessions.length === 0) && ( + <Text size="sm" c="dimmed" ta="center"> + {t("No active sessions")} + </Text> + )} + </Stack> + ); +} diff --git a/apps/client/src/features/session/queries/session-query.ts b/apps/client/src/features/session/queries/session-query.ts new file mode 100644 index 00000000..3104ce2b --- /dev/null +++ b/apps/client/src/features/session/queries/session-query.ts @@ -0,0 +1,55 @@ +import { + useMutation, + useQuery, + useQueryClient, + UseQueryResult, +} from "@tanstack/react-query"; +import { + getSessions, + revokeSession, + revokeAllSessions, +} from "@/features/session/services/session-service"; +import { ISession } from "@/features/session/types/session.types"; +import { notifications } from "@mantine/notifications"; +import { useTranslation } from "react-i18next"; + +export function useGetSessionsQuery(): UseQueryResult<ISession[], Error> { + return useQuery({ + queryKey: ["session-list"], + queryFn: () => getSessions(), + }); +} + +export function useRevokeSessionMutation() { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + return useMutation<void, Error, { sessionId: string }>({ + mutationFn: (data) => revokeSession(data), + onSuccess: () => { + notifications.show({ message: t("Session revoked") }); + queryClient.invalidateQueries({ queryKey: ["session-list"] }); + }, + onError: (error) => { + const errorMessage = error["response"]?.data?.message; + notifications.show({ message: errorMessage, color: "red" }); + }, + }); +} + +export function useRevokeAllSessionsMutation() { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + + return useMutation<void, Error, void>({ + mutationFn: () => revokeAllSessions(), + onSuccess: () => { + notifications.show({ message: t("All other sessions revoked") }); + queryClient.invalidateQueries({ queryKey: ["session-list"] }); + }, + onError: (error) => { + const errorMessage = error["response"]?.data?.message; + notifications.show({ message: errorMessage, color: "red" }); + }, + }); +} diff --git a/apps/client/src/features/session/services/session-service.ts b/apps/client/src/features/session/services/session-service.ts new file mode 100644 index 00000000..6f2bde83 --- /dev/null +++ b/apps/client/src/features/session/services/session-service.ts @@ -0,0 +1,17 @@ +import api from "@/lib/api-client"; +import { ISession } from "@/features/session/types/session.types"; + +export async function getSessions(): Promise<ISession[]> { + const req = await api.post<{ sessions: ISession[] }>("/sessions"); + return req.data.sessions; +} + +export async function revokeSession(data: { + sessionId: string; +}): Promise<void> { + await api.post("/sessions/revoke", data); +} + +export async function revokeAllSessions(): Promise<void> { + await api.post("/sessions/revoke-all"); +} diff --git a/apps/client/src/features/session/types/session.types.ts b/apps/client/src/features/session/types/session.types.ts new file mode 100644 index 00000000..636f8c30 --- /dev/null +++ b/apps/client/src/features/session/types/session.types.ts @@ -0,0 +1,8 @@ +export type ISession = { + id: string; + deviceName: string | null; + geoLocation: string | null; + lastActiveAt: string; + createdAt: string; + isCurrentDevice?: boolean; +}; diff --git a/apps/client/src/features/user/components/account-name-form.tsx b/apps/client/src/features/user/components/account-name-form.tsx index 98929b04..70a5b52c 100644 --- a/apps/client/src/features/user/components/account-name-form.tsx +++ b/apps/client/src/features/user/components/account-name-form.tsx @@ -1,9 +1,8 @@ import { useAtom } from "jotai"; -import { focusAtom } from "jotai-optics"; import { z } from "zod/v4"; import { useForm } from "@mantine/form"; import { zod4Resolver } from "mantine-form-zod-resolver"; -import { currentUserAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { userAtom } from "@/features/user/atoms/current-user-atom.ts"; import { updateUser } from "@/features/user/services/user-service.ts"; import { IUser } from "@/features/user/types/user.types.ts"; import { useState } from "react"; @@ -17,18 +16,15 @@ const formSchema = z.object({ type FormValues = z.infer<typeof formSchema>; -const userAtom = focusAtom(currentUserAtom, (optic) => optic.prop("user")); - export default function AccountNameForm() { const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); - const [currentUser] = useAtom(currentUserAtom); - const [, setUser] = useAtom(userAtom); + const [user, setUser] = useAtom(userAtom); const form = useForm<FormValues>({ validate: zod4Resolver(formSchema), initialValues: { - name: currentUser?.user.name, + name: user?.name, }, }); diff --git a/apps/client/src/pages/settings/account/account-settings.tsx b/apps/client/src/pages/settings/account/account-settings.tsx index f1d78f7d..6f87d31a 100644 --- a/apps/client/src/pages/settings/account/account-settings.tsx +++ b/apps/client/src/pages/settings/account/account-settings.tsx @@ -8,6 +8,7 @@ import { getAppName } from "@/lib/config.ts"; import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; import { AccountMfaSection } from "@/features/user/components/account-mfa-section"; +import SessionList from "@/features/session/components/session-list"; export default function AccountSettings() { const { t } = useTranslation(); @@ -36,6 +37,10 @@ export default function AccountSettings() { <Divider my="lg" /> <AccountMfaSection /> + + <Divider my="lg" /> + + <SessionList /> </> ); } diff --git a/apps/server/package.json b/apps/server/package.json index 53fd6468..94a9c935 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -66,6 +66,7 @@ "ai": "^6.0.134", "ai-sdk-ollama": "^3.8.1", "bcrypt": "^6.0.0", + "bowser": "^2.14.1", "bullmq": "^5.71.0", "cache-manager": "^7.2.8", "cheerio": "^1.2.0", diff --git a/apps/server/src/common/middlewares/audit-context.middleware.ts b/apps/server/src/common/middlewares/audit-context.middleware.ts index d58c4353..f5066535 100644 --- a/apps/server/src/common/middlewares/audit-context.middleware.ts +++ b/apps/server/src/common/middlewares/audit-context.middleware.ts @@ -7,6 +7,7 @@ export interface AuditContext { actorId: string | null; actorType: 'user' | 'system' | 'api_key'; ipAddress: string | null; + userAgent: string | null; } export const AUDIT_CONTEXT_KEY = 'auditContext'; @@ -19,11 +20,15 @@ export class AuditContextMiddleware implements NestMiddleware { const workspaceId = (req as any).workspaceId ?? null; const ipAddress = this.extractIpAddress(req); + const userAgent = + (req.headers['user-agent'] as string) ?? null; + const auditContext: AuditContext = { workspaceId, actorId: null, actorType: 'user', ipAddress, + userAgent, }; this.cls.set(AUDIT_CONTEXT_KEY, auditContext); diff --git a/apps/server/src/core/auth/auth.controller.ts b/apps/server/src/core/auth/auth.controller.ts index f83fc1cf..6eab6539 100644 --- a/apps/server/src/core/auth/auth.controller.ts +++ b/apps/server/src/core/auth/auth.controller.ts @@ -5,12 +5,14 @@ import { HttpStatus, Inject, Post, + Req, Res, UseGuards, Logger, } from '@nestjs/common'; import { LoginDto } from './dto/login.dto'; import { AuthService } from './services/auth.service'; +import { SessionService } from '../session/session.service'; import { SetupGuard } from './guards/setup.guard'; import { EnvironmentService } from '../../integrations/environment/environment.service'; import { CreateAdminUserDto } from './dto/create-admin-user.dto'; @@ -22,7 +24,7 @@ import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; import { ForgotPasswordDto } from './dto/forgot-password.dto'; import { PasswordResetDto } from './dto/password-reset.dto'; import { VerifyUserTokenDto } from './dto/verify-user-token.dto'; -import { FastifyReply } from 'fastify'; +import { FastifyReply, FastifyRequest } from 'fastify'; import { validateSsoEnforcement } from './auth.util'; import { ModuleRef } from '@nestjs/core'; import { AuditEvent, AuditResource } from '../../common/events/audit-events'; @@ -37,6 +39,7 @@ export class AuthController { constructor( private authService: AuthService, + private sessionService: SessionService, private environmentService: EnvironmentService, private moduleRef: ModuleRef, @Inject(AUDIT_SERVICE) private readonly auditService: IAuditService, @@ -115,8 +118,15 @@ export class AuthController { @Body() dto: ChangePasswordDto, @AuthUser() user: User, @AuthWorkspace() workspace: Workspace, + @Req() req: FastifyRequest, ) { - return this.authService.changePassword(dto, user.id, workspace.id); + const currentSessionId = (req.raw as any).sessionId; + return this.authService.changePassword( + dto, + user.id, + workspace.id, + currentSessionId, + ); } @HttpCode(HttpStatus.OK) @@ -178,8 +188,18 @@ export class AuthController { @Post('logout') async logout( @AuthUser() user: User, + @Req() req: FastifyRequest, @Res({ passthrough: true }) res: FastifyReply, ) { + const sessionId = (req.raw as any).sessionId; + if (sessionId) { + await this.sessionService.revokeSession( + sessionId, + user.id, + user.workspaceId, + ); + } + res.clearCookie('authToken'); this.auditService.log({ @@ -192,6 +212,7 @@ export class AuthController { setAuthCookie(res: FastifyReply, token: string) { res.setCookie('authToken', token, { httpOnly: true, + sameSite: 'lax', path: '/', expires: this.environmentService.getCookieExpiresIn(), secure: this.environmentService.isHttps(), diff --git a/apps/server/src/core/auth/dto/jwt-payload.ts b/apps/server/src/core/auth/dto/jwt-payload.ts index 0f7db401..f70848b9 100644 --- a/apps/server/src/core/auth/dto/jwt-payload.ts +++ b/apps/server/src/core/auth/dto/jwt-payload.ts @@ -11,6 +11,7 @@ export type JwtPayload = { email: string; workspaceId: string; type: 'access'; + sessionId?: string; }; export type JwtCollabPayload = { diff --git a/apps/server/src/core/auth/services/auth.service.ts b/apps/server/src/core/auth/services/auth.service.ts index 1bb2c5ee..bfd8e1a0 100644 --- a/apps/server/src/core/auth/services/auth.service.ts +++ b/apps/server/src/core/auth/services/auth.service.ts @@ -8,6 +8,8 @@ import { import { LoginDto } from '../dto/login.dto'; import { CreateUserDto } from '../dto/create-user.dto'; import { TokenService } from './token.service'; +import { SessionService } from '../../session/session.service'; +import { UserSessionRepo } from '@docmost/db/repos/session/user-session.repo'; import { SignupService } from './signup.service'; import { CreateAdminUserDto } from '../dto/create-admin-user.dto'; import { UserRepo } from '@docmost/db/repos/user/user.repo'; @@ -44,6 +46,8 @@ export class AuthService { constructor( private signupService: SignupService, private tokenService: TokenService, + private sessionService: SessionService, + private userSessionRepo: UserSessionRepo, private userRepo: UserRepo, private userTokenRepo: UserTokenRepo, private mailService: MailService, @@ -90,19 +94,19 @@ export class AuthService { metadata: { source: 'password' }, }); - return this.tokenService.generateAccessToken(user); + return this.sessionService.createSessionAndToken(user); } async register(createUserDto: CreateUserDto, workspaceId: string) { const user = await this.signupService.signup(createUserDto, workspaceId); - return this.tokenService.generateAccessToken(user); + return this.sessionService.createSessionAndToken(user); } async setup(createAdminUserDto: CreateAdminUserDto) { const { workspace, user } = await this.signupService.initialSetup(createAdminUserDto); - const authToken = await this.tokenService.generateAccessToken(user); + const authToken = await this.sessionService.createSessionAndToken(user); return { workspace, authToken }; } @@ -110,6 +114,7 @@ export class AuthService { dto: ChangePasswordDto, userId: string, workspaceId: string, + currentSessionId?: string, ): Promise<void> { const user = await this.userRepo.findById(userId, workspaceId, { includePassword: true, @@ -138,6 +143,16 @@ export class AuthService { workspaceId, ); + if (currentSessionId) { + await this.userSessionRepo.deleteAllExceptCurrent( + currentSessionId, + userId, + workspaceId, + ); + } else { + await this.userSessionRepo.deleteByUserId(userId, workspaceId); + } + this.auditService.log({ event: AuditEvent.USER_PASSWORD_CHANGED, resourceType: AuditResource.USER, @@ -244,6 +259,8 @@ export class AuthService { .execute(); }); + await this.userSessionRepo.deleteByUserId(user.id, workspace.id); + this.auditService.setActorId(user.id); this.auditService.log({ event: AuditEvent.USER_PASSWORD_RESET, @@ -276,7 +293,7 @@ export class AuthService { }; } - const authToken = await this.tokenService.generateAccessToken(user); + const authToken = await this.sessionService.createSessionAndToken(user); return { authToken }; } diff --git a/apps/server/src/core/auth/services/token.service.ts b/apps/server/src/core/auth/services/token.service.ts index 182b6675..b9035ba3 100644 --- a/apps/server/src/core/auth/services/token.service.ts +++ b/apps/server/src/core/auth/services/token.service.ts @@ -25,7 +25,7 @@ export class TokenService { private environmentService: EnvironmentService, ) {} - async generateAccessToken(user: User): Promise<string> { + async generateAccessToken(user: User, sessionId: string): Promise<string> { if (isUserDisabled(user)) { throw new ForbiddenException(); } @@ -35,6 +35,7 @@ export class TokenService { email: user.email, workspaceId: user.workspaceId, type: JwtType.ACCESS, + sessionId, }; return this.jwtService.sign(payload); } diff --git a/apps/server/src/core/auth/strategies/jwt.strategy.ts b/apps/server/src/core/auth/strategies/jwt.strategy.ts index 61096245..d861eddf 100644 --- a/apps/server/src/core/auth/strategies/jwt.strategy.ts +++ b/apps/server/src/core/auth/strategies/jwt.strategy.ts @@ -5,6 +5,8 @@ import { EnvironmentService } from '../../../integrations/environment/environmen import { JwtApiKeyPayload, JwtPayload, JwtType } from '../dto/jwt-payload'; import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo'; import { UserRepo } from '@docmost/db/repos/user/user.repo'; +import { UserSessionRepo } from '@docmost/db/repos/session/user-session.repo'; +import { SessionActivityService } from '../../session/session-activity.service'; import { FastifyRequest } from 'fastify'; import { extractBearerTokenFromHeader, isUserDisabled } from '../../../common/helpers'; import { ModuleRef } from '@nestjs/core'; @@ -16,6 +18,8 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { constructor( private userRepo: UserRepo, private workspaceRepo: WorkspaceRepo, + private userSessionRepo: UserSessionRepo, + private sessionActivityService: SessionActivityService, private readonly environmentService: EnvironmentService, private moduleRef: ModuleRef, ) { @@ -57,6 +61,16 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { throw new UnauthorizedException(); } + if ((payload as JwtPayload).sessionId) { + const sessionId = (payload as JwtPayload).sessionId; + const session = await this.userSessionRepo.findActiveById(sessionId); + if (!session || session.userId !== payload.sub || session.workspaceId !== payload.workspaceId) { + throw new UnauthorizedException(); + } + req.raw.sessionId = sessionId; + this.sessionActivityService.trackActivity(sessionId, payload.sub, payload.workspaceId); + } + return { user, workspace }; } diff --git a/apps/server/src/core/core.module.ts b/apps/server/src/core/core.module.ts index 81dfc138..4cef21ed 100644 --- a/apps/server/src/core/core.module.ts +++ b/apps/server/src/core/core.module.ts @@ -20,6 +20,7 @@ import { AuditContextMiddleware } from '../common/middlewares/audit-context.midd import { ShareModule } from './share/share.module'; import { NotificationModule } from './notification/notification.module'; import { WatcherModule } from './watcher/watcher.module'; +import { SessionModule } from './session/session.module'; import { ClsMiddleware } from 'nestjs-cls'; @Module({ @@ -38,6 +39,7 @@ import { ClsMiddleware } from 'nestjs-cls'; ShareModule, NotificationModule, WatcherModule, + SessionModule, ], }) export class CoreModule implements NestModule { diff --git a/apps/server/src/core/session/dto/revoke-session.dto.ts b/apps/server/src/core/session/dto/revoke-session.dto.ts new file mode 100644 index 00000000..7e7bb86f --- /dev/null +++ b/apps/server/src/core/session/dto/revoke-session.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsUUID } from 'class-validator'; + +export class RevokeSessionDto { + @IsUUID() + @IsNotEmpty() + sessionId: string; +} diff --git a/apps/server/src/core/session/session-activity.service.ts b/apps/server/src/core/session/session-activity.service.ts new file mode 100644 index 00000000..4b9adc4e --- /dev/null +++ b/apps/server/src/core/session/session-activity.service.ts @@ -0,0 +1,36 @@ +import { Injectable } from '@nestjs/common'; +import { RedisService } from '@nestjs-labs/nestjs-ioredis'; +import type { Redis } from 'ioredis'; +import { UserSessionRepo } from '@docmost/db/repos/session/user-session.repo'; +import { UserRepo } from '@docmost/db/repos/user/user.repo'; + +const THROTTLE_SECONDS = 15 * 60; // 15 minutes + +@Injectable() +export class SessionActivityService { + private readonly redis: Redis; + + constructor( + private readonly redisService: RedisService, + private readonly userSessionRepo: UserSessionRepo, + private readonly userRepo: UserRepo, + ) { + this.redis = this.redisService.getOrThrow(); + } + + trackActivity(sessionId: string, userId: string, workspaceId: string): void { + const key = `session:activity:${sessionId}`; + + this.redis + .set(key, '1', 'EX', THROTTLE_SECONDS, 'NX') + .then((result) => { + if (result === null) return; // key already exists, throttled + + this.userSessionRepo.updateLastActiveAt(sessionId).catch(() => {}); + this.userRepo + .updateUser({ lastActiveAt: new Date() }, userId, workspaceId) + .catch(() => {}); + }) + .catch(() => {}); + } +} diff --git a/apps/server/src/core/session/session.controller.ts b/apps/server/src/core/session/session.controller.ts new file mode 100644 index 00000000..75b83c06 --- /dev/null +++ b/apps/server/src/core/session/session.controller.ts @@ -0,0 +1,80 @@ +import { + BadRequestException, + Body, + Controller, + HttpCode, + HttpStatus, + Post, + Req, + UseGuards, +} from '@nestjs/common'; +import { SessionService } from './session.service'; +import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; +import { AuthUser } from '../../common/decorators/auth-user.decorator'; +import { AuthWorkspace } from '../../common/decorators/auth-workspace.decorator'; +import { User, Workspace } from '@docmost/db/types/entity.types'; +import { RevokeSessionDto } from './dto/revoke-session.dto'; +import { FastifyRequest } from 'fastify'; + +@UseGuards(JwtAuthGuard) +@Controller('sessions') +export class SessionController { + constructor(private readonly sessionService: SessionService) {} + + @HttpCode(HttpStatus.OK) + @Post() + async listSessions( + @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, + @Req() req: FastifyRequest, + ) { + const currentSessionId = (req.raw as any).sessionId ?? null; + const sessions = await this.sessionService.getActiveSessions( + user.id, + workspace.id, + currentSessionId, + ); + return { sessions }; + } + + @HttpCode(HttpStatus.OK) + @Post('revoke') + async revokeSession( + @Body() dto: RevokeSessionDto, + @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, + @Req() req: FastifyRequest, + ) { + const currentSessionId = (req.raw as any).sessionId; + if (dto.sessionId === currentSessionId) { + throw new BadRequestException( + 'Cannot revoke current session. Use logout instead.', + ); + } + await this.sessionService.revokeSession( + dto.sessionId, + user.id, + workspace.id, + ); + } + + @HttpCode(HttpStatus.OK) + @Post('revoke-all') + async revokeAllSessions( + @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, + @Req() req: FastifyRequest, + ) { + const currentSessionId = (req.raw as any).sessionId; + if (!currentSessionId) { + throw new BadRequestException( + 'Current session not found. Please log in again.', + ); + } + await this.sessionService.revokeAllOtherSessions( + currentSessionId, + user.id, + workspace.id, + ); + } +} diff --git a/apps/server/src/core/session/session.module.ts b/apps/server/src/core/session/session.module.ts new file mode 100644 index 00000000..9712e887 --- /dev/null +++ b/apps/server/src/core/session/session.module.ts @@ -0,0 +1,14 @@ +import { Global, Module } from '@nestjs/common'; +import { SessionService } from './session.service'; +import { SessionActivityService } from './session-activity.service'; +import { SessionController } from './session.controller'; +import { TokenModule } from '../auth/token.module'; + +@Global() +@Module({ + imports: [TokenModule], + controllers: [SessionController], + providers: [SessionService, SessionActivityService], + exports: [SessionService, SessionActivityService], +}) +export class SessionModule {} diff --git a/apps/server/src/core/session/session.service.ts b/apps/server/src/core/session/session.service.ts new file mode 100644 index 00000000..5b2ec47c --- /dev/null +++ b/apps/server/src/core/session/session.service.ts @@ -0,0 +1,127 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Interval } from '@nestjs/schedule'; +import { TokenService } from '../auth/services/token.service'; +import { UserSessionRepo } from '@docmost/db/repos/session/user-session.repo'; +import { EnvironmentService } from '../../integrations/environment/environment.service'; +import { User } from '@docmost/db/types/entity.types'; +import { ClsService } from 'nestjs-cls'; +import { + AuditContext, + AUDIT_CONTEXT_KEY, +} from '../../common/middlewares/audit-context.middleware'; +import * as Bowser from 'bowser'; + +const MAX_SESSIONS_PER_USER = 25; +const RETENTION_DAYS = 7; + +@Injectable() +export class SessionService { + private readonly logger = new Logger(SessionService.name); + + constructor( + private readonly tokenService: TokenService, + private readonly userSessionRepo: UserSessionRepo, + private readonly environmentService: EnvironmentService, + private readonly cls: ClsService, + ) {} + + @Interval('session-cleanup', 24 * 60 * 60 * 1000) + async cleanupSessions() { + try { + await this.userSessionRepo.deleteStale(RETENTION_DAYS); + await this.userSessionRepo.trimExcessSessions(MAX_SESSIONS_PER_USER); + this.logger.debug('Session cleanup completed'); + } catch (err) { + this.logger.error('Session cleanup failed', err); + } + } + + async createSessionAndToken(user: User): Promise<string> { + const auditContext = this.cls.get<AuditContext>(AUDIT_CONTEXT_KEY); + const ipAddress = auditContext?.ipAddress ?? null; + const userAgent = auditContext?.userAgent ?? null; + + const deviceName = this.parseDeviceName(userAgent); + const expiresAt = this.environmentService.getCookieExpiresIn(); + + const session = await this.userSessionRepo.insertSession({ + userId: user.id, + workspaceId: user.workspaceId, + deviceName, + ipAddress, + expiresAt, + }); + + return this.tokenService.generateAccessToken(user, session.id); + } + + async getActiveSessions( + userId: string, + workspaceId: string, + currentSessionId: string | null, + ) { + const sessions = await this.userSessionRepo.findActiveByUser( + userId, + workspaceId, + ); + + const mapped = sessions.map((s) => ({ + id: s.id, + deviceName: s.deviceName, + geoLocation: s.geoLocation, + lastActiveAt: s.lastActiveAt, + createdAt: s.createdAt, + isCurrentDevice: s.id === currentSessionId, + })); + + return mapped.sort((a, b) => { + if (a.isCurrentDevice) return -1; + if (b.isCurrentDevice) return 1; + return 0; + }); + } + + async revokeSession( + sessionId: string, + userId: string, + workspaceId: string, + ): Promise<void> { + await this.userSessionRepo.revokeById(sessionId, userId, workspaceId); + } + + async revokeAllOtherSessions( + currentSessionId: string, + userId: string, + workspaceId: string, + ): Promise<void> { + await this.userSessionRepo.revokeAllExceptCurrent( + currentSessionId, + userId, + workspaceId, + ); + } + + private parseDeviceName(userAgent: string | null): string | null { + if (!userAgent) return null; + + try { + const parsed = Bowser.parse(userAgent); + + const os = parsed.os?.name; + const browser = parsed.browser?.name; + const platformType = parsed.platform?.type; + + if (platformType === 'mobile' || platformType === 'tablet') { + return parsed.platform?.model || os || 'Mobile Device'; + } + + if (os) { + return browser ? `${browser} on ${os}` : os; + } + + return browser || null; + } catch { + return null; + } + } +} diff --git a/apps/server/src/core/workspace/services/workspace-invitation.service.ts b/apps/server/src/core/workspace/services/workspace-invitation.service.ts index e6ebe7ff..50ed49f0 100644 --- a/apps/server/src/core/workspace/services/workspace-invitation.service.ts +++ b/apps/server/src/core/workspace/services/workspace-invitation.service.ts @@ -22,6 +22,7 @@ import InvitationEmail from '@docmost/transactional/emails/invitation-email'; import { GroupUserRepo } from '@docmost/db/repos/group/group-user.repo'; import InvitationAcceptedEmail from '@docmost/transactional/emails/invitation-accepted-email'; import { TokenService } from '../../auth/services/token.service'; +import { SessionService } from '../../session/session.service'; import { nanoIdGen } from '../../../common/helpers'; import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; import { executeWithCursorPagination } from '@docmost/db/pagination/cursor-pagination'; @@ -49,6 +50,7 @@ export class WorkspaceInvitationService { private mailService: MailService, private domainService: DomainService, private tokenService: TokenService, + private sessionService: SessionService, @InjectKysely() private readonly db: KyselyDB, @InjectQueue(QueueName.BILLING_QUEUE) private billingQueue: Queue, private readonly environmentService: EnvironmentService, @@ -350,7 +352,7 @@ export class WorkspaceInvitationService { }; } - const authToken = await this.tokenService.generateAccessToken(newUser); + const authToken = await this.sessionService.createSessionAndToken(newUser); return { authToken }; } diff --git a/apps/server/src/core/workspace/services/workspace.service.ts b/apps/server/src/core/workspace/services/workspace.service.ts index a52506cd..c3b6550f 100644 --- a/apps/server/src/core/workspace/services/workspace.service.ts +++ b/apps/server/src/core/workspace/services/workspace.service.ts @@ -7,6 +7,7 @@ import { NotFoundException, } from '@nestjs/common'; import { LicenseCheckService } from '../../../integrations/environment/license-check.service'; +import { UserSessionRepo } from '@docmost/db/repos/session/user-session.repo'; import { CreateWorkspaceDto } from '../dto/create-workspace.dto'; import { UpdateWorkspaceDto } from '../dto/update-workspace.dto'; import { SpaceService } from '../../space/services/space.service'; @@ -67,6 +68,7 @@ export class WorkspaceService { @InjectQueue(QueueName.BILLING_QUEUE) private billingQueue: Queue, @InjectQueue(QueueName.AI_QUEUE) private aiQueue: Queue, @Inject(AUDIT_SERVICE) private readonly auditService: IAuditService, + private userSessionRepo: UserSessionRepo, ) {} async findById(workspaceId: string) { @@ -667,11 +669,15 @@ export class WorkspaceService { } } - await this.userRepo.updateUser( - { deactivatedAt: new Date() }, - userId, - workspaceId, - ); + await executeTx(this.db, async (trx) => { + await this.userRepo.updateUser( + { deactivatedAt: new Date() }, + userId, + workspaceId, + trx, + ); + await this.userSessionRepo.revokeByUserId(userId, workspaceId, trx); + }); this.auditService.log({ event: AuditEvent.USER_DEACTIVATED, @@ -785,6 +791,8 @@ export class WorkspaceService { await this.watcherRepo.deleteByUserAndWorkspace(userId, workspaceId, { trx, }); + + await this.userSessionRepo.revokeByUserId(userId, workspaceId, trx); }); this.auditService.log({ diff --git a/apps/server/src/database/database.module.ts b/apps/server/src/database/database.module.ts index 3503e4ea..6c9a7e56 100644 --- a/apps/server/src/database/database.module.ts +++ b/apps/server/src/database/database.module.ts @@ -17,6 +17,7 @@ import { KyselyDB } from '@docmost/db/types/kysely.types'; import * as process from 'node:process'; import { MigrationService } from '@docmost/db/services/migration.service'; import { UserTokenRepo } from './repos/user-token/user-token.repo'; +import { UserSessionRepo } from '@docmost/db/repos/session/user-session.repo'; import { BacklinkRepo } from '@docmost/db/repos/backlink/backlink.repo'; import { ShareRepo } from '@docmost/db/repos/share/share.repo'; import { NotificationRepo } from '@docmost/db/repos/notification/notification.repo'; @@ -76,6 +77,7 @@ import { normalizePostgresUrl } from '../common/helpers'; CommentRepo, AttachmentRepo, UserTokenRepo, + UserSessionRepo, BacklinkRepo, ShareRepo, NotificationRepo, @@ -95,6 +97,7 @@ import { normalizePostgresUrl } from '../common/helpers'; CommentRepo, AttachmentRepo, UserTokenRepo, + UserSessionRepo, BacklinkRepo, ShareRepo, NotificationRepo, diff --git a/apps/server/src/database/migrations/20260326T121350-user-sessions.ts b/apps/server/src/database/migrations/20260326T121350-user-sessions.ts new file mode 100644 index 00000000..76a56d77 --- /dev/null +++ b/apps/server/src/database/migrations/20260326T121350-user-sessions.ts @@ -0,0 +1,45 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely<any>): Promise<void> { + await db.schema + .createTable('user_sessions') + .addColumn('id', 'uuid', (col) => + col.primaryKey().defaultTo(sql`gen_uuid_v7()`), + ) + .addColumn('user_id', 'uuid', (col) => + col.notNull().references('users.id').onDelete('cascade'), + ) + .addColumn('workspace_id', 'uuid', (col) => + col.notNull().references('workspaces.id').onDelete('cascade'), + ) + .addColumn('device_name', 'varchar') + .addColumn('user_agent', 'text') + .addColumn('ip_address', sql`inet`) + .addColumn('geo_location', 'varchar') + .addColumn('last_active_at', 'timestamptz', (col) => + col.notNull().defaultTo(sql`now()`), + ) + .addColumn('expires_at', 'timestamptz', (col) => col.notNull()) + .addColumn('metadata', 'jsonb') + .addColumn('revoked_at', 'timestamptz') + .addColumn('created_at', 'timestamptz', (col) => + col.notNull().defaultTo(sql`now()`), + ) + .execute(); + + await sql` + CREATE INDEX idx_user_sessions_active + ON user_sessions (user_id, workspace_id, last_active_at DESC) + WHERE revoked_at IS NULL + `.execute(db); + + await sql` + CREATE INDEX idx_user_sessions_revoked + ON user_sessions (expires_at) + WHERE revoked_at IS NOT NULL + `.execute(db); +} + +export async function down(db: Kysely<any>): Promise<void> { + await db.schema.dropTable('user_sessions').execute(); +} diff --git a/apps/server/src/database/repos/session/user-session.repo.ts b/apps/server/src/database/repos/session/user-session.repo.ts new file mode 100644 index 00000000..f3da384b --- /dev/null +++ b/apps/server/src/database/repos/session/user-session.repo.ts @@ -0,0 +1,162 @@ +import { + InsertableUserSession, + UserSession, +} from '@docmost/db/types/entity.types'; +import { KyselyDB, KyselyTransaction } from '@docmost/db/types/kysely.types'; +import { dbOrTx } from '@docmost/db/utils'; +import { Injectable } from '@nestjs/common'; +import { InjectKysely } from 'nestjs-kysely'; +import { sql } from 'kysely'; + +@Injectable() +export class UserSessionRepo { + constructor(@InjectKysely() private readonly db: KyselyDB) {} + + async insertSession( + session: InsertableUserSession, + trx?: KyselyTransaction, + ): Promise<UserSession> { + const db = dbOrTx(this.db, trx); + return db + .insertInto('userSessions') + .values(session) + .returningAll() + .executeTakeFirstOrThrow(); + } + + async findActiveById(id: string): Promise<UserSession | undefined> { + return this.db + .selectFrom('userSessions') + .selectAll() + .where('id', '=', id) + .where('expiresAt', '>', new Date()) + .where('revokedAt', 'is', null) + .executeTakeFirst(); + } + + async findActiveByUser( + userId: string, + workspaceId: string, + ): Promise<UserSession[]> { + return this.db + .selectFrom('userSessions') + .selectAll() + .where('userId', '=', userId) + .where('workspaceId', '=', workspaceId) + .where('expiresAt', '>', new Date()) + .where('revokedAt', 'is', null) + .orderBy('lastActiveAt', 'desc') + .execute(); + } + + async updateLastActiveAt(id: string): Promise<void> { + await this.db + .updateTable('userSessions') + .set({ lastActiveAt: new Date() }) + .where('id', '=', id) + .execute(); + } + + async revokeById( + id: string, + userId: string, + workspaceId: string, + ): Promise<void> { + await this.db + .updateTable('userSessions') + .set({ revokedAt: new Date() }) + .where('id', '=', id) + .where('userId', '=', userId) + .where('workspaceId', '=', workspaceId) + .where('revokedAt', 'is', null) + .execute(); + } + + async revokeAllExceptCurrent( + currentSessionId: string, + userId: string, + workspaceId: string, + ): Promise<void> { + await this.db + .updateTable('userSessions') + .set({ revokedAt: new Date() }) + .where('userId', '=', userId) + .where('workspaceId', '=', workspaceId) + .where('id', '!=', currentSessionId) + .where('revokedAt', 'is', null) + .execute(); + } + + async revokeByUserId( + userId: string, + workspaceId: string, + trx?: KyselyTransaction, + ): Promise<void> { + const db = dbOrTx(this.db, trx); + await db + .updateTable('userSessions') + .set({ revokedAt: new Date() }) + .where('userId', '=', userId) + .where('workspaceId', '=', workspaceId) + .where('revokedAt', 'is', null) + .execute(); + } + + async deleteByUserId( + userId: string, + workspaceId: string, + ): Promise<void> { + await this.db + .deleteFrom('userSessions') + .where('userId', '=', userId) + .where('workspaceId', '=', workspaceId) + .execute(); + } + + async deleteAllExceptCurrent( + currentSessionId: string, + userId: string, + workspaceId: string, + ): Promise<void> { + await this.db + .deleteFrom('userSessions') + .where('userId', '=', userId) + .where('workspaceId', '=', workspaceId) + .where('id', '!=', currentSessionId) + .execute(); + } + + async deleteStale(retentionDays: number): Promise<void> { + const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000); + await this.db + .deleteFrom('userSessions') + .where((eb) => + eb.or([ + eb('revokedAt', '<', cutoff), + eb('expiresAt', '<', cutoff), + ]), + ) + .execute(); + } + + async trimExcessSessions(maxPerUser: number): Promise<void> { + const overflowed = await this.db + .selectFrom('userSessions') + .select(['userId', 'workspaceId']) + .groupBy(['userId', 'workspaceId']) + .having(sql`COUNT(*)`, '>', maxPerUser) + .execute(); + + for (const { userId, workspaceId } of overflowed) { + await sql` + DELETE FROM user_sessions + WHERE id IN ( + SELECT id FROM user_sessions + WHERE user_id = ${userId} AND workspace_id = ${workspaceId} + ORDER BY last_active_at DESC + OFFSET ${maxPerUser} + ) + `.execute(this.db); + } + } +} diff --git a/apps/server/src/database/types/db.d.ts b/apps/server/src/database/types/db.d.ts index ed166b75..fe4d5a13 100644 --- a/apps/server/src/database/types/db.d.ts +++ b/apps/server/src/database/types/db.d.ts @@ -429,6 +429,21 @@ export interface PagePermissions { updatedAt: Generated<Timestamp>; } +export interface UserSessions { + id: Generated<string>; + userId: string; + workspaceId: string; + deviceName: string | null; + userAgent: string | null; + ipAddress: string | null; + geoLocation: string | null; + metadata: Json | null; + lastActiveAt: Generated<Timestamp>; + expiresAt: Timestamp; + revokedAt: Timestamp | null; + createdAt: Generated<Timestamp>; +} + export interface DB { apiKeys: ApiKeys; attachments: Attachments; @@ -451,6 +466,7 @@ export interface DB { spaces: Spaces; userMfa: UserMfa; users: Users; + userSessions: UserSessions; userTokens: UserTokens; watchers: Watchers; workspaceInvitations: WorkspaceInvitations; diff --git a/apps/server/src/database/types/entity.types.ts b/apps/server/src/database/types/entity.types.ts index f8bf9ff7..d23e7475 100644 --- a/apps/server/src/database/types/entity.types.ts +++ b/apps/server/src/database/types/entity.types.ts @@ -22,6 +22,7 @@ import { Shares, FileTasks, UserMfa as _UserMFA, + UserSessions, ApiKeys, Watchers, Audit as _Audit, @@ -157,6 +158,11 @@ export type PagePermission = Selectable<_PagePermissions>; export type InsertablePagePermission = Insertable<_PagePermissions>; export type UpdatablePagePermission = Updateable<Omit<_PagePermissions, 'id'>>; +// User Session +export type UserSession = Selectable<UserSessions>; +export type InsertableUserSession = Insertable<UserSessions>; +export type UpdatableUserSession = Updateable<Omit<UserSessions, 'id'>>; + // Audit export type Audit = Selectable<_Audit>; export type InsertableAudit = Insertable<_Audit>; diff --git a/apps/server/src/ee b/apps/server/src/ee index e7a6b77b..02911b3b 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit e7a6b77b7b46ca88ab91b83d1f0267b5eb3538c1 +Subproject commit 02911b3b46fbca9a1f97736e75c73d74bafc3a08 diff --git a/apps/server/src/integrations/static/static.module.ts b/apps/server/src/integrations/static/static.module.ts index 52c3c160..f0b7e831 100644 --- a/apps/server/src/integrations/static/static.module.ts +++ b/apps/server/src/integrations/static/static.module.ts @@ -71,7 +71,10 @@ export class StaticModule implements OnModuleInit { app.get(RENDER_PATH, (req: any, res: any) => { const stream = fs.createReadStream(indexFilePath); - res.type('text/html').send(stream); + res + .header('Cache-Control', 'no-cache, no-store, must-revalidate') + .type('text/html') + .send(stream); }); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7601191..22d805cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -557,6 +557,9 @@ importers: bcrypt: specifier: ^6.0.0 version: 6.0.0 + bowser: + specifier: ^2.14.1 + version: 2.14.1 bullmq: specifier: ^5.71.0 version: 5.71.0 @@ -5845,8 +5848,8 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - bowser@2.11.0: - resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} boxen@5.1.2: resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} @@ -11382,7 +11385,7 @@ snapshots: dependencies: '@aws-sdk/types': 3.973.6 '@smithy/types': 4.13.1 - bowser: 2.11.0 + bowser: 2.14.1 tslib: 2.8.1 '@aws-sdk/util-user-agent-node@3.973.10': @@ -16626,7 +16629,7 @@ snapshots: boolbase@1.0.0: {} - bowser@2.11.0: {} + bowser@2.14.1: {} boxen@5.1.2: dependencies: From a3559b7c334c4e0171da0f26a03a5648c638af59 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 26 Mar 2026 20:01:02 +0000 Subject: [PATCH 14/44] sync --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index 02911b3b..a258ca36 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 02911b3b46fbca9a1f97736e75c73d74bafc3a08 +Subproject commit a258ca366077d6e97b340c4faf56747ca6a3ca78 From 2d835da0e3dec8dfb54109ee2da553c27ede1597 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Fri, 27 Mar 2026 22:11:19 +0000 Subject: [PATCH 15/44] New Crowdin updates (#2059) --- .../public/locales/de-DE/translation.json | 58 +- .../public/locales/en-US/translation.json | 510 +++++++++--------- .../public/locales/es-ES/translation.json | 58 +- .../public/locales/fr-FR/translation.json | 58 +- .../public/locales/it-IT/translation.json | 58 +- .../public/locales/ja-JP/translation.json | 58 +- .../public/locales/ko-KR/translation.json | 58 +- .../public/locales/nl-NL/translation.json | 58 +- .../public/locales/pt-BR/translation.json | 58 +- .../public/locales/ru-RU/translation.json | 58 +- .../public/locales/uk-UA/translation.json | 58 +- .../public/locales/zh-CN/translation.json | 58 +- 12 files changed, 739 insertions(+), 409 deletions(-) diff --git a/apps/client/public/locales/de-DE/translation.json b/apps/client/public/locales/de-DE/translation.json index 850374cd..feb8b46b 100644 --- a/apps/client/public/locales/de-DE/translation.json +++ b/apps/client/public/locales/de-DE/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Speichern & Beenden", "Double-click to edit Excalidraw diagram": "Zum Bearbeiten des Excalidraw-Diagramms doppelklicken", "Paste link": "Link einfügen", + "Paste link or search pages": "Link einfügen oder Seiten durchsuchen", + "Link to web page": "Link zur Webseite", + "Recents": "Zuletzt verwendet", + "Page or URL": "Seite oder URL", + "Link title": "Linktitel", "Edit link": "Link bearbeiten", "Remove link": "Link entfernen", "Add link": "Link hinzufügen", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Öffentliches Teilen im Bereich umschalten", "Public sharing is disabled at the workspace level": "Öffentliches Teilen ist auf der Arbeitsbereichsebene deaktiviert", "Prevent pages in this space from being shared publicly.": "Verhindern Sie, dass Seiten in diesem Bereich öffentlich geteilt werden.", - "Requires an enterprise license": "Erfordert eine Unternehmenslizenz", "Page permissions": "Seitenberechtigungen", "Control who can view and edit individual pages. Available with an enterprise license.": "Steuern Sie, wer einzelne Seiten ansehen und bearbeiten kann. Verfügbar mit einer Enterprise-Lizenz.", "Enable public sharing": "Öffentliches Teilen aktivieren", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "Generative KI (KI fragen)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Aktivieren Sie die KI-unterstützte Inhaltserstellung im Editor. Ermöglicht Benutzern das Erzeugen, Verbessern, Übersetzen und Transformieren von Text.", "Toggle generative AI": "Generative KI umschalten", - "Enterprise feature": "Enterprise-Funktion", + "Upgrade your plan": "Upgrade Ihres Plans", + "Available with a paid license": "Verfügbar mit einer kostenpflichtigen Lizenz", + "Upgrade your license tier.": "Stufen Sie Ihre Lizenz hoch.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "KI ist nur in der Docmost Enterprise-Edition verfügbar. Kontaktieren Sie sales@docmost.com.", "AI & MCP": "KI & MCP", "AI": "KI", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Model Context Protocol (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Aktivieren Sie den MCP-Server, damit KI-Assistenten und -Tools mit den Inhalten Ihres Arbeitsbereichs interagieren können.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP ist nur in der Docmost Enterprise-Edition verfügbar. Kontaktieren Sie sales@docmost.com.", - "MCP documentation": "MCP-Dokumentation", "MCP Server URL": "MCP-Server-URL", "Use your API key for authentication. You can manage API keys in your account settings.": "Verwenden Sie Ihren API-Schlüssel zur Authentifizierung. API-Schlüssel können in Ihren Kontoeinstellungen verwaltet werden.", "Supported tools": "Unterstützte Tools", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "In Ihrem Arbeitsbereich ist MCP aktiviert. Verwenden Sie Ihren API-Schlüssel, um KI-Assistenten anzubinden.", "MCP server URL:": "MCP-Server-URL:", "Learn more": "Mehr erfahren", - "View the": "Anzeigen", - "for usage details.": "für Informationen zur Nutzung.", - "for setup instructions.": "für Einrichtungshinweise.", - "API documentation": "API-Dokumentation", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Verwalten Sie API-Schlüssel für alle Nutzer im Arbeitsbereich. Siehe die <anchor>API-Dokumentation</anchor> für Details zur Verwendung.", + "View the <anchor>API documentation</anchor> for usage details.": "Siehe die <anchor>API-Dokumentation</anchor> für Details zur Verwendung.", + "View the <anchor>MCP documentation</anchor>.": "Sehen Sie die <anchor>MCP-Dokumentation</anchor> ein.", "Sources": "Quellen", "AI Answers not available for attachments": "KI-Antworten sind für Anhänge nicht verfügbar", "No answer available": "Keine Antwort verfügbar", @@ -654,12 +658,12 @@ "Mark all as read": "Alle als gelesen markieren", "Mark as read": "Als gelesen markieren", "More options": "Weitere Optionen", - "mentioned you in a comment": "hat Sie in einem Kommentar erwähnt", - "commented on a page": "hat auf einer Seite kommentiert", - "resolved a comment": "hat einen Kommentar gelöst", - "mentioned you on a page": "hat Sie auf einer Seite erwähnt", - "gave you edit access to a page": "hat Ihnen Bearbeitungsrechte für eine Seite gegeben", - "gave you view access to a page": "hat Ihnen Leserechte für eine Seite gewährt", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> hat Sie in einem Kommentar erwähnt", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> hat einen Kommentar auf einer Seite hinterlassen", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> hat einen Kommentar als erledigt markiert", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> hat Sie auf einer Seite erwähnt", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> hat Ihnen Bearbeitungszugriff auf eine Seite gegeben", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> hat Ihnen Ansichtsrechte für eine Seite gegeben", "Today": "Heute", "Yesterday": "Gestern", "This week": "Diese Woche", @@ -693,5 +697,31 @@ "Failed to update trash retention": "Aktualisierung der Aufbewahrungsdauer des Papierkorbs fehlgeschlagen", "Removed page restriction": "Seitenbeschränkung entfernt", "Added page permission": "Seitenberechtigung hinzugefügt", - "Removed page permission": "Seitenberechtigung entfernt" + "Removed page permission": "Seitenberechtigung entfernt", + "Verifying your email": "E-Mail wird überprüft", + "Please wait...": "Bitte warten...", + "Verification failed. The link may have expired.": "Überprüfung fehlgeschlagen. Der Link ist möglicherweise abgelaufen.", + "Check your email": "Prüfen Sie Ihr E-Mail-Postfach", + "We sent a verification link to {{email}}.": "Wir haben einen Bestätigungslink an {{email}} gesendet.", + "We sent a verification link to your email.": "Wir haben einen Bestätigungslink an Ihre E-Mail-Adresse gesendet.", + "Click the link to verify your email and access your workspace.": "Klicken Sie auf den Link, um Ihre E-Mail zu bestätigen und auf Ihren Arbeitsbereich zuzugreifen.", + "Resend verification email": "Bestätigungs-E-Mail erneut senden", + "Verification email sent. Please check your inbox.": "Bestätigungs-E-Mail gesendet. Bitte überprüfen Sie Ihr Postfach.", + "Failed to resend verification email. Please try again.": "Fehler beim erneuten Senden der Bestätigungs-E-Mail. Bitte versuchen Sie es erneut.", + "We've sent you an email with your associated workspaces.": "Wir haben Ihnen eine E-Mail mit Ihren zugehörigen Arbeitsbereichen gesendet.", + "Load more": "Mehr laden", + "Log out of all devices": "Von allen Geräten abmelden", + "Log out of all sessions except this device": "Von allen Sitzungen außer diesem Gerät abmelden", + "This Device": "Dieses Gerät", + "Unknown device": "Unbekanntes Gerät", + "No active sessions": "Keine aktiven Sitzungen", + "Session revoked": "Sitzung widerrufen", + "All other sessions revoked": "Alle anderen Sitzungen widerrufen", + "Last used": "Zuletzt verwendet", + "Created": "Erstellt", + "Rename": "Umbenennen", + "Publish": "Veröffentlichen", + "Security": "Sicherheit", + "Enforce SSO": "SSO erzwingen", + "Once enforced, members will not be able to login with email and password.": "Nach dem Erzwingen können sich Mitglieder nicht mehr mit E-Mail und Passwort anmelden." } diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 98f8eefc..e057d15e 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -1,7 +1,7 @@ { - "Account": "Account", + "Account": "Account ", "Active": "Active", - "Add": "Add", + "Add": "Add.", "Add group members": "Add group members", "Add groups": "Add groups", "Add members": "Add members", @@ -44,24 +44,24 @@ "Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.", "Description": "Description", "Details": "Details", - "e.g ACME": "e.g ACME", - "e.g ACME Inc": "e.g ACME Inc", - "e.g Developers": "e.g Developers", - "e.g Group for developers": "e.g Group for developers", - "e.g product": "e.g product", - "e.g Product Team": "e.g Product Team", - "e.g Sales": "e.g Sales", - "e.g Space for product team": "e.g Space for product team", - "e.g Space for sales team to collaborate": "e.g Space for sales team to collaborate", + "e.g ACME": "e.g. ACME", + "e.g ACME Inc": "e.g. ACME Inc", + "e.g Developers": "e.g. Developers", + "e.g Group for developers": "e.g. Group for developers", + "e.g product": "e.g. product", + "e.g Product Team": "e.g. Product Team", + "e.g Sales": "e.g. Sales", + "e.g Space for product team": "e.g. Space for product team", + "e.g Space for sales team to collaborate": "e.g. Space for sales team to collaborate", "Edit": "Edit", - "Read": "Read", + "Read": "Read.", "Edit group": "Edit group", "Email": "Email", "Enter a strong password": "Enter a strong password", "Enter valid email addresses separated by comma or space max_50": "Enter valid email addresses separated by comma or space [max: 50]", - "enter valid emails addresses": "enter valid emails addresses", + "enter valid emails addresses": "Enter valid email addresses", "Enter your current password": "Enter your current password", - "enter your full name": "enter your full name", + "enter your full name": "Enter your full name", "Enter your new password": "Enter your new password", "Enter your new preferred email": "Enter your new preferred email", "Enter your password": "Enter your password", @@ -87,7 +87,7 @@ "Import pages": "Import pages", "Import pages & space settings": "Import pages & space settings", "Importing pages": "Importing pages", - "invalid invitation link": "invalid invitation link", + "invalid invitation link": "Invalid invitation link", "Invitation signup": "Invitation signup", "Invite by email": "Invite by email", "Invite members": "Invite members", @@ -113,7 +113,7 @@ "New email": "New email", "New page": "New page", "New password": "New password", - "No group found": "No group found", + "No group found": "No group found.", "No page history saved yet.": "No page history saved yet.", "No pages yet": "No pages yet", "No shared pages": "No shared pages", @@ -149,56 +149,56 @@ "Search for users": "Search for users", "Search for users and groups": "Search for users and groups", "Search...": "Search...", - "Select language": "Select language", - "Select role": "Select role", - "Select role to assign to all invited members": "Select role to assign to all invited members", - "Select theme": "Select theme", - "Send invitation": "Send invitation", - "Invitation sent": "Invitation sent", - "Settings": "Settings", - "Setup workspace": "Setup workspace", - "Sign In": "Sign In", - "Sign Up": "Sign Up", - "Slug": "Slug", - "Space": "Space", - "Space description": "Space description", - "Space menu": "Space menu", - "Space name": "Space name", - "Space settings": "Space settings", - "Space slug": "Space slug", - "Spaces": "Spaces", - "Spaces you belong to": "Spaces you belong to", - "No space found": "No space found", - "Search for spaces": "Search for spaces", + "Select language": "Select language.", + "Select role": "Select role.", + "Select role to assign to all invited members": "Select role to assign to all invited members.", + "Select theme": "Select theme.", + "Send invitation": "Send invitation.", + "Invitation sent": "Invitation sent.", + "Settings": "Settings.", + "Setup workspace": "Setup workspace.", + "Sign In": "Sign In.", + "Sign Up": "Sign Up.", + "Slug": "Slug.", + "Space": "Space.", + "Space description": "Space description.", + "Space menu": "Space menu.", + "Space name": "Space name.", + "Space settings": "Space settings.", + "Space slug": "Space slug.", + "Spaces": "Spaces.", + "Spaces you belong to": "Spaces you belong to.", + "No space found": "No space found.", + "Search for spaces": "Search for spaces.", "Start typing to search...": "Start typing to search...", - "Status": "Status", - "Successfully imported": "Successfully imported", - "Successfully restored": "Successfully restored", - "System settings": "System settings", - "Theme": "Theme", + "Status": "Status.", + "Successfully imported": "Successfully imported.", + "Successfully restored": "Successfully restored.", + "System settings": "System settings.", + "Theme": "Theme.", "To change your email, you have to enter your password and new email.": "To change your email, you have to enter your password and new email.", - "Toggle full page width": "Toggle full page width", + "Toggle full page width": "Toggle full page width.", "Unable to import pages. Please try again.": "Unable to import pages. Please try again.", - "untitled": "untitled", - "Untitled": "Untitled", - "Updated successfully": "Updated successfully", - "User": "User", - "Workspace": "Workspace", - "Workspace Name": "Workspace Name", - "Workspace settings": "Workspace settings", + "untitled": "untitled.", + "Untitled": "Untitled.", + "Updated successfully": "Updated successfully.", + "User": "User.", + "Workspace": "Workspace.", + "Workspace Name": "Workspace Name.", + "Workspace settings": "Workspace settings.", "You can change your password here.": "You can change your password here.", - "Your Email": "Your Email", + "Your Email": "Your Email.", "Your import is complete.": "Your import is complete.", - "Your name": "Your name", - "Your Name": "Your Name", - "Your password": "Your password", + "Your name": "Your name.", + "Your Name": "Your Name.", + "Your password": "Your password.", "Your password must be a minimum of 8 characters.": "Your password must be a minimum of 8 characters.", - "Sidebar toggle": "Sidebar toggle", - "Comments": "Comments", - "404 page not found": "404 page not found", + "Sidebar toggle": "Sidebar toggle.", + "Comments": "Comments.", + "404 page not found": "404 page not found.", "Sorry, we can't find the page you are looking for.": "Sorry, we can't find the page you are looking for.", - "Take me back to homepage": "Take me back to homepage", - "Forgot password": "Forgot password", + "Take me back to homepage": "Take me back to homepage.", + "Forgot password": "Forgot password.", "Forgot your password?": "Forgot your password?", "A password reset link has been sent to your email. Please check your inbox.": "A password reset link has been sent to your email. Please check your inbox.", "Send reset link": "Send reset link", @@ -222,16 +222,16 @@ "Comment deleted successfully": "Comment deleted successfully", "Failed to delete comment": "Failed to delete comment", "Comment resolved successfully": "Comment resolved successfully", - "Comment re-opened successfully": "Comment re-opened successfully", - "Comment unresolved successfully": "Comment unresolved successfully", + "Comment re-opened successfully": "Comment re-opened successfully.", + "Comment unresolved successfully": "Comment unresolved successfully.", "Failed to resolve comment": "Failed to resolve comment", - "Resolve comment": "Resolve comment", - "Unresolve comment": "Unresolve comment", - "Resolve Comment Thread": "Resolve Comment Thread", - "Unresolve Comment Thread": "Unresolve Comment Thread", + "Resolve comment": "Resolve comment.", + "Unresolve comment": "Unresolve comment.", + "Resolve Comment Thread": "Resolve Comment Thread.", + "Unresolve Comment Thread": "Unresolve Comment Thread.", "Are you sure you want to resolve this comment thread? This will mark it as completed.": "Are you sure you want to resolve this comment thread? This will mark it as completed.", "Are you sure you want to unresolve this comment thread?": "Are you sure you want to unresolve this comment thread?", - "Resolved": "Resolved", + "Resolved": "Resolved.", "No active comments.": "No active comments.", "Revoke invitation": "Revoke invitation", "Revoke": "Revoke", @@ -241,9 +241,9 @@ "Anyone with this link can join this workspace.": "Anyone with this link can join this workspace.", "Invite link": "Invite link", "Copy": "Copy", - "Copy to space": "Copy to space", + "Copy to space": "Copy to space.", "Copied": "Copied", - "Duplicate": "Duplicate", + "Duplicate": "Duplicate.", "Select a user": "Select a user", "Select a group": "Select a group", "Export all pages and attachments in this space.": "Export all pages and attachments in this space.", @@ -251,7 +251,7 @@ "Are you sure you want to delete this space?": "Are you sure you want to delete this space?", "Delete this space with all its pages and data.": "Delete this space with all its pages and data.", "All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "All pages, comments, attachments and permissions in this space will be deleted irreversibly.", - "Confirm space name": "Confirm space name", + "Confirm space name": "Confirm space name.", "Type the space name <b>{{spaceName}}</b> to confirm your action.": "Type the space name <b>{{spaceName}}</b> to confirm your action.", "Format": "Format", "Include subpages": "Include subpages", @@ -267,7 +267,7 @@ "Align left": "Align left", "Align right": "Align right", "Align center": "Align center", - "Justify": "Justify", + "Justify": "Justify.", "Merge cells": "Merge cells", "Split cell": "Split cell", "Delete column": "Delete column", @@ -312,7 +312,7 @@ "Pink": "Pink", "Gray": "Gray", "Embed link": "Embed link", - "Invalid {{provider}} embed link": "Invalid {{provider}} embed link", + "Invalid {{provider}} embed link": "Invalid {{provider}} embed link.", "Embed {{provider}}": "Embed {{provider}}", "Enter {{provider}} link to embed": "Enter {{provider}} link to embed", "Bold": "Bold", @@ -344,35 +344,35 @@ "Upload any file from your device.": "Upload any file from your device.", "Uploading {{name}}": "Uploading {{name}}", "Uploading file": "Uploading file", - "Table": "Table", + "Table": "Table.", "Insert a table.": "Insert a table.", "Insert collapsible block.": "Insert collapsible block.", - "Video": "Video", - "Divider": "Divider", - "Quote": "Quote", - "Image": "Image", - "File attachment": "File attachment", - "Toggle block": "Toggle block", - "Callout": "Callout", + "Video": "Video.", + "Divider": "Divider.", + "Quote": "Quote.", + "Image": "Image.", + "File attachment": "File attachment.", + "Toggle block": "Toggle block.", + "Callout": "Callout.", "Insert callout notice.": "Insert callout notice.", - "Math inline": "Math inline", + "Math inline": "Math inline.", "Insert inline math equation.": "Insert inline math equation.", - "Math block": "Math block", - "Insert math equation": "Insert math equation", - "Mermaid diagram": "Mermaid diagram", - "Insert mermaid diagram": "Insert mermaid diagram", - "Insert and design Drawio diagrams": "Insert and design Drawio diagrams", - "Insert current date": "Insert current date", - "Draw and sketch excalidraw diagrams": "Draw and sketch excalidraw diagrams", - "Multiple": "Multiple", + "Math block": "Math block.", + "Insert math equation": "Insert math equation.", + "Mermaid diagram": "Mermaid diagram.", + "Insert mermaid diagram": "Insert mermaid diagram.", + "Insert and design Drawio diagrams": "Insert and design Drawio diagrams.", + "Insert current date": "Insert current date.", + "Draw and sketch excalidraw diagrams": "Draw and sketch excalidraw diagrams.", + "Multiple": "Multiple.", "Turn into": "Turn into", "Text align": "Text align", "This page may have been deleted, moved, or you may not have access.": "This page may have been deleted, moved, or you may not have access.", "Go to homepage": "Go to homepage", "Pages you create will show up here.": "Pages you create will show up here.", - "Heading {{level}}": "Heading {{level}}", - "Toggle title": "Toggle title", - "Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands", + "Heading {{level}}": "Heading {{level}}.", + "Toggle title": "Toggle title.", + "Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands.", "Write...": "Write...", "Column count": "Column count", "{{count}} Columns": "{{count}} Columns", @@ -382,27 +382,27 @@ "Wide center": "Wide center", "Left wide": "Left wide", "Right wide": "Right wide", - "Names do not match": "Names do not match", - "Today, {{time}}": "Today, {{time}}", - "Yesterday, {{time}}": "Yesterday, {{time}}", - "Space created successfully": "Space created successfully", - "Space updated successfully": "Space updated successfully", - "Space deleted successfully": "Space deleted successfully", - "Members added successfully": "Members added successfully", - "Member removed successfully": "Member removed successfully", - "Member role updated successfully": "Member role updated successfully", - "Created by: <b>{{creatorName}}</b>": "Created by: <b>{{creatorName}}</b>", - "Created at: {{time}}": "Created at: {{time}}", - "Edited by {{name}} {{time}}": "Edited by {{name}} {{time}}", - "Word count: {{wordCount}}": "Word count: {{wordCount}}", - "Character count: {{characterCount}}": "Character count: {{characterCount}}", - "New update": "New update", - "{{latestVersion}} is available": "{{latestVersion}} is available", - "Default page edit mode": "Default page edit mode", + "Names do not match": "Names do not match.", + "Today, {{time}}": "Today, {{time}}.", + "Yesterday, {{time}}": "Yesterday, {{time}}.", + "Space created successfully": "Space created successfully.", + "Space updated successfully": "Space updated successfully.", + "Space deleted successfully": "Space deleted successfully.", + "Members added successfully": "Members added successfully.", + "Member removed successfully": "Member removed successfully.", + "Member role updated successfully": "Member role updated successfully.", + "Created by: <b>{{creatorName}}</b>": "Created by: <b>{{creatorName}}</b>.", + "Created at: {{time}}": "Created at: {{time}}.", + "Edited by {{name}} {{time}}": "Edited by {{name}} {{time}}.", + "Word count: {{wordCount}}": "Word count: {{wordCount}}.", + "Character count: {{characterCount}}": "Character count: {{characterCount}}.", + "New update": "New update.", + "{{latestVersion}} is available": "{{latestVersion}} is available.", + "Default page edit mode": "Default page edit mode.", "Choose your preferred page edit mode. Avoid accidental edits.": "Choose your preferred page edit mode. Avoid accidental edits.", - "Reading": "Reading", - "Delete member": "Delete member", - "Member deleted successfully": "Member deleted successfully", + "Reading": "Reading.", + "Delete member": "Delete member.", + "Member deleted successfully": "Member deleted successfully.", "Are you sure you want to delete this workspace member? This action is irreversible.": "Are you sure you want to delete this workspace member? This action is irreversible.", "Deactivate member": "Deactivate member", "Activate member": "Activate member", @@ -411,33 +411,33 @@ "Deactivate": "Deactivate", "Activate": "Activate", "Deactivated": "Deactivated", - "Move": "Move", - "Move page": "Move page", + "Move": "Move.", + "Move page": "Move page.", "Move page to a different space.": "Move page to a different space.", "Real-time editor connection lost. Retrying...": "Real-time editor connection lost. Retrying...", - "Table of contents": "Table of contents", + "Table of contents": "Table of contents.", "Add headings (H1, H2, H3) to generate a table of contents.": "Add headings (H1, H2, H3) to generate a table of contents.", - "Share": "Share", - "Public sharing": "Public sharing", - "Shared by": "Shared by", - "Shared at": "Shared at", - "Inherits public sharing from": "Inherits public sharing from", - "Share to web": "Share to web", - "Shared to web": "Shared to web", - "Anyone with the link can view this page": "Anyone with the link can view this page", - "Make this page publicly accessible": "Make this page publicly accessible", - "Include sub-pages": "Include sub-pages", - "Make sub-pages public too": "Make sub-pages public too", - "Allow search engines to index page": "Allow search engines to index page", - "Open page": "Open page", - "Page": "Page", - "Delete public share link": "Delete public share link", - "Delete share": "Delete share", + "Share": "Share.", + "Public sharing": "Public sharing.", + "Shared by": "Shared by.", + "Shared at": "Shared at.", + "Inherits public sharing from": "Inherits public sharing from.", + "Share to web": "Share to web.", + "Shared to web": "Shared to web.", + "Anyone with the link can view this page": "Anyone with the link can view this page.", + "Make this page publicly accessible": "Make this page publicly accessible.", + "Include sub-pages": "Include sub-pages.", + "Make sub-pages public too": "Make sub-pages public too.", + "Allow search engines to index page": "Allow search engines to index page.", + "Open page": "Open page.", + "Page": "Page.", + "Delete public share link": "Delete public share link.", + "Delete share": "Delete share.", "Are you sure you want to delete this shared link?": "Are you sure you want to delete this shared link?", - "Publicly shared pages from spaces you are a member of will appear here": "Publicly shared pages from spaces you are a member of will appear here", - "Share deleted successfully": "Share deleted successfully", - "Share not found": "Share not found", - "Failed to share page": "Failed to share page", + "Publicly shared pages from spaces you are a member of will appear here": "Publicly shared pages from spaces you are a member of will appear here.", + "Share deleted successfully": "Share deleted successfully.", + "Share not found": "Share not found.", + "Failed to share page": "Failed to share page.", "Disable public sharing": "Disable public sharing", "Prevent members from sharing pages publicly.": "Prevent members from sharing pages publicly.", "Toggle public sharing": "Toggle public sharing", @@ -454,135 +454,135 @@ "Public sharing is disabled": "Public sharing is disabled", "Public sharing has been disabled at the workspace level.": "Public sharing has been disabled at the workspace level.", "Public sharing has been disabled for this space.": "Public sharing has been disabled for this space.", - "Copy page": "Copy page", + "Copy page": "Copy page.", "Copy page to a different space.": "Copy page to a different space.", - "Page copied successfully": "Page copied successfully", - "Page duplicated successfully": "Page duplicated successfully", - "Find": "Find", - "Not found": "Not found", - "Previous Match (Shift+Enter)": "Previous Match (Shift+Enter)", - "Next match (Enter)": "Next match (Enter)", - "Match case (Alt+C)": "Match case (Alt+C)", - "Replace": "Replace", - "Close (Escape)": "Close (Escape)", - "Replace (Enter)": "Replace (Enter)", - "Replace all (Ctrl+Alt+Enter)": "Replace all (Ctrl+Alt+Enter)", - "Replace all": "Replace all", - "View all spaces": "View all spaces", - "Error": "Error", - "Failed to disable MFA": "Failed to disable MFA", - "Disable two-factor authentication": "Disable two-factor authentication", + "Page copied successfully": "Page copied successfully.", + "Page duplicated successfully": "Page duplicated successfully.", + "Find": "Find.", + "Not found": "Not found.", + "Previous Match (Shift+Enter)": "Previous Match (Shift+Enter).", + "Next match (Enter)": "Next match (Enter).", + "Match case (Alt+C)": "Match case (Alt+C).", + "Replace": "Replace.", + "Close (Escape)": "Close (Escape).", + "Replace (Enter)": "Replace (Enter).", + "Replace all (Ctrl+Alt+Enter)": "Replace all (Ctrl+Alt+Enter).", + "Replace all": "Replace all.", + "View all spaces": "View all spaces.", + "Error": "Error.", + "Failed to disable MFA": "Failed to disable MFA.", + "Disable two-factor authentication": "Disable two-factor authentication.", "Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.", "Please enter your password to disable two-factor authentication:": "Please enter your password to disable two-factor authentication:", - "Two-factor authentication has been enabled": "Two-factor authentication has been enabled", - "Two-factor authentication has been disabled": "Two-factor authentication has been disabled", - "2-step verification": "2-step verification", + "Two-factor authentication has been enabled": "Two-factor authentication has been enabled.", + "Two-factor authentication has been disabled": "Two-factor authentication has been disabled.", + "2-step verification": "2-step verification.", "Protect your account with an additional verification layer when signing in.": "Protect your account with an additional verification layer when signing in.", "Two-factor authentication is active on your account.": "Two-factor authentication is active on your account.", - "Add 2FA method": "Add 2FA method", - "Backup codes": "Backup codes", - "Disable": "Disable", - "Invalid verification code": "Invalid verification code", - "New backup codes have been generated": "New backup codes have been generated", - "Failed to regenerate backup codes": "Failed to regenerate backup codes", - "About backup codes": "About backup codes", + "Add 2FA method": "Add 2FA method.", + "Backup codes": "Backup codes.", + "Disable": "Disable.", + "Invalid verification code": "Invalid verification code.", + "New backup codes have been generated": "New backup codes have been generated.", + "Failed to regenerate backup codes": "Failed to regenerate backup codes.", + "About backup codes": "About backup codes.", "Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.", "You can regenerate new backup codes at any time. This will invalidate all existing codes.": "You can regenerate new backup codes at any time. This will invalidate all existing codes.", - "Confirm password": "Confirm password", - "Generate new backup codes": "Generate new backup codes", - "Save your new backup codes": "Save your new backup codes", + "Confirm password": "Confirm password.", + "Generate new backup codes": "Generate new backup codes.", + "Save your new backup codes": "Save your new backup codes.", "Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Make sure to save these codes in a secure place. Your old backup codes are no longer valid.", - "Your new backup codes": "Your new backup codes", - "I've saved my backup codes": "I've saved my backup codes", - "Failed to setup MFA": "Failed to setup MFA", - "Setup & Verify": "Setup & Verify", - "Add to authenticator": "Add to authenticator", - "1. Scan this QR code with your authenticator app": "1. Scan this QR code with your authenticator app", + "Your new backup codes": "Your new backup codes.", + "I've saved my backup codes": "I've saved my backup codes.", + "Failed to setup MFA": "Failed to setup MFA.", + "Setup & Verify": "Setup & Verify.", + "Add to authenticator": "Add to authenticator.", + "1. Scan this QR code with your authenticator app": "1. Scan this QR code with your authenticator app.", "Can't scan the code?": "Can't scan the code?", "Enter this code manually in your authenticator app:": "Enter this code manually in your authenticator app:", - "2. Enter the 6-digit code from your authenticator": "2. Enter the 6-digit code from your authenticator", - "Verify and enable": "Verify and enable", + "2. Enter the 6-digit code from your authenticator": "2. Enter the 6-digit code from your authenticator.", + "Verify and enable": "Verify and enable.", "Failed to generate QR code. Please try again.": "Failed to generate QR code. Please try again.", - "Backup": "Backup", - "Save codes": "Save codes", - "Save your backup codes": "Save your backup codes", + "Backup": "Backup.", + "Save codes": "Save codes.", + "Save your backup codes": "Save your backup codes.", "These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.", - "Print": "Print", + "Print": "Print.", "Two-factor authentication has been set up. Please log in again.": "Two-factor authentication has been set up. Please log in again.", - "Two-Factor authentication required": "Two-factor authentication required", - "Your workspace requires two-factor authentication for all users": "Your workspace requires two-factor authentication for all users", + "Two-Factor authentication required": "Two-factor authentication required.", + "Your workspace requires two-factor authentication for all users": "Your workspace requires two-factor authentication for all users.", "To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.", - "Set up two-factor authentication": "Set up two-factor authentication", - "Cancel and logout": "Cancel and logout", + "Set up two-factor authentication": "Set up two-factor authentication.", + "Cancel and logout": "Cancel and logout.", "Your workspace requires two-factor authentication. Please set it up to continue.": "Your workspace requires two-factor authentication. Please set it up to continue.", "This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "This adds an extra layer of security to your account by requiring a verification code from your authenticator app.", - "Password is required": "Password is required", - "Password must be at least 8 characters": "Password must be at least 8 characters", - "Please enter a 6-digit code": "Please enter a 6-digit code", - "Code must be exactly 6 digits": "Code must be exactly 6 digits", - "Enter the 6-digit code found in your authenticator app": "Enter the 6-digit code found in your authenticator app", + "Password is required": "Password is required.", + "Password must be at least 8 characters": "Password must be at least 8 characters.", + "Please enter a 6-digit code": "Please enter a 6-digit code.", + "Code must be exactly 6 digits": "Code must be exactly 6 digits.", + "Enter the 6-digit code found in your authenticator app": "Enter the 6-digit code found in your authenticator app.", "Need help authenticating?": "Need help authenticating?", - "MFA QR Code": "MFA QR Code", + "MFA QR Code": "MFA QR Code.", "Account created successfully. Please log in to set up two-factor authentication.": "Account created successfully. Please log in to set up two-factor authentication.", "Password reset successful. Please log in with your new password and complete two-factor authentication.": "Password reset successful. Please log in with your new password and complete two-factor authentication.", "Password reset successful. Please log in with your new password to set up two-factor authentication.": "Password reset successful. Please log in with your new password to set up two-factor authentication.", "Password reset was successful. Please log in with your new password.": "Password reset was successful. Please log in with your new password.", - "Two-factor authentication": "Two-factor authentication", - "Use authenticator app instead": "Use authenticator app instead", - "Verify backup code": "Verify backup code", - "Use backup code": "Use backup code", - "Enter one of your backup codes": "Enter one of your backup codes", - "Backup code": "Backup code", + "Two-factor authentication": "Two-factor authentication.", + "Use authenticator app instead": "Use authenticator app instead.", + "Verify backup code": "Verify backup code.", + "Use backup code": "Use backup code.", + "Enter one of your backup codes": "Enter one of your backup codes.", + "Backup code": "Backup code.", "Enter one of your backup codes. Each backup code can only be used once.": "Enter one of your backup codes. Each backup code can only be used once.", - "Verify": "Verify", - "Trash": "Trash", + "Verify": "Verify.", + "Trash": "Trash.", "Pages in trash will be permanently deleted after {{count}} days.": "Pages in trash will be permanently deleted after {{count}} days.", - "Deleted": "Deleted", - "No pages in trash": "No pages in trash", + "Deleted": "Deleted.", + "No pages in trash": "No pages in trash.", "Permanently delete page?": "Permanently delete page?", "Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.": "Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.", "Restore '{{title}}' and its sub-pages?": "Restore '{{title}}' and its sub-pages?", - "Move to trash": "Move to trash", + "Move to trash": "Move to trash.", "Move this page to trash?": "Move this page to trash?", - "Restore page": "Restore page", - "Page moved to trash": "Page moved to trash", - "Page restored successfully": "Page restored successfully", - "Deleted by": "Deleted by", - "Deleted at": "Deleted at", - "Preview": "Preview", - "Subpages": "Subpages", - "Failed to load subpages": "Failed to load subpages", - "No subpages": "No subpages", - "Subpages (Child pages)": "Subpages (Child pages)", - "List all subpages of the current page": "List all subpages of the current page", - "Attachments": "Attachments", - "All spaces": "All spaces", - "Unknown": "Unknown", - "Find a space": "Find a space", - "Search in all your spaces": "Search in all your spaces", - "Type": "Type", - "Enterprise": "Enterprise", - "Download attachment": "Download attachment", - "Allowed email domains": "Allowed email domains", - "Only users with email addresses from these domains can signup via SSO.": "Only users with email addresses from these domains can signup via SSO.", - "Enter valid domain names separated by comma or space": "Enter valid domain names separated by comma or space", - "Enforce two-factor authentication": "Enforce two-factor authentication", + "Restore page": "Restore page.", + "Page moved to trash": "Page moved to trash.", + "Page restored successfully": "Page restored successfully.", + "Deleted by": "Deleted by.", + "Deleted at": "Deleted at.", + "Preview": "Preview.", + "Subpages": "Subpages.", + "Failed to load subpages": "Failed to load subpages.", + "No subpages": "No subpages.", + "Subpages (Child pages)": "Subpages (Child pages).", + "List all subpages of the current page": "List all subpages of the current page.", + "Attachments": "Attachments.", + "All spaces": "All spaces.", + "Unknown": "Unknown.", + "Find a space": "Find a space.", + "Search in all your spaces": "Search in all your spaces.", + "Type": "Type.", + "Enterprise": "Enterprise.", + "Download attachment": "Download attachment.", + "Allowed email domains": "Allowed email domains.", + "Only users with email addresses from these domains can signup via SSO.": "Only users with email addresses from these domains can sign up via SSO.", + "Enter valid domain names separated by comma or space": "Enter valid domain names separated by comma or space.", + "Enforce two-factor authentication": "Enforce two-factor authentication.", "Once enforced, all members must enable two-factor authentication to access the workspace.": "Once enforced, all members must enable two-factor authentication to access the workspace.", - "Toggle MFA enforcement": "Toggle MFA enforcement", - "Display name": "Display name", - "Allow signup": "Allow signup", - "Enabled": "Enabled", - "Advanced Settings": "Advanced Settings", - "Enable TLS/SSL": "Enable TLS/SSL", - "Use secure connection to LDAP server": "Use secure connection to LDAP server", - "Group sync": "Group sync", + "Toggle MFA enforcement": "Toggle MFA enforcement.", + "Display name": "Display name.", + "Allow signup": "Allow signup.", + "Enabled": "Enabled.", + "Advanced Settings": "Advanced Settings.", + "Enable TLS/SSL": "Enable TLS/SSL.", + "Use secure connection to LDAP server": "Use secure connection to LDAP server.", + "Group sync": "Group sync.", "No SSO providers found.": "No SSO providers found.", - "Delete SSO provider": "Delete SSO provider", + "Delete SSO provider": "Delete SSO provider.", "Are you sure you want to delete this SSO provider?": "Are you sure you want to delete this SSO provider?", - "Action": "Action", - "{{ssoProviderType}} configuration": "{{ssoProviderType}} configuration", - "Icon": "Icon", - "Upload image": "Upload image", + "Action": "Action.", + "{{ssoProviderType}} configuration": "{{ssoProviderType}} configuration.", + "Icon": "Icon.", + "Upload image": "Upload image.", "Remove image": "Remove image", "Failed to remove image": "Failed to remove image", "Image exceeds 10MB limit.": "Image exceeds 10MB limit.", @@ -660,10 +660,10 @@ "More options": "More options", "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> mentioned you in a comment", "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> commented on a page", - "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> resolved a comment", - "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> mentioned you on a page", - "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> gave you edit access to a page", - "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> gave you view access to a page", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> resolved a comment.", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> mentioned you on a page.", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> gave you edit access to a page.", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> gave you view access to a page.", "Today": "Today", "Yesterday": "Yesterday", "This week": "This week", @@ -698,30 +698,30 @@ "Removed page restriction": "Removed page restriction", "Added page permission": "Added page permission", "Removed page permission": "Removed page permission", - "Verifying your email": "Verifying your email", + "Verifying your email": "Verifying your email.", "Please wait...": "Please wait...", "Verification failed. The link may have expired.": "Verification failed. The link may have expired.", - "Check your email": "Check your email", + "Check your email": "Check your email.", "We sent a verification link to {{email}}.": "We sent a verification link to {{email}}.", "We sent a verification link to your email.": "We sent a verification link to your email.", "Click the link to verify your email and access your workspace.": "Click the link to verify your email and access your workspace.", - "Resend verification email": "Resend verification email", + "Resend verification email": "Resend verification email.", "Verification email sent. Please check your inbox.": "Verification email sent. Please check your inbox.", "Failed to resend verification email. Please try again.": "Failed to resend verification email. Please try again.", "We've sent you an email with your associated workspaces.": "We've sent you an email with your associated workspaces.", - "Load more": "Load more", - "Log out of all devices": "Log out of all devices", - "Log out of all sessions except this device": "Log out of all sessions except this device", - "This Device": "This Device", - "Unknown device": "Unknown device", - "No active sessions": "No active sessions", - "Session revoked": "Session revoked", - "All other sessions revoked": "All other sessions revoked", - "Last used": "Last used", - "Created": "Created", - "Rename": "Rename", - "Publish": "Publish", - "Security": "Security", - "Enforce SSO": "Enforce SSO", - "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to login with email and password." + "Load more": "Load more.", + "Log out of all devices": "Log out of all devices.", + "Log out of all sessions except this device": "Log out of all sessions except this device.", + "This Device": "This Device.", + "Unknown device": "Unknown device.", + "No active sessions": "No active sessions.", + "Session revoked": "Session revoked.", + "All other sessions revoked": "All other sessions revoked.", + "Last used": "Last used.", + "Created": "Created.", + "Rename": "Rename.", + "Publish": "Publish.", + "Security": "Security.", + "Enforce SSO": "Enforce SSO.", + "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to log in with email and password." } diff --git a/apps/client/public/locales/es-ES/translation.json b/apps/client/public/locales/es-ES/translation.json index 875ba3f4..797523d7 100644 --- a/apps/client/public/locales/es-ES/translation.json +++ b/apps/client/public/locales/es-ES/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Guardar y Salir", "Double-click to edit Excalidraw diagram": "Doble clic para editar el diagrama de Excalidraw", "Paste link": "Pegar enlace", + "Paste link or search pages": "Pega un enlace o busca páginas", + "Link to web page": "Enlazar a una página web", + "Recents": "Recientes", + "Page or URL": "Página o URL", + "Link title": "Título del enlace", "Edit link": "Editar enlace", "Remove link": "Eliminar enlace", "Add link": "Agregar enlace", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Alternar el uso compartido público del espacio", "Public sharing is disabled at the workspace level": "El uso compartido público está desactivado a nivel de espacio de trabajo", "Prevent pages in this space from being shared publicly.": "Evitar que las páginas en este espacio se compartan públicamente.", - "Requires an enterprise license": "Requiere una licencia empresarial", "Page permissions": "Permisos de la página},{", "Control who can view and edit individual pages. Available with an enterprise license.": "Controla quién puede ver y editar páginas individuales. Disponible con una licencia empresarial.", "Enable public sharing": "Activar el uso compartido público", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "IA generativa (Preguntar a la IA)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Habilitar la generación de contenido impulsada por IA en el editor. Permite a los usuarios generar, mejorar, traducir y transformar texto.", "Toggle generative AI": "Activar IA generativa", - "Enterprise feature": "Función empresarial", + "Upgrade your plan": "Mejora tu plan", + "Available with a paid license": "Disponible con una licencia de pago", + "Upgrade your license tier.": "Mejora el nivel de tu licencia.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "La IA solo está disponible en la edición empresarial de Docmost. Contacte con sales@docmost.com.", "AI & MCP": "IA y MCP", "AI": "IA", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Protocolo de Contexto del Modelo (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Habilite el servidor MCP para permitir que asistentes de IA y herramientas interactúen con el contenido de su espacio de trabajo.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP solo está disponible en la edición empresarial de Docmost. Contacte con sales@docmost.com.", - "MCP documentation": "Documentación de MCP", "MCP Server URL": "URL del servidor MCP", "Use your API key for authentication. You can manage API keys in your account settings.": "Use su clave API para la autenticación. Puede gestionar las claves API en la configuración de su cuenta.", "Supported tools": "Herramientas compatibles", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Su espacio de trabajo tiene MCP habilitado. Use su clave API para conectar asistentes de IA.", "MCP server URL:": "URL del servidor MCP:", "Learn more": "Más información", - "View the": "Ver la", - "for usage details.": "para detalles de uso.", - "for setup instructions.": "para instrucciones de configuración.", - "API documentation": "Documentación de la API", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gestiona las claves de API para todos los usuarios en el espacio de trabajo. Consulta la <anchor>documentación de la API</anchor> para detalles de uso.", + "View the <anchor>API documentation</anchor> for usage details.": "Consulta la <anchor>documentación de la API</anchor> para detalles de uso.", + "View the <anchor>MCP documentation</anchor>.": "Consulta la <anchor>documentación de MCP</anchor>.", "Sources": "Fuentes", "AI Answers not available for attachments": "Respuestas de IA no disponibles para archivos adjuntos", "No answer available": "No hay respuesta disponible", @@ -654,12 +658,12 @@ "Mark all as read": "Marcar todo como leído", "Mark as read": "Marcar como leído", "More options": "Más opciones", - "mentioned you in a comment": "te mencionó en un comentario", - "commented on a page": "comentó en una página", - "resolved a comment": "resolvió un comentario", - "mentioned you on a page": "te mencionó en una página", - "gave you edit access to a page": "Te dio acceso para editar una página.", - "gave you view access to a page": "Te dio acceso para ver una página.", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> te mencionó en un comentario", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> comentó en una página", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> resolvió un comentario", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> te mencionó en una página", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> te dio acceso de edición a una página", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> te dio acceso de visualización a una página", "Today": "Hoy", "Yesterday": "Ayer", "This week": "Esta semana", @@ -693,5 +697,31 @@ "Failed to update trash retention": "No se pudo actualizar la retención de la papelera.", "Removed page restriction": "Restricción de página eliminada", "Added page permission": "Permiso de página añadido", - "Removed page permission": "Permiso de página eliminado" + "Removed page permission": "Permiso de página eliminado", + "Verifying your email": "Verificando tu correo electrónico", + "Please wait...": "Por favor, espera...", + "Verification failed. The link may have expired.": "La verificación ha fallado. Es posible que el enlace haya expirado.", + "Check your email": "Revisa tu correo electrónico", + "We sent a verification link to {{email}}.": "Te enviamos un enlace de verificación a {{email}}.", + "We sent a verification link to your email.": "Te enviamos un enlace de verificación a tu correo.", + "Click the link to verify your email and access your workspace.": "Haz clic en el enlace para verificar tu correo electrónico y acceder a tu espacio de trabajo.", + "Resend verification email": "Reenviar correo de verificación", + "Verification email sent. Please check your inbox.": "Correo de verificación enviado. Por favor, revisa tu bandeja de entrada.", + "Failed to resend verification email. Please try again.": "No se pudo reenviar el correo de verificación. Por favor, intente de nuevo.", + "We've sent you an email with your associated workspaces.": "Te hemos enviado un correo electrónico con tus espacios de trabajo asociados.", + "Load more": "Cargar más", + "Log out of all devices": "Cerrar sesión en todos los dispositivos", + "Log out of all sessions except this device": "Cerrar sesión en todos los dispositivos excepto este", + "This Device": "Este dispositivo", + "Unknown device": "Dispositivo desconocido", + "No active sessions": "No hay sesiones activas", + "Session revoked": "Sesión revocada", + "All other sessions revoked": "Todas las demás sesiones revocadas", + "Last used": "Último uso", + "Created": "Creado", + "Rename": "Renombrar", + "Publish": "Publicar", + "Security": "Seguridad", + "Enforce SSO": "Forzar SSO", + "Once enforced, members will not be able to login with email and password.": "Una vez forzado, los miembros no podrán iniciar sesión con correo electrónico y contraseña." } diff --git a/apps/client/public/locales/fr-FR/translation.json b/apps/client/public/locales/fr-FR/translation.json index f0e4f7af..d3fdf33f 100644 --- a/apps/client/public/locales/fr-FR/translation.json +++ b/apps/client/public/locales/fr-FR/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Enregistrer & Quitter", "Double-click to edit Excalidraw diagram": "Double-cliquez pour modifier le diagramme Excalidraw", "Paste link": "Coller le lien", + "Paste link or search pages": "Coller le lien ou rechercher des pages", + "Link to web page": "Lien vers une page web", + "Recents": "Récents", + "Page or URL": "Page ou URL", + "Link title": "Titre du lien", "Edit link": "Modifier le lien", "Remove link": "Supprimer le lien", "Add link": "Ajouter un lien", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Basculer le partage public de l'espace", "Public sharing is disabled at the workspace level": "Le partage public est désactivé au niveau de l'espace de travail", "Prevent pages in this space from being shared publicly.": "Empêcher les pages de cet espace d'être partagées publiquement.", - "Requires an enterprise license": "Nécessite une licence d'entreprise", "Page permissions": "Autorisations de la page", "Control who can view and edit individual pages. Available with an enterprise license.": "Contrôlez qui peut consulter et modifier chaque page. Disponible avec une licence Entreprise.", "Enable public sharing": "Activer le partage public", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "IA générative (Demandez à l'IA)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Activer la génération de contenu assistée par IA dans l'éditeur. Permet aux utilisateurs de générer, améliorer, traduire et transformer du texte.", "Toggle generative AI": "Activer/désactiver l'IA générative", - "Enterprise feature": "Fonctionnalité entreprise", + "Upgrade your plan": "Mettez à niveau votre forfait", + "Available with a paid license": "Disponible avec une licence payante", + "Upgrade your license tier.": "Mettez à niveau votre niveau de licence.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "L'IA n'est disponible que dans l'édition Entreprise de Docmost. Contactez sales@docmost.com.", "AI & MCP": "IA & MCP", "AI": "IA", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Protocole de contexte de modèle (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Activez le serveur MCP pour permettre aux assistants et outils IA d'interagir avec le contenu de votre espace de travail.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP n'est disponible que dans l'édition Entreprise de Docmost. Contactez sales@docmost.com.", - "MCP documentation": "Documentation MCP", "MCP Server URL": "URL du serveur MCP", "Use your API key for authentication. You can manage API keys in your account settings.": "Utilisez votre clé API pour l'authentification. Vous pouvez gérer les clés API dans les paramètres de votre compte.", "Supported tools": "Outils pris en charge", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Votre espace de travail a MCP activé. Utilisez votre clé API pour connecter des assistants IA.", "MCP server URL:": "URL du serveur MCP :", "Learn more": "En savoir plus", - "View the": "Voir la", - "for usage details.": "pour les détails d'utilisation.", - "for setup instructions.": "pour les instructions de configuration.", - "API documentation": "Documentation de l'API", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gérez les clés API pour tous les utilisateurs de l'espace de travail. Consultez la <anchor>documentation API</anchor> pour plus de détails sur l'utilisation.", + "View the <anchor>API documentation</anchor> for usage details.": "Consultez la <anchor>documentation API</anchor> pour plus de détails sur l'utilisation.", + "View the <anchor>MCP documentation</anchor>.": "Consultez la <anchor>documentation MCP</anchor>.", "Sources": "Sources", "AI Answers not available for attachments": "Réponses IA non disponibles pour les pièces jointes", "No answer available": "Pas de réponse disponible", @@ -654,12 +658,12 @@ "Mark all as read": "Tout marquer comme lu", "Mark as read": "Marquer comme lu", "More options": "Plus d'options", - "mentioned you in a comment": "vous a mentionné dans un commentaire", - "commented on a page": "a commenté une page", - "resolved a comment": "a résolu un commentaire", - "mentioned you on a page": "vous a mentionné sur une page", - "gave you edit access to a page": "vous a donné l'accès pour modifier une page", - "gave you view access to a page": "vous a donné l'accès pour consulter une page", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> vous a mentionné dans un commentaire", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> a commenté une page", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> a résolu un commentaire", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> vous a mentionné sur une page", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> vous a donné l'accès en modification à une page", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> vous a donné l'accès en lecture à une page", "Today": "Aujourd'hui", "Yesterday": "Hier", "This week": "Cette semaine", @@ -693,5 +697,31 @@ "Failed to update trash retention": "Échec de la mise à jour de la durée de conservation de la corbeille", "Removed page restriction": "Restriction de la page supprimée", "Added page permission": "Autorisation de la page ajoutée", - "Removed page permission": "Autorisation de la page supprimée" + "Removed page permission": "Autorisation de la page supprimée", + "Verifying your email": "Vérification de votre e-mail", + "Please wait...": "Veuillez patienter...", + "Verification failed. The link may have expired.": "Échec de la vérification. Le lien a peut-être expiré.", + "Check your email": "Vérifiez votre e-mail", + "We sent a verification link to {{email}}.": "Nous avons envoyé un lien de vérification à {{email}}.", + "We sent a verification link to your email.": "Nous avons envoyé un lien de vérification à votre adresse e-mail.", + "Click the link to verify your email and access your workspace.": "Cliquez sur le lien pour vérifier votre adresse et accéder à votre espace de travail.", + "Resend verification email": "Renvoyer l'e-mail de vérification", + "Verification email sent. Please check your inbox.": "E-mail de vérification envoyé. Veuillez vérifier votre boîte de réception.", + "Failed to resend verification email. Please try again.": "Échec de l'envoi du nouvel e-mail de vérification. Veuillez réessayer.", + "We've sent you an email with your associated workspaces.": "Nous vous avons envoyé un e-mail avec vos espaces de travail associés.", + "Load more": "Charger plus", + "Log out of all devices": "Déconnexion de tous les appareils", + "Log out of all sessions except this device": "Déconnexion de toutes les sessions sauf cet appareil", + "This Device": "Cet appareil", + "Unknown device": "Appareil inconnu", + "No active sessions": "Aucune session active", + "Session revoked": "Session révoquée", + "All other sessions revoked": "Toutes les autres sessions révoquées", + "Last used": "Dernière utilisation", + "Created": "Créé", + "Rename": "Renommer", + "Publish": "Publier", + "Security": "Sécurité", + "Enforce SSO": "Imposer SSO", + "Once enforced, members will not be able to login with email and password.": "Une fois imposé, les membres ne pourront plus se connecter par e-mail et mot de passe." } diff --git a/apps/client/public/locales/it-IT/translation.json b/apps/client/public/locales/it-IT/translation.json index 78a13f5c..fdcaa8d1 100644 --- a/apps/client/public/locales/it-IT/translation.json +++ b/apps/client/public/locales/it-IT/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Salva ed esci", "Double-click to edit Excalidraw diagram": "Fai doppio clic per modificare il diagramma di Excalidraw", "Paste link": "Incolla link", + "Paste link or search pages": "Incolla il link o cerca le pagine", + "Link to web page": "Collega a una pagina web", + "Recents": "Recenti", + "Page or URL": "Pagina o URL", + "Link title": "Titolo del link", "Edit link": "Modifica link", "Remove link": "Rimuovi link", "Add link": "Aggiungi link", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Attiva/disattiva la condivisione pubblica nello spazio", "Public sharing is disabled at the workspace level": "La condivisione pubblica è disabilitata a livello di area di lavoro", "Prevent pages in this space from being shared publicly.": "Impedisci che le pagine in questo spazio vengano condivise pubblicamente.", - "Requires an enterprise license": "Richiede una licenza enterprise", "Page permissions": "Autorizzazioni della pagina.", "Control who can view and edit individual pages. Available with an enterprise license.": "Controlla chi può visualizzare e modificare le singole pagine. Disponibile con una licenza Enterprise.", "Enable public sharing": "Abilita la condivisione pubblica", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "AI generativa (Chiedi AI)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Abilita la generazione di contenuti con AI nell'editor. Consente agli utenti di generare, migliorare, tradurre e trasformare il testo.", "Toggle generative AI": "Attiva/Disattiva AI generativa", - "Enterprise feature": "Funzionalità Enterprise", + "Upgrade your plan": "Aggiorna il tuo piano", + "Available with a paid license": "Disponibile con una licenza a pagamento", + "Upgrade your license tier.": "Aggiorna il livello della tua licenza.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "L'IA è disponibile solo nell'edizione Enterprise di Docmost. Contatta sales@docmost.com.", "AI & MCP": "IA e MCP", "AI": "IA", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Model Context Protocol (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Abilita il server MCP per consentire ad assistenti e strumenti IA di interagire con i contenuti del tuo spazio di lavoro.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP è disponibile solo nell'edizione Enterprise di Docmost. Contatta sales@docmost.com.", - "MCP documentation": "Documentazione MCP", "MCP Server URL": "URL del server MCP", "Use your API key for authentication. You can manage API keys in your account settings.": "Usa la tua chiave API per l'autenticazione. Puoi gestire le chiavi API nelle impostazioni del tuo account.", "Supported tools": "Strumenti supportati", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Il tuo spazio di lavoro ha MCP abilitato. Usa la tua chiave API per collegare gli assistenti IA.", "MCP server URL:": "URL del server MCP:", "Learn more": "Scopri di più", - "View the": "Visualizza la", - "for usage details.": "per i dettagli sull'utilizzo.", - "for setup instructions.": "per le istruzioni di configurazione.", - "API documentation": "Documentazione API", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gestisci le API key per tutti gli utenti nello spazio di lavoro. Consulta la <anchor>documentazione API</anchor> per i dettagli sull'utilizzo.", + "View the <anchor>API documentation</anchor> for usage details.": "Consulta la <anchor>documentazione API</anchor> per i dettagli sull'utilizzo.", + "View the <anchor>MCP documentation</anchor>.": "Consulta la <anchor>documentazione MCP</anchor>.", "Sources": "Fonti", "AI Answers not available for attachments": "Risposte AI non disponibili per gli allegati", "No answer available": "Nessuna risposta disponibile", @@ -654,12 +658,12 @@ "Mark all as read": "Segna tutto come letto", "Mark as read": "Segna come letto", "More options": "Altre opzioni", - "mentioned you in a comment": "ti ha menzionato in un commento", - "commented on a page": "ha commentato una pagina", - "resolved a comment": "ha risolto un commento", - "mentioned you on a page": "ti ha menzionato in una pagina", - "gave you edit access to a page": "ti ha concesso l'accesso per modificare una pagina", - "gave you view access to a page": "ti ha concesso l'accesso per visualizzare una pagina", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> ti ha menzionato in un commento", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> ha commentato una pagina", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> ha risolto un commento", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> ti ha menzionato su una pagina", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> ti ha dato l'accesso di modifica a una pagina", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> ti ha dato l'accesso di visualizzazione a una pagina", "Today": "Oggi", "Yesterday": "Ieri", "This week": "Questa settimana", @@ -693,5 +697,31 @@ "Failed to update trash retention": "Impossibile aggiornare la conservazione del cestino", "Removed page restriction": "Restrizione della pagina rimossa", "Added page permission": "Permesso sulla pagina aggiunto", - "Removed page permission": "Permesso sulla pagina rimosso" + "Removed page permission": "Permesso sulla pagina rimosso", + "Verifying your email": "Verifica della tua email", + "Please wait...": "Attendere...", + "Verification failed. The link may have expired.": "Verifica non riuscita. Il link potrebbe essere scaduto.", + "Check your email": "Controlla la tua email", + "We sent a verification link to {{email}}.": "Abbiamo inviato un link di verifica a {{email}}.", + "We sent a verification link to your email.": "Abbiamo inviato un link di verifica alla tua email.", + "Click the link to verify your email and access your workspace.": "Clicca sul link per verificare la tua email e accedere al tuo workspace.", + "Resend verification email": "Invia nuovamente l'email di verifica", + "Verification email sent. Please check your inbox.": "Email di verifica inviata. Controlla la tua casella di posta.", + "Failed to resend verification email. Please try again.": "Invio dell'email di verifica non riuscito. Si prega di riprovare.", + "We've sent you an email with your associated workspaces.": "Ti abbiamo inviato un'email con i workspace associati.", + "Load more": "Carica altro", + "Log out of all devices": "Disconnetti da tutti i dispositivi", + "Log out of all sessions except this device": "Disconnetti da tutte le sessioni tranne questo dispositivo", + "This Device": "Questo dispositivo", + "Unknown device": "Dispositivo sconosciuto", + "No active sessions": "Nessuna sessione attiva", + "Session revoked": "Sessione revocata", + "All other sessions revoked": "Tutte le altre sessioni revocate", + "Last used": "Ultimo utilizzo", + "Created": "Creato", + "Rename": "Rinomina", + "Publish": "Pubblica", + "Security": "Sicurezza", + "Enforce SSO": "Forza SSO", + "Once enforced, members will not be able to login with email and password.": "Una volta attivata, i membri non potranno più accedere con email e password." } diff --git a/apps/client/public/locales/ja-JP/translation.json b/apps/client/public/locales/ja-JP/translation.json index 09f10ab0..b14adb53 100644 --- a/apps/client/public/locales/ja-JP/translation.json +++ b/apps/client/public/locales/ja-JP/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "保存して終了", "Double-click to edit Excalidraw diagram": "ダブルクリックして Excalidraw 図を編集", "Paste link": "リンクを貼り付け", + "Paste link or search pages": "リンクを貼り付けるかページを検索してください。 ", + "Link to web page": "ウェブページへのリンク", + "Recents": "最近使用したもの", + "Page or URL": "ページまたはURL", + "Link title": "リンクタイトル", "Edit link": "リンクを編集", "Remove link": "リンクを削除", "Add link": "リンクを追加", @@ -439,7 +444,6 @@ "Toggle space public sharing": "スペースの公開共有を切り替える", "Public sharing is disabled at the workspace level": "ワークスペースレベルで公開共有が無効になっています", "Prevent pages in this space from being shared publicly.": "このスペース内のページが公開で共有されるのを防ぐ。", - "Requires an enterprise license": "エンタープライズライセンスが必要です", "Page permissions": "ページのアクセス権", "Control who can view and edit individual pages. Available with an enterprise license.": "個々のページを誰が表示・編集できるかを制御します。エンタープライズライセンスで利用可能です。", "Enable public sharing": "公開共有を有効にする", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "生成AI (Ask AI)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "エディターでAIを活用したコンテンツ生成を有効にします。ユーザーがテキストの生成、改善、翻訳、および変換を行うことができます。", "Toggle generative AI": "生成AIを切り替える", - "Enterprise feature": "エンタープライズ機能", + "Upgrade your plan": "プランをアップグレードする", + "Available with a paid license": "有料ライセンスで利用可能", + "Upgrade your license tier.": "ライセンスタイアをアップグレードしてください。", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI は Docmost のエンタープライズ版でのみ利用可能です。sales@docmost.com までお問い合わせください。", "AI & MCP": "AI と MCP", "AI": "AI", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "モデルコンテキストプロトコル(MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "MCP サーバーを有効にして、AI アシスタントやツールがワークスペースのコンテンツとやり取りできるようにします。", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP は Docmost のエンタープライズ版でのみ利用可能です。sales@docmost.com までお問い合わせください。", - "MCP documentation": "MCP ドキュメント", "MCP Server URL": "MCP サーバーの URL", "Use your API key for authentication. You can manage API keys in your account settings.": "認証には API キーを使用してください。API キーはアカウント設定で管理できます。", "Supported tools": "サポートされているツール", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "このワークスペースでは MCP が有効になっています。AI アシスタントを接続するには API キーを使用してください。", "MCP server URL:": "MCP サーバーの URL:", "Learn more": "詳細を見る", - "View the": "表示", - "for usage details.": "使用方法の詳細については。", - "for setup instructions.": "設定手順については。", - "API documentation": "API ドキュメント", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "ワークスペース内のすべてのユーザーのAPIキーを管理します。利用方法の詳細は<anchor>APIドキュメント</anchor>をご覧ください。", + "View the <anchor>API documentation</anchor> for usage details.": "利用方法の詳細は<anchor>APIドキュメント</anchor>をご覧ください。", + "View the <anchor>MCP documentation</anchor>.": "<anchor>MCPドキュメント</anchor>をご覧ください。", "Sources": "ソース", "AI Answers not available for attachments": "添付ファイルにはAI回答を利用できません", "No answer available": "回答がありません", @@ -654,12 +658,12 @@ "Mark all as read": "すべてを既読にする", "Mark as read": "既読にする", "More options": "その他のオプション", - "mentioned you in a comment": "コメントであなたに言及しました", - "commented on a page": "ページにコメントしました", - "resolved a comment": "コメントを解決しました", - "mentioned you on a page": "ページ上であなたに言及しました", - "gave you edit access to a page": "あなたにページの編集アクセス権を付与しました", - "gave you view access to a page": "あなたにページの閲覧アクセス権を付与しました", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold>さんがコメントであなたに言及しました", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold>さんがページにコメントしました", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold>さんがコメントを解決しました", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold>さんがページであなたに言及しました", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold>さんがページの編集権限をあなたに付与しました", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold>さんがページの閲覧権限をあなたに付与しました", "Today": "今日", "Yesterday": "昨日", "This week": "今週", @@ -693,5 +697,31 @@ "Failed to update trash retention": "ゴミ箱保持期間の更新に失敗しました", "Removed page restriction": "ページの制限を解除しました", "Added page permission": "ページの権限を追加しました", - "Removed page permission": "ページの権限を削除しました" + "Removed page permission": "ページの権限を削除しました", + "Verifying your email": "メールを確認中", + "Please wait...": "お待ちください…", + "Verification failed. The link may have expired.": "認証に失敗しました。リンクの有効期限が切れている可能性があります。", + "Check your email": "メールを確認してください", + "We sent a verification link to {{email}}.": "確認用リンクを{{email}}に送信しました。", + "We sent a verification link to your email.": "確認用リンクをあなたのメールアドレスに送信しました。", + "Click the link to verify your email and access your workspace.": "リンクをクリックしてメールを認証し、ワークスペースにアクセスしてください。", + "Resend verification email": "確認メールを再送信", + "Verification email sent. Please check your inbox.": "確認メールを送信しました。受信箱をご確認ください。", + "Failed to resend verification email. Please try again.": "確認メールの再送信に失敗しました。もう一度お試しください。", + "We've sent you an email with your associated workspaces.": "紐づいているワークスペース情報をメールでお送りしました。", + "Load more": "もっと見る", + "Log out of all devices": "すべての端末からログアウト", + "Log out of all sessions except this device": "この端末以外の全セッションからログアウト", + "This Device": "このデバイス", + "Unknown device": "不明な端末", + "No active sessions": "アクティブなセッションはありません", + "Session revoked": "セッションが取り消されました", + "All other sessions revoked": "他のすべてのセッションが取り消されました", + "Last used": "最終使用", + "Created": "作成日", + "Rename": "名前を変更", + "Publish": "公開する", + "Security": "セキュリティ", + "Enforce SSO": "SSOを強制する", + "Once enforced, members will not be able to login with email and password.": "一度SSOが強制されると、メールとパスワードでログインできなくなります。" } diff --git a/apps/client/public/locales/ko-KR/translation.json b/apps/client/public/locales/ko-KR/translation.json index eb763685..8bc26c55 100644 --- a/apps/client/public/locales/ko-KR/translation.json +++ b/apps/client/public/locales/ko-KR/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "저장 후 나가기", "Double-click to edit Excalidraw diagram": "Excalidraw diagram을 편집하려면 더블 클릭하세요", "Paste link": "링크 붙여넣기", + "Paste link or search pages": "링크를 붙여넣거나 페이지를 검색", + "Link to web page": "웹페이지에 링크하기", + "Recents": "최근 항목", + "Page or URL": "페이지 또는 URL", + "Link title": "링크 제목", "Edit link": "링크 수정", "Remove link": "링크 제거", "Add link": "링크 추가", @@ -439,7 +444,6 @@ "Toggle space public sharing": "공간 공유 전환", "Public sharing is disabled at the workspace level": "워크스페이스 수준에서 공유가 비활성화되었습니다.", "Prevent pages in this space from being shared publicly.": "이 공간의 페이지가 공개적으로 공유되지 않도록 방지하십시오.", - "Requires an enterprise license": "기업 라이센스가 필요합니다.", "Page permissions": "페이지 권한},{", "Control who can view and edit individual pages. Available with an enterprise license.": "개별 페이지의 조회 및 편집 권한을 제어합니다. 엔터프라이즈 라이선스에서 이용 가능합니다.", "Enable public sharing": "공유 활성화", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "생성 AI (Ask AI)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "편집기에서 AI 구동 콘텐츠 생성을 활성화합니다. 사용자가 텍스트를 생성, 개선, 번역 및 변환할 수 있습니다.", "Toggle generative AI": "생성 AI 토글", - "Enterprise feature": "엔터프라이즈 기능", + "Upgrade your plan": "요금제를 업그레이드하세요", + "Available with a paid license": "유료 라이선스에서만 사용 가능합니다", + "Upgrade your license tier.": "라이선스 등급을 업그레이드하세요.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI는 Docmost 엔터프라이즈 에디션에서만 제공됩니다. sales@docmost.com으로 문의하세요.", "AI & MCP": "AI 및 MCP", "AI": "AI", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "모델 컨텍스트 프로토콜(MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "AI 어시스턴트와 도구가 워크스페이스 콘텐츠와 상호작용할 수 있도록 MCP 서버를 활성화하세요.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP는 Docmost 엔터프라이즈 에디션에서만 제공됩니다. sales@docmost.com으로 문의하세요.", - "MCP documentation": "MCP 문서", "MCP Server URL": "MCP 서버 URL", "Use your API key for authentication. You can manage API keys in your account settings.": "인증을 위해 API 키를 사용하세요. API 키는 계정 설정에서 관리할 수 있습니다.", "Supported tools": "지원되는 도구", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "워크스페이스에 MCP가 활성화되어 있습니다. AI 어시스턴트를 연결하려면 API 키를 사용하세요.", "MCP server URL:": "MCP 서버 URL:", "Learn more": "자세히 알아보기", - "View the": "다음을", - "for usage details.": "에서 사용 방법을 확인하세요.", - "for setup instructions.": "에서 설정 지침을 확인하세요.", - "API documentation": "API 문서", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "워크스페이스의 모든 사용자를 위한 API 키를 관리하세요. 사용 방법은 <anchor>API 문서</anchor>를 참고하세요.", + "View the <anchor>API documentation</anchor> for usage details.": "사용 방법은 <anchor>API 문서</anchor>를 참고하세요.", + "View the <anchor>MCP documentation</anchor>.": "<anchor>MCP 문서</anchor>를 확인하세요.", "Sources": "출처", "AI Answers not available for attachments": "첨부 파일에 대해 AI 답변을 사용할 수 없습니다", "No answer available": "답변을 제공할 수 없습니다", @@ -654,12 +658,12 @@ "Mark all as read": "모두 읽음으로 표시", "Mark as read": "읽음으로 표시", "More options": "추가 옵션", - "mentioned you in a comment": "댓글에서 당신을 언급했습니다", - "commented on a page": "페이지에 댓글을 달았습니다", - "resolved a comment": "댓글을 해결했습니다", - "mentioned you on a page": "페이지에서 당신을 언급했습니다", - "gave you edit access to a page": "페이지 편집 권한을 부여했습니다", - "gave you view access to a page": "페이지 보기 권한을 부여했습니다", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold>님이 댓글에서 당신을 언급했습니다", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold>님이 페이지에 댓글을 남겼습니다", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold>님이 댓글을 해결했습니다", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold>님이 페이지에서 당신을 언급했습니다", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold>님이 페이지 편집 권한을 부여했습니다", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold>님이 페이지 조회 권한을 부여했습니다", "Today": "오늘", "Yesterday": "어제", "This week": "이번 주", @@ -693,5 +697,31 @@ "Failed to update trash retention": "휴지통 보관 기간 업데이트에 실패했습니다.", "Removed page restriction": "페이지 제한이 제거됨", "Added page permission": "페이지 권한이 추가됨", - "Removed page permission": "페이지 권한이 제거됨" + "Removed page permission": "페이지 권한이 제거됨", + "Verifying your email": "이메일 인증 중", + "Please wait...": "잠시만 기다려 주세요...", + "Verification failed. The link may have expired.": "인증에 실패했습니다. 링크가 만료되었을 수 있습니다.", + "Check your email": "이메일을 확인하세요", + "We sent a verification link to {{email}}.": "{{email}} 주소로 인증 링크를 보냈습니다.", + "We sent a verification link to your email.": "이메일로 인증 링크를 보냈습니다.", + "Click the link to verify your email and access your workspace.": "이메일의 링크를 클릭하여 인증하고 워크스페이스에 접속하세요.", + "Resend verification email": "인증 이메일 재전송", + "Verification email sent. Please check your inbox.": "인증 이메일이 전송되었습니다. 받은 편지함을 확인하세요.", + "Failed to resend verification email. Please try again.": "인증 이메일 재전송에 실패했습니다. 다시 시도해 주세요.", + "We've sent you an email with your associated workspaces.": "연결된 워크스페이스 목록이 포함된 이메일을 보내드렸습니다.", + "Load more": "더 불러오기", + "Log out of all devices": "모든 기기에서 로그아웃", + "Log out of all sessions except this device": "이 기기를 제외한 모든 세션에서 로그아웃", + "This Device": "이 기기", + "Unknown device": "알 수 없는 기기", + "No active sessions": "활성 세션이 없습니다", + "Session revoked": "세션이 해제되었습니다", + "All other sessions revoked": "나머지 모든 세션이 해제되었습니다", + "Last used": "최근 사용", + "Created": "생성됨", + "Rename": "이름 바꾸기", + "Publish": "게시", + "Security": "보안", + "Enforce SSO": "SSO 강제 적용", + "Once enforced, members will not be able to login with email and password.": "강제 적용 시, 멤버는 이메일과 비밀번호로는 로그인할 수 없습니다." } diff --git a/apps/client/public/locales/nl-NL/translation.json b/apps/client/public/locales/nl-NL/translation.json index eb414753..d6eb9d5e 100644 --- a/apps/client/public/locales/nl-NL/translation.json +++ b/apps/client/public/locales/nl-NL/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Opslaan & Afsluiten", "Double-click to edit Excalidraw diagram": "Dubbelklik om Excalidraw diagram te bewerken", "Paste link": "Link plakken", + "Paste link or search pages": "Plak link of zoek pagina's", + "Link to web page": "Link naar webpagina", + "Recents": "Recent", + "Page or URL": "Pagina of URL", + "Link title": "Kop van de link", "Edit link": "Link bewerken", "Remove link": "Link verwijderen", "Add link": "Link toevoegen", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Wissel openbaar delen van ruimte", "Public sharing is disabled at the workspace level": "Openbaar delen is uitgeschakeld op werkruimteniveau", "Prevent pages in this space from being shared publicly.": "Voorkom dat pagina's in deze ruimte openbaar worden gedeeld.", - "Requires an enterprise license": "Vereist een bedrijfslicentie", "Page permissions": "Pagina rechten", "Control who can view and edit individual pages. Available with an enterprise license.": "Beheer wie individuele pagina's kan bekijken en bewerken. Beschikbaar met een Enterprise-licentie.", "Enable public sharing": "Openbaar delen inschakelen", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "Generatieve AI (Vraag het AI)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Schakel AI-gestuurde inhoudsgeneratie in de editor in. Hiermee kunnen gebruikers tekst genereren, verbeteren, vertalen en transformeren.", "Toggle generative AI": "Generatieve AI schakelen", - "Enterprise feature": "Enterprise-functie", + "Upgrade your plan": "Upgrade je abonnement", + "Available with a paid license": "Beschikbaar met een betaalde licentie", + "Upgrade your license tier.": "Upgrade je licentieniveau.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI is alleen beschikbaar in de Docmost Enterprise-editie. Neem contact op met sales@docmost.com.", "AI & MCP": "AI & MCP", "AI": "AI", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Model Context Protocol (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Schakel de MCP-server in zodat AI-assistenten en tools kunnen interageren met de inhoud van uw werkruimte.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP is alleen beschikbaar in de Docmost Enterprise-editie. Neem contact op met sales@docmost.com.", - "MCP documentation": "MCP-documentatie", "MCP Server URL": "MCP-server-URL", "Use your API key for authentication. You can manage API keys in your account settings.": "Gebruik uw API-sleutel voor authenticatie. U kunt API-sleutels beheren in uw accountinstellingen.", "Supported tools": "Ondersteunde tools", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "In uw werkruimte is MCP ingeschakeld. Gebruik uw API-sleutel om AI-assistenten te koppelen.", "MCP server URL:": "MCP-server-URL:", "Learn more": "Meer informatie", - "View the": "Bekijk de", - "for usage details.": "voor details over het gebruik.", - "for setup instructions.": "voor installatie-instructies.", - "API documentation": "API-documentatie", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Beheer API-sleutels voor alle gebruikers in de werkruimte. Bekijk de <anchor>API-documentatie</anchor> voor gebruiksdetails.", + "View the <anchor>API documentation</anchor> for usage details.": "Bekijk de <anchor>API-documentatie</anchor> voor gebruiksdetails.", + "View the <anchor>MCP documentation</anchor>.": "Bekijk de <anchor>MCP-documentatie</anchor>.", "Sources": "Bronnen", "AI Answers not available for attachments": "AI Antwoorden niet beschikbaar voor bijlagen", "No answer available": "Geen antwoord beschikbaar", @@ -654,12 +658,12 @@ "Mark all as read": "Markeer alles als gelezen", "Mark as read": "Markeer als gelezen", "More options": "Meer opties", - "mentioned you in a comment": "noemde je in een reactie", - "commented on a page": "reageerde op een pagina", - "resolved a comment": "heeft een opmerking opgelost", - "mentioned you on a page": "noemde je op een pagina", - "gave you edit access to a page": "heeft je toegang gegeven om een pagina te bewerken", - "gave you view access to a page": "heeft je toegang gegeven om een pagina te bekijken", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> noemde je in een reactie", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> heeft een reactie geplaatst op een pagina", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> heeft een reactie opgelost", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> noemde je op een pagina", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> heeft je toegang gegeven om een pagina te bewerken", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> heeft je toegang gegeven om een pagina te bekijken", "Today": "Vandaag", "Yesterday": "Gisteren", "This week": "Deze week", @@ -693,5 +697,31 @@ "Failed to update trash retention": "Bijwerken van de bewaartermijn voor de prullenbak is mislukt.", "Removed page restriction": "Pagina-restrictie verwijderd", "Added page permission": "Paginatoestemming toegevoegd", - "Removed page permission": "Paginatoestemming verwijderd" + "Removed page permission": "Paginatoestemming verwijderd", + "Verifying your email": "Je e-mailadres wordt geverifieerd", + "Please wait...": "Even geduld...", + "Verification failed. The link may have expired.": "Verificatie mislukt. De link is mogelijk verlopen.", + "Check your email": "Controleer je e-mail", + "We sent a verification link to {{email}}.": "We hebben een verificatielink naar {{email}} gestuurd.", + "We sent a verification link to your email.": "We hebben een verificatielink naar je e-mailadres gestuurd.", + "Click the link to verify your email and access your workspace.": "Klik op de link om je e-mailadres te verifiëren en toegang te krijgen tot je werkruimte.", + "Resend verification email": "Verificatie-e-mail opnieuw verzenden", + "Verification email sent. Please check your inbox.": "Verificatie-e-mail verzonden. Controleer je inbox.", + "Failed to resend verification email. Please try again.": "Het verzenden van de verificatie-e-mail is mislukt. Probeer het opnieuw.", + "We've sent you an email with your associated workspaces.": "We hebben je een e-mail gestuurd met je gekoppelde werkruimtes.", + "Load more": "Meer laden", + "Log out of all devices": "Log uit op alle apparaten", + "Log out of all sessions except this device": "Log uit op alle sessies behalve dit apparaat", + "This Device": "Dit apparaat", + "Unknown device": "Onbekend apparaat", + "No active sessions": "Geen actieve sessies", + "Session revoked": "Sessie ingetrokken", + "All other sessions revoked": "Alle andere sessies ingetrokken", + "Last used": "Laatst gebruikt", + "Created": "Aangemaakt", + "Rename": "Hernoemen", + "Publish": "Publiceren", + "Security": "Beveiliging", + "Enforce SSO": "SSO afdwingen", + "Once enforced, members will not be able to login with email and password.": "Zodra dit is afgedwongen, kunnen leden niet meer inloggen met e-mail en wachtwoord." } diff --git a/apps/client/public/locales/pt-BR/translation.json b/apps/client/public/locales/pt-BR/translation.json index 961e9a8f..94750861 100644 --- a/apps/client/public/locales/pt-BR/translation.json +++ b/apps/client/public/locales/pt-BR/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Salvar e Sair", "Double-click to edit Excalidraw diagram": "Clique duas vezes para editar o diagrama Excalidraw", "Paste link": "Colar link", + "Paste link or search pages": "Cole o link ou pesquise páginas", + "Link to web page": "Link para página da web", + "Recents": "Recentes", + "Page or URL": "Página ou URL", + "Link title": "Título do link", "Edit link": "Editar link", "Remove link": "Remover link", "Add link": "Adicionar link", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Alternar compartilhamento público do espaço", "Public sharing is disabled at the workspace level": "O compartilhamento público está desativado no nível do espaço de trabalho", "Prevent pages in this space from being shared publicly.": "Impedir que as páginas neste espaço sejam compartilhadas publicamente.", - "Requires an enterprise license": "Requer uma licença empresarial", "Page permissions": "Permissões da página},{", "Control who can view and edit individual pages. Available with an enterprise license.": "Controle quem pode visualizar e editar páginas individuais. Disponível com licença empresarial.", "Enable public sharing": "Ativar compartilhamento público", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "IA generativa (Perguntar à IA)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Habilitar geração de conteúdo com IA no editor. Permite aos usuários gerar, melhorar, traduzir e transformar texto.", "Toggle generative AI": "Alternar IA generativa", - "Enterprise feature": "Recurso empresarial", + "Upgrade your plan": "Faça upgrade do seu plano", + "Available with a paid license": "Disponível com uma licença paga", + "Upgrade your license tier.": "Faça upgrade do seu nível de licença.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "A IA está disponível apenas na edição empresarial do Docmost. Contate sales@docmost.com.", "AI & MCP": "IA e MCP", "AI": "IA", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Protocolo de Contexto de Modelo (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Ative o servidor MCP para permitir que assistentes de IA e ferramentas interajam com o conteúdo do seu espaço de trabalho.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "O MCP está disponível apenas na edição empresarial do Docmost. Contate sales@docmost.com.", - "MCP documentation": "Documentação do MCP", "MCP Server URL": "URL do servidor MCP", "Use your API key for authentication. You can manage API keys in your account settings.": "Use sua chave de API para autenticação. Você pode gerenciar chaves de API nas configurações da sua conta.", "Supported tools": "Ferramentas compatíveis", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "Seu espaço de trabalho tem MCP habilitado. Use sua chave de API para conectar assistentes de IA.", "MCP server URL:": "URL do servidor MCP:", "Learn more": "Saiba mais", - "View the": "Veja o", - "for usage details.": "para detalhes de uso.", - "for setup instructions.": "para instruções de configuração.", - "API documentation": "Documentação da API", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Gerencie as chaves de API de todos os usuários do workspace. Veja a <anchor>documentação da API</anchor> para detalhes de uso.", + "View the <anchor>API documentation</anchor> for usage details.": "Veja a <anchor>documentação da API</anchor> para detalhes de uso.", + "View the <anchor>MCP documentation</anchor>.": "Veja a <anchor>documentação MCP</anchor>.", "Sources": "Fontes", "AI Answers not available for attachments": "Respostas de IA não disponíveis para anexos", "No answer available": "Nenhuma resposta disponível", @@ -654,12 +658,12 @@ "Mark all as read": "Marcar todas como lidas", "Mark as read": "Marcar como lida", "More options": "Mais opções", - "mentioned you in a comment": "mencionou você em um comentário", - "commented on a page": "comentou em uma página", - "resolved a comment": "resolveu um comentário", - "mentioned you on a page": "mencionou você em uma página", - "gave you edit access to a page": "concedeu a você acesso para editar a página", - "gave you view access to a page": "concedeu a você acesso para visualizar a página", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> mencionou você em um comentário", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> comentou em uma página", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> resolveu um comentário", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> mencionou você em uma página", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> concedeu acesso de edição a uma página", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> concedeu acesso de visualização a uma página", "Today": "Hoje", "Yesterday": "Ontem", "This week": "Esta semana", @@ -693,5 +697,31 @@ "Failed to update trash retention": "Falha ao atualizar a retenção da lixeira", "Removed page restriction": "Restrição de página removida", "Added page permission": "Permissão de página adicionada", - "Removed page permission": "Permissão de página removida" + "Removed page permission": "Permissão de página removida", + "Verifying your email": "Verificando seu e-mail", + "Please wait...": "Por favor, aguarde...", + "Verification failed. The link may have expired.": "Falha na verificação. O link pode ter expirado.", + "Check your email": "Verifique seu e-mail", + "We sent a verification link to {{email}}.": "Enviamos um link de verificação para {{email}}.", + "We sent a verification link to your email.": "Enviamos um link de verificação para seu e-mail.", + "Click the link to verify your email and access your workspace.": "Clique no link para verificar seu e-mail e acessar seu workspace.", + "Resend verification email": "Reenviar e-mail de verificação", + "Verification email sent. Please check your inbox.": "E-mail de verificação enviado. Por favor, verifique sua caixa de entrada.", + "Failed to resend verification email. Please try again.": "Falha ao reenviar o e-mail de verificação. Por favor, tente novamente.", + "We've sent you an email with your associated workspaces.": "Enviamos um e-mail para você com seus workspaces associados.", + "Load more": "Carregar mais", + "Log out of all devices": "Sair de todos os dispositivos", + "Log out of all sessions except this device": "Sair de todas as sessões, exceto neste dispositivo", + "This Device": "Este dispositivo", + "Unknown device": "Dispositivo desconhecido", + "No active sessions": "Sem sessões ativas", + "Session revoked": "Sessão revogada", + "All other sessions revoked": "Todas as outras sessões revogadas", + "Last used": "Último uso", + "Created": "Criado", + "Rename": "Renomear", + "Publish": "Publicar", + "Security": "Segurança", + "Enforce SSO": "Exigir SSO", + "Once enforced, members will not be able to login with email and password.": "Uma vez exigido, os membros não poderão entrar com e-mail e senha." } diff --git a/apps/client/public/locales/ru-RU/translation.json b/apps/client/public/locales/ru-RU/translation.json index e6a14df8..f602bdd6 100644 --- a/apps/client/public/locales/ru-RU/translation.json +++ b/apps/client/public/locales/ru-RU/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Сохранить и выйти", "Double-click to edit Excalidraw diagram": "Кликните дважды для редактирования диаграммы Excalidraw", "Paste link": "Вставить ссылку", + "Paste link or search pages": "Вставьте ссылку или найдите страницы", + "Link to web page": "Ссылка на веб-страницу", + "Recents": "Недавние", + "Page or URL": "Страница или URL", + "Link title": "Заголовок ссылки", "Edit link": "Редактировать ссылку", "Remove link": "Удалить ссылку", "Add link": "Добавить ссылку", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Переключить общий доступ для пространства", "Public sharing is disabled at the workspace level": "Общий доступ отключен на уровне рабочего пространства", "Prevent pages in this space from being shared publicly.": "Запретить делиться страницами в этом пространстве публично.", - "Requires an enterprise license": "Требуется корпоративная лицензия", "Page permissions": "Права доступа к странице},{", "Control who can view and edit individual pages. Available with an enterprise license.": "Контролируйте, кто может просматривать и редактировать отдельные страницы. Доступно при наличии лицензии Enterprise.", "Enable public sharing": "Включить общий доступ", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "Генеративный ИИ (Спросить ИИ)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Включите создание контента на базе ИИ в редакторе. Позволяет пользователям генерировать, улучшать, переводить и преобразовывать текст.", "Toggle generative AI": "Переключить генеративный ИИ", - "Enterprise feature": "Корпоративная функция", + "Upgrade your plan": "Обновите свой тарифный план", + "Available with a paid license": "Доступно с платной лицензией", + "Upgrade your license tier.": "Обновите уровень вашей лицензии.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "ИИ доступен только в корпоративной версии Docmost. Свяжитесь по адресу sales@docmost.com.", "AI & MCP": "ИИ и MCP", "AI": "ИИ", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Протокол контекста модели (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Включите сервер MCP, чтобы ИИ-ассистенты и инструменты могли взаимодействовать с содержимым вашего рабочего пространства.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP доступен только в корпоративной версии Docmost. Свяжитесь по адресу sales@docmost.com.", - "MCP documentation": "Документация MCP", "MCP Server URL": "URL сервера MCP", "Use your API key for authentication. You can manage API keys in your account settings.": "Используйте ваш API-ключ для аутентификации. Управлять API-ключами можно в настройках аккаунта.", "Supported tools": "Поддерживаемые инструменты", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "В вашем рабочем пространстве включён MCP. Используйте свой API-ключ для подключения ИИ-ассистентов.", "MCP server URL:": "URL сервера MCP:", "Learn more": "Подробнее", - "View the": "Просмотреть", - "for usage details.": "для подробностей использования.", - "for setup instructions.": "для инструкций по настройке.", - "API documentation": "Документация API", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Управляйте API-ключами для всех пользователей в рабочем пространстве. Смотрите <anchor>документацию по API</anchor> для получения информации об использовании.", + "View the <anchor>API documentation</anchor> for usage details.": "Смотрите <anchor>документацию по API</anchor> для получения информации об использовании.", + "View the <anchor>MCP documentation</anchor>.": "Смотрите <anchor>документацию по MCP</anchor>.", "Sources": "Источники", "AI Answers not available for attachments": "Ответы ИИ недоступны для вложений", "No answer available": "Ответ недоступен", @@ -654,12 +658,12 @@ "Mark all as read": "Отметить все как прочитанные", "Mark as read": "Отметить как прочитанное", "More options": "Больше возможностей", - "mentioned you in a comment": "упомянул вас в комментарии", - "commented on a page": "прокомментировал на странице", - "resolved a comment": "разрешил комментарий", - "mentioned you on a page": "упомянул вас на странице", - "gave you edit access to a page": "предоставил вам доступ на редактирование страницы", - "gave you view access to a page": "предоставил вам доступ для просмотра страницы", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> упомянул вас в комментарии", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> оставил комментарий на странице", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> решил комментарий", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> упомянул вас на странице", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> предоставил вам доступ для редактирования страницы", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> предоставил вам доступ к просмотру страницы", "Today": "Сегодня", "Yesterday": "Вчера", "This week": "На этой неделе", @@ -693,5 +697,31 @@ "Failed to update trash retention": "Не удалось обновить срок хранения корзины", "Removed page restriction": "Ограничение доступа к странице удалено", "Added page permission": "Добавлено разрешение доступа к странице", - "Removed page permission": "Удалено разрешение доступа к странице" + "Removed page permission": "Удалено разрешение доступа к странице", + "Verifying your email": "Проверка вашей электронной почты", + "Please wait...": "Пожалуйста, подождите...", + "Verification failed. The link may have expired.": "Ошибка проверки. Ссылка могла устареть.", + "Check your email": "Проверьте вашу электронную почту", + "We sent a verification link to {{email}}.": "Мы отправили ссылку для подтверждения на {{email}}.", + "We sent a verification link to your email.": "Мы отправили ссылку для подтверждения на вашу электронную почту.", + "Click the link to verify your email and access your workspace.": "Перейдите по ссылке, чтобы подтвердить электронную почту и получить доступ к рабочему пространству.", + "Resend verification email": "Отправить письмо для подтверждения повторно", + "Verification email sent. Please check your inbox.": "Письмо для подтверждения отправлено. Пожалуйста, проверьте ваш почтовый ящик.", + "Failed to resend verification email. Please try again.": "Не удалось отправить письмо для подтверждения. Пожалуйста, попробуйте снова.", + "We've sent you an email with your associated workspaces.": "Мы отправили вам электронное письмо с привязанными рабочими пространствами.", + "Load more": "Загрузить ещё", + "Log out of all devices": "Выйти со всех устройств", + "Log out of all sessions except this device": "Выйти из всех сессий, кроме этого устройства", + "This Device": "Это устройство", + "Unknown device": "Неизвестное устройство", + "No active sessions": "Нет активных сессий", + "Session revoked": "Сессия отозвана", + "All other sessions revoked": "Все другие сессии отозваны", + "Last used": "Последнее использование", + "Created": "Создано", + "Rename": "Переименовать", + "Publish": "Опубликовать", + "Security": "Безопасность", + "Enforce SSO": "Принудительно использовать SSO", + "Once enforced, members will not be able to login with email and password.": "После включения участники не смогут войти с помощью электронной почты и пароля." } diff --git a/apps/client/public/locales/uk-UA/translation.json b/apps/client/public/locales/uk-UA/translation.json index 83f96bb7..96ec50a9 100644 --- a/apps/client/public/locales/uk-UA/translation.json +++ b/apps/client/public/locales/uk-UA/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "Зберегти та вийти", "Double-click to edit Excalidraw diagram": "Клацніть двічі для редагування діаграми Excalidraw", "Paste link": "Вставити посилання", + "Paste link or search pages": "Вставте посилання або знайдіть сторінки", + "Link to web page": "Посилання на веб-сторінку", + "Recents": "Нещодавні", + "Page or URL": "Сторінка або URL", + "Link title": "Назва посилання", "Edit link": "Редагувати посилання", "Remove link": "Видалити посилання", "Add link": "Додати посилання", @@ -439,7 +444,6 @@ "Toggle space public sharing": "Перемикання публічного доступу до просторів", "Public sharing is disabled at the workspace level": "Публічний доступ вимкнуто на рівні робочого простору", "Prevent pages in this space from being shared publicly.": "Перешкодити публічному поширенню сторінок у цьому просторі.", - "Requires an enterprise license": "Потребує корпоративної ліцензії", "Page permissions": "Права доступу до сторінки.", "Control who can view and edit individual pages. Available with an enterprise license.": "Керуйте тим, хто може переглядати та редагувати окремі сторінки. Доступно з корпоративною ліцензією.", "Enable public sharing": "Увімкнути публічний доступ", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "Генеративний ШІ (Запитати ШІ)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "Увімкнути генерацію контенту за допомогою ШІ в редакторі. Дозволяє користувачам генерувати, покращувати, перекладати та трансформувати текст.", "Toggle generative AI": "Переключити генеративний ШІ", - "Enterprise feature": "Функція корпоративної версії", + "Upgrade your plan": "Оновіть свій тарифний план", + "Available with a paid license": "Доступно за платною ліцензією", + "Upgrade your license tier.": "Оновіть рівень своєї ліцензії.", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "ШІ доступний лише в корпоративній редакції Docmost. Зверніться до sales@docmost.com.", "AI & MCP": "ШІ та MCP", "AI": "ШІ", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "Протокол контексту моделі (MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Увімкніть MCP‑сервер, щоб дозволити ШІ‑помічникам та інструментам взаємодіяти з вмістом вашого робочого простору.", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP доступний лише в корпоративній редакції Docmost. Зверніться до sales@docmost.com.", - "MCP documentation": "Документація MCP", "MCP Server URL": "URL сервера MCP", "Use your API key for authentication. You can manage API keys in your account settings.": "Використовуйте свій API‑ключ для аутентифікації. Ви можете керувати API‑ключами в налаштуваннях облікового запису.", "Supported tools": "Підтримувані інструменти", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "У вашому робочому просторі MCP увімкнено. Використайте свій API‑ключ, щоб підключити ШІ‑помічників.", "MCP server URL:": "URL сервера MCP:", "Learn more": "Дізнатися більше", - "View the": "Переглянути", - "for usage details.": "для відомостей про використання.", - "for setup instructions.": "для інструкцій з налаштування.", - "API documentation": "Документація API", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "Керуйте ключами API для всіх користувачів у робочому просторі. Перегляньте <anchor>документацію API</anchor> для деталей використання.", + "View the <anchor>API documentation</anchor> for usage details.": "Перегляньте <anchor>документацію API</anchor> для деталей використання.", + "View the <anchor>MCP documentation</anchor>.": "Перегляньте <anchor>документацію MCP</anchor>.", "Sources": "Джерела", "AI Answers not available for attachments": "Відповіді ШІ недоступні для вкладень", "No answer available": "Відповідь недоступна", @@ -654,12 +658,12 @@ "Mark all as read": "Позначити все як прочитане", "Mark as read": "Позначити як прочитане", "More options": "Більше опцій", - "mentioned you in a comment": "згадали вас у коментарі", - "commented on a page": "прокоментували на сторінці", - "resolved a comment": "вирішили коментар", - "mentioned you on a page": "згадали вас на сторінці", - "gave you edit access to a page": "надав вам доступ для редагування сторінки", - "gave you view access to a page": "надав вам доступ для перегляду сторінки", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold> згадав вас у коментарі", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold> залишив коментар на сторінці", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold> вирішив коментар", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> згадав вас на сторінці", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> надав вам доступ до редагування сторінки", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> надав вам доступ до перегляду сторінки", "Today": "Сьогодні", "Yesterday": "Вчора", "This week": "Цього тижня", @@ -693,5 +697,31 @@ "Failed to update trash retention": "Не вдалося оновити термін зберігання у кошику", "Removed page restriction": "Обмеження сторінки видалено", "Added page permission": "Додано дозвіл на сторінку", - "Removed page permission": "Дозвіл на сторінку видалено" + "Removed page permission": "Дозвіл на сторінку видалено", + "Verifying your email": "Підтвердження вашої електронної пошти", + "Please wait...": "Будь ласка, зачекайте...", + "Verification failed. The link may have expired.": "Підтвердження не вдалося. Посилання могло втратити чинність.", + "Check your email": "Перевірте свою електронну пошту", + "We sent a verification link to {{email}}.": "Ми надіслали посилання для підтвердження на {{email}}.", + "We sent a verification link to your email.": "Ми надіслали посилання для підтвердження на вашу електронну пошту.", + "Click the link to verify your email and access your workspace.": "Клікніть на посилання, щоб підтвердити електронну пошту та отримати доступ до робочого простору.", + "Resend verification email": "Повторно надіслати лист для підтвердження", + "Verification email sent. Please check your inbox.": "Лист для підтвердження надіслано. Будь ласка, перевірте свою скриньку.", + "Failed to resend verification email. Please try again.": "Не вдалося повторно надіслати лист для підтвердження. Будь ласка, спробуйте ще раз.", + "We've sent you an email with your associated workspaces.": "Ми надіслали вам лист із переліком пов’язаних робочих просторів.", + "Load more": "Завантажити ще", + "Log out of all devices": "Вийти з усіх пристроїв", + "Log out of all sessions except this device": "Вийти з усіх сесій, окрім цього пристрою", + "This Device": "Цей пристрій", + "Unknown device": "Невідомий пристрій", + "No active sessions": "Немає активних сесій", + "Session revoked": "Сесію скасовано", + "All other sessions revoked": "Всі інші сесії скасовано", + "Last used": "Останнє використання", + "Created": "Створено", + "Rename": "Перейменувати", + "Publish": "Опублікувати", + "Security": "Безпека", + "Enforce SSO": "Вимагати SSO", + "Once enforced, members will not be able to login with email and password.": "Після активування учасники не зможуть увійти за допомогою електронної пошти та паролю." } diff --git a/apps/client/public/locales/zh-CN/translation.json b/apps/client/public/locales/zh-CN/translation.json index 95883800..9ff28f8e 100644 --- a/apps/client/public/locales/zh-CN/translation.json +++ b/apps/client/public/locales/zh-CN/translation.json @@ -289,6 +289,11 @@ "Save & Exit": "保存并退出", "Double-click to edit Excalidraw diagram": "双击以编辑 Excalidraw 图表", "Paste link": "粘贴链接", + "Paste link or search pages": "粘贴链接或搜索页面", + "Link to web page": "链接到网页", + "Recents": "最近使用", + "Page or URL": "页面或网址", + "Link title": "链接标题", "Edit link": "编辑链接", "Remove link": "移除链接", "Add link": "添加链接", @@ -439,7 +444,6 @@ "Toggle space public sharing": "切换空间公开分享", "Public sharing is disabled at the workspace level": "公开分享在工作区级别被禁用", "Prevent pages in this space from being shared publicly.": "阻止此空间中的页面被公开分享。", - "Requires an enterprise license": "需要企业许可证", "Page permissions": "页面权限},{", "Control who can view and edit individual pages. Available with an enterprise license.": "控制谁可以查看和编辑单个页面。此功能在企业版许可下可用。", "Enable public sharing": "启用公开分享", @@ -621,7 +625,9 @@ "Generative AI (Ask AI)": "生成型AI (询问AI)", "Enable AI-powered content generation in the editor. Allows users to generate, improve, translate and transform text.": "在编辑器中启用AI驱动的内容生成。允许用户生成、改进、翻译和转换文本。", "Toggle generative AI": "切换生成型AI", - "Enterprise feature": "企业版功能", + "Upgrade your plan": "升级您的方案", + "Available with a paid license": "需付费许可才可用", + "Upgrade your license tier.": "升级您的许可等级。", "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI 仅在 Docmost 企业版中提供。请联系 sales@docmost.com。", "AI & MCP": "AI 与 MCP", "AI": "AI", @@ -629,17 +635,15 @@ "Model Context Protocol (MCP)": "模型上下文协议(MCP)", "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "启用 MCP 服务器以允许 AI 助手和工具与您的工作区内容交互。", "MCP is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "MCP 仅在 Docmost 企业版中提供。请联系 sales@docmost.com。", - "MCP documentation": "MCP 文档", "MCP Server URL": "MCP 服务器 URL", "Use your API key for authentication. You can manage API keys in your account settings.": "使用您的 API 密钥进行身份验证。您可以在账户设置中管理 API 密钥。", "Supported tools": "支持的工具", "Your workspace has MCP enabled. Use your API key to connect AI assistants.": "您的工作区已启用 MCP。使用您的 API 密钥连接 AI 助手。", "MCP server URL:": "MCP 服务器 URL:", "Learn more": "了解更多", - "View the": "查看", - "for usage details.": "以获取使用详情。", - "for setup instructions.": "以获取设置说明。", - "API documentation": "API 文档", + "Manage API keys for all users in the workspace. View the <anchor>API documentation</anchor> for usage details.": "为工作区内所有用户管理 API 密钥。有关使用详情,请查阅<anchor>API 文档</anchor>。", + "View the <anchor>API documentation</anchor> for usage details.": "有关使用详情,请查阅<anchor>API 文档</anchor>。", + "View the <anchor>MCP documentation</anchor>.": "查看<anchor>MCP 文档</anchor>。", "Sources": "来源", "AI Answers not available for attachments": "AI答案不适用于附件", "No answer available": "无可用答案", @@ -654,12 +658,12 @@ "Mark all as read": "标记所有为已读", "Mark as read": "标记为已读", "More options": "更多选项", - "mentioned you in a comment": "在评论中提到你", - "commented on a page": "在页面上评论", - "resolved a comment": "解决了一个评论", - "mentioned you on a page": "在页面上提到你", - "gave you edit access to a page": "已授予你编辑该页面的权限", - "gave you view access to a page": "已授予你查看该页面的权限", + "<bold>{{name}}</bold> mentioned you in a comment": "<bold>{{name}}</bold>在评论中提到你", + "<bold>{{name}}</bold> commented on a page": "<bold>{{name}}</bold>在页面上评论了", + "<bold>{{name}}</bold> resolved a comment": "<bold>{{name}}</bold>已解决一条评论", + "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold>在页面上提到你", + "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold>授予你页面编辑权限", + "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold>授予你页面查看权限", "Today": "今天", "Yesterday": "昨天", "This week": "本周", @@ -693,5 +697,31 @@ "Failed to update trash retention": "更新垃圾箱保留期失败", "Removed page restriction": "已移除页面限制", "Added page permission": "已添加页面权限", - "Removed page permission": "已移除页面权限" + "Removed page permission": "已移除页面权限", + "Verifying your email": "正在验证您的邮箱", + "Please wait...": "请稍候……", + "Verification failed. The link may have expired.": "验证失败。该链接可能已过期。", + "Check your email": "查看您的邮箱", + "We sent a verification link to {{email}}.": "我们已向{{email}}发送了一封验证邮件。", + "We sent a verification link to your email.": "我们已向您的邮箱发送了一封验证邮件。", + "Click the link to verify your email and access your workspace.": "请点击链接以验证邮箱并访问您的工作区。", + "Resend verification email": "重新发送验证邮件", + "Verification email sent. Please check your inbox.": "验证邮件已发送。请检查您的收件箱。", + "Failed to resend verification email. Please try again.": "重新发送验证邮件失败。请重试。", + "We've sent you an email with your associated workspaces.": "我们已向您发送包含关联工作区的邮件。", + "Load more": "加载更多", + "Log out of all devices": "退出所有设备登录", + "Log out of all sessions except this device": "除本设备外,退出所有会话", + "This Device": "本设备", + "Unknown device": "未知设备", + "No active sessions": "无活动会话", + "Session revoked": "会话已被撤销", + "All other sessions revoked": "所有其他会话已被撤销", + "Last used": "上次使用", + "Created": "创建时间", + "Rename": "重命名", + "Publish": "发布", + "Security": "安全性", + "Enforce SSO": "强制启用 SSO", + "Once enforced, members will not be able to login with email and password.": "一旦强制,成员将无法用邮箱和密码登录。" } From 7981ef462e5d1d78b83e358f6ee03a299ba18524 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:33:29 +0000 Subject: [PATCH 16/44] feat(editor): audio and PDF nodes (#2064) * use local resizable * feat: aduio * support audio imports * feat: use confluence real file names * cleanup * error handling * hide notice * add audio * fix pulse * Fix import and export * unify pulse * hide in readonly mode * keywords * keyword * translations * better sort * feat: PDF embed * cleanup * remove audio menu * open active * hide focus on readonly mode * increase iframe default dimension --- .../public/locales/en-US/translation.json | 11 +- .../components/page-share-modal.tsx | 5 +- .../components/attachment/attachment-view.tsx | 53 +- .../editor/components/audio/audio-menu.tsx | 123 ++ .../components/audio/audio-view.module.css | 37 + .../editor/components/audio/audio-view.tsx | 65 + .../components/audio/upload-audio-action.tsx | 36 + .../common/editor-paste-handler.tsx | 5 + .../components/common/node-resize-handles.ts | 2 +- .../common/resizable-wrapper.module.css | 8 +- .../components/common/resizable-wrapper.tsx | 9 + .../editor/components/embed/embed-view.tsx | 9 +- .../components/image/image-view.module.css | 3 + .../editor/components/image/image-view.tsx | 1 + .../editor/components/pdf/pdf-menu.tsx | 145 +++ .../editor/components/pdf/pdf-view.module.css | 100 ++ .../editor/components/pdf/pdf-view.tsx | 168 +++ .../components/pdf/upload-pdf-action.tsx | 36 + .../components/slash-menu/menu-items.ts | 84 +- .../components/video/video-view.module.css | 4 + .../editor/components/video/video-view.tsx | 1 + .../features/editor/extensions/extensions.ts | 10 + .../src/features/editor/page-editor.tsx | 2 + .../src/features/editor/styles/core.css | 8 + .../src/features/editor/styles/media.css | 25 +- apps/client/src/i18n.ts | 1 + .../src/collaboration/collaboration.util.ts | 4 + .../src/common/helpers/prosemirror/utils.ts | 2 + .../core/attachment/attachment.constants.ts | 5 + .../core/attachment/attachment.controller.ts | 4 + .../services/import-attachment.service.ts | 76 +- .../integrations/import/utils/import.utils.ts | 13 + .../storage/drivers/local.driver.ts | 22 +- package.json | 4 +- packages/editor-ext/src/index.ts | 4 + .../editor-ext/src/lib/audio/audio-upload.ts | 139 +++ packages/editor-ext/src/lib/audio/audio.ts | 134 ++ packages/editor-ext/src/lib/audio/index.ts | 2 + packages/editor-ext/src/lib/drawio.ts | 10 +- packages/editor-ext/src/lib/embed.ts | 4 +- packages/editor-ext/src/lib/excalidraw.ts | 10 +- packages/editor-ext/src/lib/image/image.ts | 9 +- packages/editor-ext/src/lib/pdf/index.ts | 2 + packages/editor-ext/src/lib/pdf/pdf-upload.ts | 123 ++ packages/editor-ext/src/lib/pdf/pdf.ts | 156 +++ .../editor-ext/src/lib/resizable-nodeview.ts | 1098 +++++++++++++++++ packages/editor-ext/src/lib/utils.ts | 6 + packages/editor-ext/src/lib/video/video.ts | 10 +- pnpm-lock.yaml | 291 ++--- 49 files changed, 2870 insertions(+), 209 deletions(-) create mode 100644 apps/client/src/features/editor/components/audio/audio-menu.tsx create mode 100644 apps/client/src/features/editor/components/audio/audio-view.module.css create mode 100644 apps/client/src/features/editor/components/audio/audio-view.tsx create mode 100644 apps/client/src/features/editor/components/audio/upload-audio-action.tsx create mode 100644 apps/client/src/features/editor/components/pdf/pdf-menu.tsx create mode 100644 apps/client/src/features/editor/components/pdf/pdf-view.module.css create mode 100644 apps/client/src/features/editor/components/pdf/pdf-view.tsx create mode 100644 apps/client/src/features/editor/components/pdf/upload-pdf-action.tsx create mode 100644 packages/editor-ext/src/lib/audio/audio-upload.ts create mode 100644 packages/editor-ext/src/lib/audio/audio.ts create mode 100644 packages/editor-ext/src/lib/audio/index.ts create mode 100644 packages/editor-ext/src/lib/pdf/index.ts create mode 100644 packages/editor-ext/src/lib/pdf/pdf-upload.ts create mode 100644 packages/editor-ext/src/lib/pdf/pdf.ts create mode 100644 packages/editor-ext/src/lib/resizable-nodeview.ts diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index e057d15e..68bef0f2 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Insert horizontal rule divider", "Upload any image from your device.": "Upload any image from your device.", "Upload any video from your device.": "Upload any video from your device.", + "Upload any audio from your device.": "Upload any audio from your device.", "Upload any file from your device.": "Upload any file from your device.", "Uploading {{name}}": "Uploading {{name}}", "Uploading file": "Uploading file", @@ -351,6 +352,12 @@ "Divider": "Divider.", "Quote": "Quote.", "Image": "Image.", + "Audio": "Audio.", + "Embed PDF": "Embed PDF", + "Upload and embed a PDF file.": "Upload and embed a PDF file.", + "Embed as PDF": "Embed as PDF", + "Failed to load PDF": "Failed to load PDF", + "Convert to attachment": "Convert to attachment", "File attachment": "File attachment.", "Toggle block": "Toggle block.", "Callout": "Callout.", @@ -723,5 +730,7 @@ "Publish": "Publish.", "Security": "Security.", "Enforce SSO": "Enforce SSO.", - "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to log in with email and password." + "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to log in with email and password.", + "Uploading {{name}}": "Uploading {{name}}", + "Uploading file": "Uploading file" } diff --git a/apps/client/src/ee/page-permission/components/page-share-modal.tsx b/apps/client/src/ee/page-permission/components/page-share-modal.tsx index 01b91a7c..0a19b318 100644 --- a/apps/client/src/ee/page-permission/components/page-share-modal.tsx +++ b/apps/client/src/ee/page-permission/components/page-share-modal.tsx @@ -71,7 +71,10 @@ export function PageShareModal({ readOnly }: PageShareModalProps) { ) : null } variant="default" - onClick={open} + onClick={() => { + setActiveTab(isPubliclyShared ? "publish" : hasPagePermissions ? "access" : "publish"); + open(); + }} > {t("Share")} </Button> diff --git a/apps/client/src/features/editor/components/attachment/attachment-view.tsx b/apps/client/src/features/editor/components/attachment/attachment-view.tsx index e3281e64..f6c13c80 100644 --- a/apps/client/src/features/editor/components/attachment/attachment-view.tsx +++ b/apps/client/src/features/editor/components/attachment/attachment-view.tsx @@ -1,17 +1,43 @@ import { NodeViewProps, NodeViewWrapper } from "@tiptap/react"; -import { Group, Text, Paper, ActionIcon, Loader } from "@mantine/core"; +import { Group, Text, Paper, ActionIcon, Loader, Tooltip } from "@mantine/core"; import { getFileUrl } from "@/lib/config.ts"; -import { IconDownload, IconPaperclip } from "@tabler/icons-react"; +import { IconDownload, IconFileTypePdf, IconPaperclip } from "@tabler/icons-react"; import { useHover } from "@mantine/hooks"; import { formatBytes } from "@/lib"; import { useTranslation } from "react-i18next"; +import { useCallback } from "react"; export default function AttachmentView(props: NodeViewProps) { const { t } = useTranslation(); - const { node, selected } = props; - const { url, name, size } = node.attrs; + const { editor, node, getPos, selected } = props; + const { url, name, size, mime, attachmentId } = node.attrs; const { hovered, ref } = useHover(); + const isPdf = mime === "application/pdf" || name?.toLowerCase().endsWith(".pdf"); + + const handleEmbedAsPdf = useCallback(() => { + const pos = getPos(); + if (pos === undefined || !url) return; + + const nodeSize = node.nodeSize; + + editor + .chain() + .insertContentAt( + { from: pos, to: pos + nodeSize }, + { + type: "pdf", + attrs: { + src: url, + name, + attachmentId, + size, + }, + }, + ) + .run(); + }, [editor, getPos, node, url, name, attachmentId]); + return ( <NodeViewWrapper> <Paper withBorder p="4px" ref={ref} data-drag-handle> @@ -39,11 +65,20 @@ export default function AttachmentView(props: NodeViewProps) { </Group> {url && (selected || hovered) && ( - <a href={getFileUrl(url)} target="_blank"> - <ActionIcon variant="default" aria-label="download file"> - <IconDownload size={18} /> - </ActionIcon> - </a> + <Group gap={4} wrap="nowrap" style={{ flexShrink: 0 }}> + {isPdf && editor.isEditable && ( + <Tooltip label={t("Embed as PDF")} position="top" withinPortal={false}> + <ActionIcon variant="default" aria-label={t("Embed as PDF")} onClick={handleEmbedAsPdf}> + <IconFileTypePdf size={18} /> + </ActionIcon> + </Tooltip> + )} + <a href={getFileUrl(url)} target="_blank"> + <ActionIcon variant="default" aria-label="download file"> + <IconDownload size={18} /> + </ActionIcon> + </a> + </Group> )} </Group> </Paper> diff --git a/apps/client/src/features/editor/components/audio/audio-menu.tsx b/apps/client/src/features/editor/components/audio/audio-menu.tsx new file mode 100644 index 00000000..3ca1950d --- /dev/null +++ b/apps/client/src/features/editor/components/audio/audio-menu.tsx @@ -0,0 +1,123 @@ +import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus"; +import { findParentNode, posToDOMRect, useEditorState } from "@tiptap/react"; +import { useCallback } from "react"; +import { Node as PMNode } from "@tiptap/pm/model"; +import { + EditorMenuProps, + ShouldShowProps, +} from "@/features/editor/components/table/types/types.ts"; +import { ActionIcon, Tooltip } from "@mantine/core"; +import { + IconDownload, + IconTrash, +} from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import { getFileUrl } from "@/lib/config.ts"; +import classes from "../common/toolbar-menu.module.css"; + +export function AudioMenu({ editor }: EditorMenuProps) { + const { t } = useTranslation(); + + const editorState = useEditorState({ + editor, + selector: (ctx) => { + if (!ctx.editor) { + return null; + } + + const audioAttrs = ctx.editor.getAttributes("audio"); + + return { + isAudio: ctx.editor.isActive("audio"), + src: audioAttrs?.src || null, + }; + }, + }); + + const shouldShow = useCallback( + ({ state }: ShouldShowProps) => { + if (!state) { + return false; + } + + return editor.isActive("audio") && editor.getAttributes("audio").src; + }, + [editor], + ); + + const getReferencedVirtualElement = useCallback(() => { + if (!editor) return; + const { selection } = editor.state; + const predicate = (node: PMNode) => node.type.name === "audio"; + const parent = findParentNode(predicate)(selection); + + if (parent) { + const dom = editor.view.nodeDOM(parent?.pos) as HTMLElement; + const domRect = dom.getBoundingClientRect(); + return { + getBoundingClientRect: () => domRect, + getClientRects: () => [domRect], + }; + } + + const domRect = posToDOMRect(editor.view, selection.from, selection.to); + return { + getBoundingClientRect: () => domRect, + getClientRects: () => [domRect], + }; + }, [editor]); + + const handleDownload = useCallback(() => { + if (!editorState?.src) return; + const url = getFileUrl(editorState.src); + const a = document.createElement("a"); + a.href = url; + a.download = ""; + a.click(); + }, [editorState?.src]); + + const handleDelete = useCallback(() => { + editor.commands.deleteSelection(); + }, [editor]); + + return ( + <BaseBubbleMenu + editor={editor} + pluginKey={`audio-menu`} + updateDelay={0} + getReferencedVirtualElement={getReferencedVirtualElement} + options={{ + placement: "top", + offset: 8, + flip: false, + }} + shouldShow={shouldShow} + > + <div className={classes.toolbar}> + <Tooltip position="top" label={t("Download")} withinPortal={false}> + <ActionIcon + onClick={handleDownload} + size="lg" + aria-label={t("Download")} + variant="subtle" + > + <IconDownload size={18} /> + </ActionIcon> + </Tooltip> + + <Tooltip position="top" label={t("Delete")} withinPortal={false}> + <ActionIcon + onClick={handleDelete} + size="lg" + aria-label={t("Delete")} + variant="subtle" + > + <IconTrash size={18} /> + </ActionIcon> + </Tooltip> + </div> + </BaseBubbleMenu> + ); +} + +export default AudioMenu; diff --git a/apps/client/src/features/editor/components/audio/audio-view.module.css b/apps/client/src/features/editor/components/audio/audio-view.module.css new file mode 100644 index 00000000..5773ea82 --- /dev/null +++ b/apps/client/src/features/editor/components/audio/audio-view.module.css @@ -0,0 +1,37 @@ +.audioWrapper { + display: flex; + justify-content: center; + align-items: center; + max-width: 100%; + border-radius: 8px; + overflow: hidden; +} + +.skeleton { + animation: pulse 1.2s ease-in-out infinite; + + @mixin light { + background: linear-gradient(-90deg, var(--mantine-color-gray-3) 0%, var(--mantine-color-gray-1) 50%, var(--mantine-color-gray-3) 100%); + background-size: 400% 400%; + } + + @mixin dark { + background: linear-gradient(-90deg, var(--mantine-color-dark-6) 0%, var(--mantine-color-dark-5) 50%, var(--mantine-color-dark-6) 100%); + background-size: 400% 400%; + } + + @keyframes pulse { + 0% { + background-position: 0% 0%; + } + 100% { + background-position: -135% 0%; + } + } +} + +.audio { + display: block; + width: 100%; + border-radius: 8px; +} diff --git a/apps/client/src/features/editor/components/audio/audio-view.tsx b/apps/client/src/features/editor/components/audio/audio-view.tsx new file mode 100644 index 00000000..a353ce45 --- /dev/null +++ b/apps/client/src/features/editor/components/audio/audio-view.tsx @@ -0,0 +1,65 @@ +import { NodeViewProps, NodeViewWrapper } from "@tiptap/react"; +import { Group, Loader, Text } from "@mantine/core"; +import { useMemo } from "react"; +import { getFileUrl } from "@/lib/config.ts"; +import { isInternalFileUrl } from "@docmost/editor-ext"; +import classes from "./audio-view.module.css"; +import { useTranslation } from "react-i18next"; + +export default function AudioView(props: NodeViewProps) { + const { t } = useTranslation(); + const { editor, node } = props; + const { src, placeholder } = node.attrs; + + const safeSrc = useMemo(() => { + if (!src || !isInternalFileUrl(src)) return null; + return getFileUrl(src); + }, [src]); + + const previewSrc = useMemo(() => { + editor.storage.shared.audioPreviews = + editor.storage.shared.audioPreviews || {}; + + if (placeholder?.id) { + return editor.storage.shared.audioPreviews[placeholder.id]; + } + + return null; + }, [placeholder, editor]); + + return ( + <NodeViewWrapper data-drag-handle> + <div className={`${classes.audioWrapper} ${!safeSrc ? classes.skeleton : ''}`}> + {safeSrc && ( + <audio + className={classes.audio} + preload="metadata" + controls + src={safeSrc} + /> + )} + {!safeSrc && previewSrc && ( + <Group pos="relative" w="100%"> + <audio + className={classes.audio} + preload="metadata" + controls + src={previewSrc} + /> + <Loader size={20} pos="absolute" top={6} right={6} /> + </Group> + )} + {!safeSrc && !previewSrc && ( + <Group justify="center" wrap="nowrap" gap="xs" maw="100%" px="md" h={54}> + <Loader size={20} style={{ flexShrink: 0 }} /> + <Text component="span" size="sm" truncate="end"> + {placeholder?.name + ? t("Uploading {{name}}", { name: placeholder.name }) + : t("Uploading file")} + </Text> + </Group> + )} + </div> + </NodeViewWrapper> + ); +} diff --git a/apps/client/src/features/editor/components/audio/upload-audio-action.tsx b/apps/client/src/features/editor/components/audio/upload-audio-action.tsx new file mode 100644 index 00000000..e3df5775 --- /dev/null +++ b/apps/client/src/features/editor/components/audio/upload-audio-action.tsx @@ -0,0 +1,36 @@ +import { handleAudioUpload } from "@docmost/editor-ext"; +import { uploadFile } from "@/features/page/services/page-service.ts"; +import { notifications } from "@mantine/notifications"; +import { getFileUploadSizeLimit } from "@/lib/config.ts"; +import { formatBytes } from "@/lib"; +import i18n from "@/i18n.ts"; + +export const uploadAudioAction = handleAudioUpload({ + onUpload: async (file: File, pageId: string): Promise<any> => { + try { + return await uploadFile(file, pageId); + } catch (err) { + notifications.show({ + color: "red", + message: err?.response.data.message, + }); + throw err; + } + }, + validateFn: (file) => { + if (!file.type.includes("audio/")) { + return false; + } + + if (file.size > getFileUploadSizeLimit()) { + notifications.show({ + color: "red", + message: i18n.t("File exceeds the {{limit}} attachment limit", { + limit: formatBytes(getFileUploadSizeLimit()), + }), + }); + return false; + } + return true; + }, +}); diff --git a/apps/client/src/features/editor/components/common/editor-paste-handler.tsx b/apps/client/src/features/editor/components/common/editor-paste-handler.tsx index 6407d835..85d49872 100644 --- a/apps/client/src/features/editor/components/common/editor-paste-handler.tsx +++ b/apps/client/src/features/editor/components/common/editor-paste-handler.tsx @@ -1,6 +1,7 @@ import { uploadImageAction } from "@/features/editor/components/image/upload-image-action.tsx"; import { uploadVideoAction } from "@/features/editor/components/video/upload-video-action.tsx"; import { uploadAttachmentAction } from "../attachment/upload-attachment-action"; +import { uploadPdfAction } from "../pdf/upload-pdf-action"; import { createMentionAction } from "@/features/editor/components/link/internal-link-paste.ts"; import { INTERNAL_LINK_REGEX } from "@/lib/constants.ts"; import { Editor } from "@tiptap/core"; @@ -12,6 +13,8 @@ import { const ATTACHMENT_NODE_TYPES = [ "image", "video", + "audio", + "pdf", "attachment", "excalidraw", "drawio", @@ -63,6 +66,7 @@ export const handlePaste = ( const pos = editor.state.selection.from; uploadImageAction(file, editor, pos, pageId); uploadVideoAction(file, editor, pos, pageId); + uploadPdfAction(file, editor, pos, pageId); uploadAttachmentAction(file, editor, pos, pageId); } return true; @@ -229,6 +233,7 @@ export const handleFileDrop = ( uploadImageAction(file, editor, coordinates?.pos ?? 0 - 1, pageId); uploadVideoAction(file, editor, coordinates?.pos ?? 0 - 1, pageId); + uploadPdfAction(file, editor, coordinates?.pos ?? 0 - 1, pageId); uploadAttachmentAction(file, editor, coordinates?.pos ?? 0 - 1, pageId); } return true; diff --git a/apps/client/src/features/editor/components/common/node-resize-handles.ts b/apps/client/src/features/editor/components/common/node-resize-handles.ts index 0785845d..38e10a9a 100644 --- a/apps/client/src/features/editor/components/common/node-resize-handles.ts +++ b/apps/client/src/features/editor/components/common/node-resize-handles.ts @@ -1,5 +1,5 @@ -import type { ResizableNodeViewDirection } from "@tiptap/core"; import classes from "./node-resize.module.css"; +import { ResizableNodeViewDirection } from "@docmost/editor-ext"; export function createResizeHandle( direction: ResizableNodeViewDirection, diff --git a/apps/client/src/features/editor/components/common/resizable-wrapper.module.css b/apps/client/src/features/editor/components/common/resizable-wrapper.module.css index 0d0a7688..edfa8c3b 100644 --- a/apps/client/src/features/editor/components/common/resizable-wrapper.module.css +++ b/apps/client/src/features/editor/components/common/resizable-wrapper.module.css @@ -20,8 +20,8 @@ .cornerHandle { position: absolute; - width: 36px; - height: 36px; + width: 24px; + height: 24px; z-index: 2; opacity: 0; transition: opacity 0.2s ease; @@ -42,13 +42,13 @@ } &::before { - width: 28px; + width: 20px; height: 3px; } &::after { width: 3px; - height: 28px; + height: 20px; } &:hover::before, diff --git a/apps/client/src/features/editor/components/common/resizable-wrapper.tsx b/apps/client/src/features/editor/components/common/resizable-wrapper.tsx index ebb9cd78..69a5058b 100644 --- a/apps/client/src/features/editor/components/common/resizable-wrapper.tsx +++ b/apps/client/src/features/editor/components/common/resizable-wrapper.tsx @@ -74,6 +74,15 @@ export const ResizableWrapper: React.FC<ResizableWrapperProps> = ({ const constraintsRef = useRef({ minWidth, maxWidth, minHeight, maxHeight }); constraintsRef.current = { minWidth, maxWidth, minHeight, maxHeight }; + useEffect(() => { + if (!dragRef.current && wrapperRef.current) { + widthRef.current = initialWidth; + heightRef.current = initialHeight; + wrapperRef.current.style.width = `${initialWidth}px`; + wrapperRef.current.style.height = `${initialHeight}px`; + } + }, [initialWidth, initialHeight]); + const handleMouseMove = useRef((e: MouseEvent) => { const drag = dragRef.current; if (!drag || !wrapperRef.current) return; diff --git a/apps/client/src/features/editor/components/embed/embed-view.tsx b/apps/client/src/features/editor/components/embed/embed-view.tsx index 021f4f3a..176f14ad 100644 --- a/apps/client/src/features/editor/components/embed/embed-view.tsx +++ b/apps/client/src/features/editor/components/embed/embed-view.tsx @@ -86,8 +86,8 @@ export default function EmbedView(props: NodeViewProps) { {embedUrl ? ( <div className={classes.embedContainer}> <ResizableWrapper - initialWidth={nodeWidth || 640} - initialHeight={nodeHeight || 480} + initialWidth={nodeWidth || 800} + initialHeight={nodeHeight || 600} minWidth={200} maxWidth={1200} minHeight={200} @@ -102,8 +102,9 @@ export default function EmbedView(props: NodeViewProps) { <iframe className={classes.embedIframe} src={sanitizeUrl(embedUrl)} - allow="encrypted-media" - sandbox="allow-scripts allow-same-origin allow-forms allow-popups" + allow="encrypted-media; clipboard-read; clipboard-write; picture-in-picture;" + loading="lazy" + sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-downloads" allowFullScreen frameBorder="0" /> diff --git a/apps/client/src/features/editor/components/image/image-view.module.css b/apps/client/src/features/editor/components/image/image-view.module.css index d326ee5a..987ec0d7 100644 --- a/apps/client/src/features/editor/components/image/image-view.module.css +++ b/apps/client/src/features/editor/components/image/image-view.module.css @@ -5,6 +5,9 @@ max-width: 100%; border-radius: 8px; overflow: hidden; +} + +.skeleton { animation: pulse 1.2s ease-in-out infinite; @mixin light { diff --git a/apps/client/src/features/editor/components/image/image-view.tsx b/apps/client/src/features/editor/components/image/image-view.tsx index defb64c4..7ec3e26f 100644 --- a/apps/client/src/features/editor/components/image/image-view.tsx +++ b/apps/client/src/features/editor/components/image/image-view.tsx @@ -33,6 +33,7 @@ export default function ImageView(props: NodeViewProps) { className={clsx( selected && "ProseMirror-selectednode", classes.imageWrapper, + !src && classes.skeleton, alignClass, )} style={{ diff --git a/apps/client/src/features/editor/components/pdf/pdf-menu.tsx b/apps/client/src/features/editor/components/pdf/pdf-menu.tsx new file mode 100644 index 00000000..2104bfbc --- /dev/null +++ b/apps/client/src/features/editor/components/pdf/pdf-menu.tsx @@ -0,0 +1,145 @@ +import { BubbleMenu as BaseBubbleMenu } from "@tiptap/react/menus"; +import { findParentNode, posToDOMRect, useEditorState } from "@tiptap/react"; +import { useCallback } from "react"; +import { Node as PMNode } from "@tiptap/pm/model"; +import { + EditorMenuProps, + ShouldShowProps, +} from "@/features/editor/components/table/types/types.ts"; +import { ActionIcon, Tooltip } from "@mantine/core"; +import { + IconPaperclip, + IconTrash, +} from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import classes from "../common/toolbar-menu.module.css"; + +export function PdfMenu({ editor }: EditorMenuProps) { + const { t } = useTranslation(); + + const editorState = useEditorState({ + editor, + selector: (ctx) => { + if (!ctx.editor) { + return null; + } + + const pdfAttrs = ctx.editor.getAttributes("pdf"); + + return { + isPdf: ctx.editor.isActive("pdf"), + src: pdfAttrs?.src || null, + name: pdfAttrs?.name || null, + attachmentId: pdfAttrs?.attachmentId || null, + }; + }, + }); + + const shouldShow = useCallback( + ({ state }: ShouldShowProps) => { + if (!state || !editor.isActive("pdf")) { + return false; + } + + const { selection } = state; + const dom = editor.view.nodeDOM(selection.from) as HTMLElement | null; + if (!dom) return false; + + return !!dom.querySelector("[data-pdf-error]"); + }, + [editor], + ); + + const getReferencedVirtualElement = useCallback(() => { + if (!editor) return; + const { selection } = editor.state; + const predicate = (node: PMNode) => node.type.name === "pdf"; + const parent = findParentNode(predicate)(selection); + + if (parent) { + const dom = editor.view.nodeDOM(parent?.pos) as HTMLElement; + const domRect = dom.getBoundingClientRect(); + return { + getBoundingClientRect: () => domRect, + getClientRects: () => [domRect], + }; + } + + const domRect = posToDOMRect(editor.view, selection.from, selection.to); + return { + getBoundingClientRect: () => domRect, + getClientRects: () => [domRect], + }; + }, [editor]); + + const handleConvertToAttachment = useCallback(() => { + if (!editorState?.src) return; + + const { selection } = editor.state; + const { from } = selection; + const node = editor.state.doc.nodeAt(from); + if (!node || node.type.name !== "pdf") return; + + editor + .chain() + .insertContentAt( + { from, to: from + node.nodeSize }, + { + type: "attachment", + attrs: { + url: node.attrs.src, + name: node.attrs.name, + attachmentId: node.attrs.attachmentId, + size: node.attrs.size, + mime: "application/pdf", + }, + }, + ) + .run(); + }, [editor, editorState]); + + const handleDelete = useCallback(() => { + editor.commands.deleteSelection(); + }, [editor]); + + return ( + <BaseBubbleMenu + editor={editor} + pluginKey={`pdf-menu`} + updateDelay={0} + getReferencedVirtualElement={getReferencedVirtualElement} + options={{ + placement: "top", + offset: 8, + flip: false, + }} + shouldShow={shouldShow} + > + <div className={classes.toolbar}> + <Tooltip position="top" label={t("Convert to attachment")} withinPortal={false}> + <ActionIcon + onClick={handleConvertToAttachment} + size="lg" + aria-label={t("Convert to attachment")} + variant="subtle" + > + <IconPaperclip size={18} /> + </ActionIcon> + </Tooltip> + + <Tooltip position="top" label={t("Delete")} withinPortal={false}> + <ActionIcon + onClick={handleDelete} + size="lg" + aria-label={t("Delete")} + variant="subtle" + > + <IconTrash size={18} /> + </ActionIcon> + </Tooltip> + </div> + </BaseBubbleMenu> + ); +} + +export default PdfMenu; diff --git a/apps/client/src/features/editor/components/pdf/pdf-view.module.css b/apps/client/src/features/editor/components/pdf/pdf-view.module.css new file mode 100644 index 00000000..df5af87f --- /dev/null +++ b/apps/client/src/features/editor/components/pdf/pdf-view.module.css @@ -0,0 +1,100 @@ +.pdfWrapper { + display: flex; + justify-content: center; + align-items: center; + max-width: 100%; + border-radius: 8px; + overflow: hidden; +} + +.skeleton { + animation: pulse 1.2s ease-in-out infinite; + + @mixin light { + background: linear-gradient(-90deg, var(--mantine-color-gray-3) 0%, var(--mantine-color-gray-1) 50%, var(--mantine-color-gray-3) 100%); + background-size: 400% 400%; + } + + @mixin dark { + background: linear-gradient(-90deg, var(--mantine-color-dark-6) 0%, var(--mantine-color-dark-5) 50%, var(--mantine-color-dark-6) 100%); + background-size: 400% 400%; + } + + @keyframes pulse { + 0% { + background-position: 0% 0%; + } + 100% { + background-position: -135% 0%; + } + } +} + +.pdfContainer { + display: flex; + justify-content: center; +} + +.pdfResizeWrapper { + @mixin light { + background-color: var(--mantine-color-gray-0); + } + + @mixin dark { + background-color: var(--mantine-color-dark-7); + } +} + +.pdfIframe { + width: 100%; + height: 100%; + border: none; + border-radius: 8px; +} + +.hoverMenu { + position: absolute; + top: 56px; + right: 8px; + z-index: 2; + display: flex; + gap: 4px; + padding: 4px; + border-radius: 6px; + opacity: 0; + transition: opacity 0.15s ease; + background-color: rgba(0, 0, 0, 0.5); +} + +.hoverMenu::before { + content: ""; + position: absolute; + inset: -12px; +} + +.hoverMenu:hover { + opacity: 1; +} + +.pdfResizeWrapper:hover .hoverMenu { + opacity: 1; +} + +.pdfError { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px 32px; + border-radius: 8px; + cursor: pointer; + + @mixin light { + background-color: var(--mantine-color-gray-0); + } + + @mixin dark { + background-color: var(--mantine-color-dark-7); + } +} diff --git a/apps/client/src/features/editor/components/pdf/pdf-view.tsx b/apps/client/src/features/editor/components/pdf/pdf-view.tsx new file mode 100644 index 00000000..6207da9f --- /dev/null +++ b/apps/client/src/features/editor/components/pdf/pdf-view.tsx @@ -0,0 +1,168 @@ +import { NodeViewProps, NodeViewWrapper } from "@tiptap/react"; +import { ActionIcon, Group, Loader, Text, Tooltip } from "@mantine/core"; +import { useCallback, useMemo, useState } from "react"; +import { getFileUrl } from "@/lib/config.ts"; +import { ResizableWrapper } from "../common/resizable-wrapper"; +import clsx from "clsx"; +import classes from "./pdf-view.module.css"; +import { useTranslation } from "react-i18next"; +import { isInternalFileUrl } from "@docmost/editor-ext"; +import { + IconFileTypePdf, + IconPaperclip, + IconTrash, +} from "@tabler/icons-react"; + +export default function PdfView(props: NodeViewProps) { + const { t } = useTranslation(); + const { editor, node, getPos, selected, updateAttributes } = props; + const { src, placeholder, width: nodeWidth, height: nodeHeight } = node.attrs; + const [hasError, setHasError] = useState(false); + + const safeSrc = useMemo(() => { + if (!src || !isInternalFileUrl(src)) return null; + return getFileUrl(src); + }, [src]); + + const handleSelect = useCallback(() => { + const pos = getPos(); + if (pos !== undefined) { + editor.commands.setNodeSelection(pos); + } + }, [editor, getPos]); + + const handleResize = useCallback( + (newWidth: number, newHeight: number) => { + updateAttributes({ width: newWidth, height: newHeight }); + }, + [updateAttributes], + ); + + const handleConvertToAttachment = useCallback(() => { + if (!src) return; + const pos = getPos(); + if (pos === undefined) return; + const currentNode = editor.state.doc.nodeAt(pos); + if (!currentNode || currentNode.type.name !== "pdf") return; + + editor + .chain() + .insertContentAt( + { from: pos, to: pos + currentNode.nodeSize }, + { + type: "attachment", + attrs: { + url: currentNode.attrs.src, + name: currentNode.attrs.name, + attachmentId: currentNode.attrs.attachmentId, + size: currentNode.attrs.size, + mime: "application/pdf", + }, + }, + ) + .run(); + }, [editor, src, getPos]); + + const handleDelete = useCallback(() => { + const pos = getPos(); + if (pos === undefined) return; + editor.commands.setNodeSelection(pos); + editor.commands.deleteSelection(); + }, [editor, getPos]); + + if (!src || !safeSrc) { + return ( + <NodeViewWrapper data-drag-handle> + <div className={`${classes.pdfWrapper} ${classes.skeleton}`} style={{ height: 600 }}> + <Group justify="center" wrap="nowrap" gap="xs" maw="100%" px="md"> + <Loader size={20} style={{ flexShrink: 0 }} /> + <Text component="span" size="sm" truncate="end"> + {placeholder?.name + ? t("Uploading {{name}}", { name: placeholder.name }) + : t("Uploading file")} + </Text> + </Group> + </div> + </NodeViewWrapper> + ); + } + + if (hasError) { + return ( + <NodeViewWrapper data-drag-handle> + <div data-pdf-error className={clsx(classes.pdfError, { "ProseMirror-selectednode": selected })} onClick={handleSelect}> + <IconFileTypePdf size={32} stroke={1.5} /> + <Text size="sm" c="dimmed"> + {t("Failed to load PDF")} + </Text> + </div> + </NodeViewWrapper> + ); + } + + return ( + <NodeViewWrapper data-drag-handle className={classes.pdfNodeView}> + <div className={classes.pdfContainer}> + <ResizableWrapper + initialWidth={nodeWidth || 800} + initialHeight={nodeHeight || 600} + minWidth={200} + maxWidth={1200} + minHeight={200} + maxHeight={1200} + onResize={handleResize} + isEditable={editor.isEditable} + selected={selected} + className={clsx(classes.pdfResizeWrapper, { + "ProseMirror-selectednode": selected, + })} + > + <iframe + className={classes.pdfIframe} + src={safeSrc} + loading="lazy" + frameBorder="0" + onError={() => setHasError(true)} + onLoad={(e) => { + try { + const iframe = e.currentTarget; + const status = iframe.contentDocument?.querySelector("pre")?.textContent; + if (status && status.includes('"statusCode":404')) { + setHasError(true); + } + } catch { + // cross-origin - can't inspect, assume OK + } + }} + /> + {editor.isEditable && ( + <div className={classes.hoverMenu}> + <Tooltip position="top" label={t("Convert to attachment")} withinPortal> + <ActionIcon + size="sm" + variant="filled" + color="dark" + onClick={handleConvertToAttachment} + aria-label={t("Convert to attachment")} + > + <IconPaperclip size={14} /> + </ActionIcon> + </Tooltip> + <Tooltip position="top" label={t("Delete")} withinPortal> + <ActionIcon + size="sm" + variant="filled" + color="dark" + onClick={handleDelete} + aria-label={t("Delete")} + > + <IconTrash size={14} /> + </ActionIcon> + </Tooltip> + </div> + )} + </ResizableWrapper> + </div> + </NodeViewWrapper> + ); +} diff --git a/apps/client/src/features/editor/components/pdf/upload-pdf-action.tsx b/apps/client/src/features/editor/components/pdf/upload-pdf-action.tsx new file mode 100644 index 00000000..31a56f5b --- /dev/null +++ b/apps/client/src/features/editor/components/pdf/upload-pdf-action.tsx @@ -0,0 +1,36 @@ +import { handlePdfUpload } from "@docmost/editor-ext"; +import { uploadFile } from "@/features/page/services/page-service.ts"; +import { notifications } from "@mantine/notifications"; +import { getFileUploadSizeLimit } from "@/lib/config.ts"; +import { formatBytes } from "@/lib"; +import i18n from "@/i18n.ts"; + +export const uploadPdfAction = handlePdfUpload({ + onUpload: async (file: File, pageId: string): Promise<any> => { + try { + return await uploadFile(file, pageId); + } catch (err) { + notifications.show({ + color: "red", + message: err?.response.data.message, + }); + throw err; + } + }, + validateFn: (file) => { + if (file.type !== "application/pdf") { + return false; + } + + if (file.size > getFileUploadSizeLimit()) { + notifications.show({ + color: "red", + message: i18n.t("File exceeds the {{limit}} attachment limit", { + limit: formatBytes(getFileUploadSizeLimit()), + }), + }); + return false; + } + return true; + }, +}); diff --git a/apps/client/src/features/editor/components/slash-menu/menu-items.ts b/apps/client/src/features/editor/components/slash-menu/menu-items.ts index d31bdb18..58228ed9 100644 --- a/apps/client/src/features/editor/components/slash-menu/menu-items.ts +++ b/apps/client/src/features/editor/components/slash-menu/menu-items.ts @@ -12,7 +12,9 @@ import { IconMath, IconMathFunction, IconMovie, + IconMusic, IconPaperclip, + IconFileTypePdf, IconPhoto, IconTable, IconTypography, @@ -30,7 +32,9 @@ import { } from "@/features/editor/components/slash-menu/types"; import { uploadImageAction } from "@/features/editor/components/image/upload-image-action.tsx"; import { uploadVideoAction } from "@/features/editor/components/video/upload-video-action.tsx"; +import { uploadAudioAction } from "@/features/editor/components/audio/upload-audio-action.tsx"; import { uploadAttachmentAction } from "@/features/editor/components/attachment/upload-attachment-action.tsx"; +import { uploadPdfAction } from "@/features/editor/components/pdf/upload-pdf-action.tsx"; import IconExcalidraw from "@/components/icons/icon-excalidraw"; import IconMermaid from "@/components/icons/icon-mermaid"; import IconDrawio from "@/components/icons/icon-drawio"; @@ -161,7 +165,7 @@ const CommandGroups: SlashMenuGroupedItemsType = { { title: "Image", description: "Upload any image from your device.", - searchTerms: ["photo", "picture", "media"], + searchTerms: ["photo", "picture", "media", "file", "attachment"], icon: IconPhoto, command: ({ editor, range }) => { editor.chain().focus().deleteRange(range).run(); @@ -194,7 +198,7 @@ const CommandGroups: SlashMenuGroupedItemsType = { { title: "Video", description: "Upload any video from your device.", - searchTerms: ["video", "mp4", "media"], + searchTerms: ["video", "mp4", "media", "file", "attachment"], icon: IconMovie, command: ({ editor, range }) => { editor.chain().focus().deleteRange(range).run(); @@ -224,10 +228,74 @@ const CommandGroups: SlashMenuGroupedItemsType = { input.click(); }, }, + { + title: "Audio", + description: "Upload any audio from your device.", + searchTerms: ["audio", "music", "sound", "mp3", "media", "file", "attachment"], + icon: IconMusic, + command: ({ editor, range }) => { + editor.chain().focus().deleteRange(range).run(); + + // @ts-ignore + const pageId = editor.storage?.pageId; + if (!pageId) return; + + // upload audio + const input = document.createElement("input"); + input.type = "file"; + input.accept = "audio/*"; + input.multiple = true; + input.style.display = "none"; + document.body.appendChild(input); + input.onchange = async () => { + if (input.files?.length) { + for (const file of input.files) { + const pos = editor.view.state.selection.from; + + uploadAudioAction(file, editor, pos, pageId); + } + } + + input.remove(); + }; + input.click(); + }, + }, + { + title: "Embed PDF", + description: "Upload and embed a PDF file.", + searchTerms: ["pdf", "document", "embed"], + icon: IconFileTypePdf, + command: ({ editor, range }) => { + editor.chain().focus().deleteRange(range).run(); + + // @ts-ignore + const pageId = editor.storage?.pageId; + if (!pageId) return; + + const input = document.createElement("input"); + input.type = "file"; + input.accept = "application/pdf"; + input.style.display = "none"; + document.body.appendChild(input); + input.onchange = async () => { + if (input.files?.length) { + for (const file of input.files) { + const pos = editor.view.state.selection.from; + + uploadPdfAction(file, editor, pos, pageId); + } + } + + input.remove(); + }; + input.click(); + }, + }, { title: "File attachment", description: "Upload any file from your device.", - searchTerms: ["file", "attachment", "upload", "pdf", "csv", "zip"], + searchTerms: ["file", "attachment", "upload", "csv", "zip"], icon: IconPaperclip, command: ({ editor, range }) => { editor.chain().focus().deleteRange(range).run(); @@ -359,7 +427,7 @@ const CommandGroups: SlashMenuGroupedItemsType = { editor.chain().focus().deleteRange(range).setDrawio().run(), }, { - title: "Excalidraw diagram", + title: "Excalidraw (Whiteboard)", description: "Draw and sketch excalidraw diagrams", searchTerms: ["diagrams", "draw", "sketch", "whiteboard"], icon: IconExcalidraw, @@ -548,7 +616,7 @@ const CommandGroups: SlashMenuGroupedItemsType = { { title: "YouTube", description: "Embed YouTube video", - searchTerms: ["youtube", "yt"], + searchTerms: ["youtube", "yt", "media", "video"], icon: YoutubeIcon, command: ({ editor, range }: CommandProps) => { editor @@ -647,7 +715,11 @@ export const getSuggestionItems = ({ }); if (filteredItems.length) { - filteredGroups[group] = filteredItems; + filteredGroups[group] = filteredItems.sort((a, b) => { + const aTitle = a.title.toLowerCase().includes(search) ? 0 : 1; + const bTitle = b.title.toLowerCase().includes(search) ? 0 : 1; + return aTitle - bTitle; + }); } } diff --git a/apps/client/src/features/editor/components/video/video-view.module.css b/apps/client/src/features/editor/components/video/video-view.module.css index 8d1d28c2..f1a51846 100644 --- a/apps/client/src/features/editor/components/video/video-view.module.css +++ b/apps/client/src/features/editor/components/video/video-view.module.css @@ -5,6 +5,9 @@ max-width: 100%; border-radius: 8px; overflow: hidden; +} + +.skeleton { animation: pulse 1.2s ease-in-out infinite; @mixin light { @@ -26,6 +29,7 @@ } } } + .video { display: block; width: 100%; diff --git a/apps/client/src/features/editor/components/video/video-view.tsx b/apps/client/src/features/editor/components/video/video-view.tsx index e2473afc..1e662640 100644 --- a/apps/client/src/features/editor/components/video/video-view.tsx +++ b/apps/client/src/features/editor/components/video/video-view.tsx @@ -33,6 +33,7 @@ export default function VideoView(props: NodeViewProps) { className={clsx( selected && "ProseMirror-selectednode", classes.videoWrapper, + !src && classes.skeleton, alignClass, )} style={{ diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index 0532156b..37f173dc 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -30,6 +30,7 @@ import { TiptapImage, Callout, TiptapVideo, + TiptapAudio, LinkExtension, Selection, Attachment, @@ -37,6 +38,7 @@ import { Drawio, Excalidraw, Embed, + TiptapPdf, SearchAndReplace, Mention, TableDndExtension, @@ -68,11 +70,13 @@ import ImageView from "@/features/editor/components/image/image-view.tsx"; import CalloutView from "@/features/editor/components/callout/callout-view.tsx"; import StatusView from "@/features/editor/components/status/status-view.tsx"; import VideoView from "@/features/editor/components/video/video-view.tsx"; +import AudioView from "@/features/editor/components/audio/audio-view.tsx"; import AttachmentView from "@/features/editor/components/attachment/attachment-view.tsx"; import CodeBlockView from "@/features/editor/components/code-block/code-block-view.tsx"; import DrawioView from "../components/drawio/drawio-view"; import ExcalidrawView from "@/features/editor/components/excalidraw/excalidraw-view.tsx"; import EmbedView from "@/features/editor/components/embed/embed-view.tsx"; +import PdfView from "@/features/editor/components/pdf/pdf-view.tsx"; import SubpagesView from "@/features/editor/components/subpages/subpages-view.tsx"; import { common, createLowlight } from "lowlight"; import plaintext from "highlight.js/lib/languages/plaintext"; @@ -269,6 +273,9 @@ export const mainExtensions = [ className: buildResizeClasses("node-video"), }, }), + TiptapAudio.configure({ + view: AudioView, + }), Callout.configure({ view: CalloutView, }), @@ -313,6 +320,9 @@ export const mainExtensions = [ Embed.configure({ view: EmbedView, }), + TiptapPdf.configure({ + view: PdfView, + }), Subpages.configure({ view: SubpagesView, }), diff --git a/apps/client/src/features/editor/page-editor.tsx b/apps/client/src/features/editor/page-editor.tsx index a226d3d2..d51a5a4c 100644 --- a/apps/client/src/features/editor/page-editor.tsx +++ b/apps/client/src/features/editor/page-editor.tsx @@ -45,6 +45,7 @@ import TableMenu from "@/features/editor/components/table/table-menu.tsx"; import ImageMenu from "@/features/editor/components/image/image-menu.tsx"; import CalloutMenu from "@/features/editor/components/callout/callout-menu.tsx"; import VideoMenu from "@/features/editor/components/video/video-menu.tsx"; +import PdfMenu from "@/features/editor/components/pdf/pdf-menu.tsx"; import SubpagesMenu from "@/features/editor/components/subpages/subpages-menu.tsx"; import { handleFileDrop, @@ -414,6 +415,7 @@ export default function PageEditor({ <TableCellMenu editor={editor} appendTo={menuContainerRef} /> <ImageMenu editor={editor} /> <VideoMenu editor={editor} /> + <PdfMenu editor={editor} /> <CalloutMenu editor={editor} /> <SubpagesMenu editor={editor} /> <ExcalidrawMenu editor={editor} /> diff --git a/apps/client/src/features/editor/styles/core.css b/apps/client/src/features/editor/styles/core.css index 1f8861f3..34ddaca3 100644 --- a/apps/client/src/features/editor/styles/core.css +++ b/apps/client/src/features/editor/styles/core.css @@ -133,10 +133,18 @@ border-top: 1px solid #68cef8; } + &[contenteditable="false"] hr.ProseMirror-selectednode { + border-top: none; + } + .ProseMirror-selectednode { outline: 2px solid #70cff8; } + &[contenteditable="false"] .ProseMirror-selectednode { + outline: none; + } + & > .react-renderer { margin-top: var(--mantine-spacing-sm); margin-bottom: var(--mantine-spacing-sm); diff --git a/apps/client/src/features/editor/styles/media.css b/apps/client/src/features/editor/styles/media.css index ac9cf3f7..0b02cdbe 100644 --- a/apps/client/src/features/editor/styles/media.css +++ b/apps/client/src/features/editor/styles/media.css @@ -8,7 +8,7 @@ } } - .node-image, .node-video, .node-excalidraw, .node-drawio { + .node-image, .node-video, .node-pdf, .node-excalidraw, .node-drawio { &.ProseMirror-selectednode { outline: none; } @@ -37,5 +37,28 @@ font-size: var(--mantine-font-size-md); line-height: var(--mantine-line-height-md); } + + .media-pulse { + animation: media-pulse 1.2s ease-in-out infinite; + + @mixin light { + background: linear-gradient(-90deg, var(--mantine-color-gray-3) 0%, var(--mantine-color-gray-1) 50%, var(--mantine-color-gray-3) 100%); + background-size: 400% 400%; + } + + @mixin dark { + background: linear-gradient(-90deg, var(--mantine-color-dark-6) 0%, var(--mantine-color-dark-5) 50%, var(--mantine-color-dark-6) 100%); + background-size: 400% 400%; + } + + @keyframes media-pulse { + 0% { + background-position: 0% 0%; + } + 100% { + background-position: -135% 0%; + } + } + } } diff --git a/apps/client/src/i18n.ts b/apps/client/src/i18n.ts index 2f240dcf..d5c8a99e 100644 --- a/apps/client/src/i18n.ts +++ b/apps/client/src/i18n.ts @@ -14,6 +14,7 @@ i18n .init({ fallbackLng: "en-US", debug: false, + showSupportNotice: false, load: 'currentOnly', interpolation: { diff --git a/apps/server/src/collaboration/collaboration.util.ts b/apps/server/src/collaboration/collaboration.util.ts index 9fa2f7a6..6cfed052 100644 --- a/apps/server/src/collaboration/collaboration.util.ts +++ b/apps/server/src/collaboration/collaboration.util.ts @@ -24,6 +24,8 @@ import { CustomTable, TiptapImage, TiptapVideo, + TiptapAudio, + TiptapPdf, TrailingNode, Attachment, Drawio, @@ -86,6 +88,8 @@ export const tiptapExtensions = [ Youtube, TiptapImage, TiptapVideo, + TiptapAudio, + TiptapPdf, Callout, Attachment, CustomCodeBlock, diff --git a/apps/server/src/common/helpers/prosemirror/utils.ts b/apps/server/src/common/helpers/prosemirror/utils.ts index 424cd787..7704306e 100644 --- a/apps/server/src/common/helpers/prosemirror/utils.ts +++ b/apps/server/src/common/helpers/prosemirror/utils.ts @@ -102,6 +102,8 @@ export function isAttachmentNode(nodeType: string) { 'attachment', 'image', 'video', + 'audio', + 'pdf', 'excalidraw', 'drawio', ]; diff --git a/apps/server/src/core/attachment/attachment.constants.ts b/apps/server/src/core/attachment/attachment.constants.ts index 7fb7e126..a9bce5c1 100644 --- a/apps/server/src/core/attachment/attachment.constants.ts +++ b/apps/server/src/core/attachment/attachment.constants.ts @@ -15,4 +15,9 @@ export const inlineFileExtensions = [ '.pdf', '.mp4', '.mov', + '.mp3', + '.wav', + '.ogg', + '.m4a', + '.webm', ]; diff --git a/apps/server/src/core/attachment/attachment.controller.ts b/apps/server/src/core/attachment/attachment.controller.ts index d70f0034..6382b34e 100644 --- a/apps/server/src/core/attachment/attachment.controller.ts +++ b/apps/server/src/core/attachment/attachment.controller.ts @@ -457,6 +457,10 @@ export class AttachmentController { const rangeHeader = req.headers.range; res.header('Accept-Ranges', 'bytes'); + res.header( + 'Content-Security-Policy', + "base-uri 'none'; object-src 'self'; default-src 'self';", + ); if (!inlineFileExtensions.includes(attachment.fileExt)) { res.header( diff --git a/apps/server/src/integrations/import/services/import-attachment.service.ts b/apps/server/src/integrations/import/services/import-attachment.service.ts index a797ecc6..3c14d854 100644 --- a/apps/server/src/integrations/import/services/import-attachment.service.ts +++ b/apps/server/src/integrations/import/services/import-attachment.service.ts @@ -190,13 +190,32 @@ export class ImportAttachmentService { } } + // Build a map from resolved archive path → real filename from Confluence + // metadata. Confluence Server archives often store files under numeric IDs + // (e.g. "attachments/65601/65602") instead of the original filename. + const pageDir = path.dirname(pageRelativePath); + const attachmentNameByRelPath = new Map<string, string>(); + for (const attachment of pageAttachments) { + const relPath = resolveRelativeAttachmentPath( + attachment.href, + pageDir, + attachmentCandidates, + ); + if (relPath && attachment.fileName) { + attachmentNameByRelPath.set(relPath, attachment.fileName); + } + } + const uploadOnce = (relPath: string) => { const abs = attachmentCandidates.get(relPath)!; const attachmentId = v7(); - const ext = path.extname(abs); + + const realName = attachmentNameByRelPath.get(relPath); + const baseName = realName || path.basename(abs); + const ext = path.extname(baseName); const fileNameWithExt = - sanitizeFileName(path.basename(abs, ext)) + ext.toLowerCase(); + sanitizeFileName(path.basename(baseName, ext)) + ext.toLowerCase(); const storageFilePath = `${getAttachmentFolderPath( AttachmentType.File, @@ -240,7 +259,6 @@ export class ImportAttachmentService { return fresh; }; - const pageDir = path.dirname(pageRelativePath); const $ = load(html); // image @@ -335,6 +353,28 @@ export class ImportAttachmentService { unwrapFromParagraph($, $vid); } + // audio + for (const audEl of $('audio').toArray()) { + const $aud = $(audEl); + const src = cleanUrlString($aud.attr('src') ?? '')!; + if (!src || src.startsWith('http')) continue; + + const relPath = resolveRelativeAttachmentPath( + src, + pageDir, + attachmentCandidates, + ); + if (!relPath) continue; + + const { attachmentId, apiFilePath } = processFile(relPath); + + $aud + .attr('src', apiFilePath) + .attr('data-attachment-id', attachmentId); + + unwrapFromParagraph($, $aud); + } + // <div data-type="attachment"> for (const el of $('div[data-type="attachment"]').toArray()) { const $oldDiv = $(el); @@ -401,7 +441,18 @@ export class ImportAttachmentService { const { attachmentId, apiFilePath, abs } = processFile(relPath); const ext = path.extname(relPath).toLowerCase(); - if (ext === '.mp4') { + const audioExtensions = new Set(['.mp3', '.wav', '.ogg', '.m4a', '.webm', '.flac', '.aac']); + + if (ext === '.pdf') { + const $pdf = $('<div>') + .attr('data-type', 'pdf') + .attr('src', apiFilePath) + .attr('data-attachment-id', attachmentId) + .attr('width', '800') + .attr('height', '600'); + $a.replaceWith($pdf); + unwrapFromParagraph($, $pdf); + } else if (ext === '.mp4') { const $video = $('<video>') .attr('src', apiFilePath) .attr('data-attachment-id', attachmentId) @@ -409,6 +460,12 @@ export class ImportAttachmentService { .attr('data-align', 'center'); $a.replaceWith($video); unwrapFromParagraph($, $video); + } else if (audioExtensions.has(ext)) { + const $audio = $('<audio>') + .attr('src', apiFilePath) + .attr('data-attachment-id', attachmentId); + $a.replaceWith($audio); + unwrapFromParagraph($, $audio); } else { const confAliasName = $a.attr('data-linked-resource-default-alias'); let attachmentName = path.basename(abs); @@ -555,7 +612,7 @@ export class ImportAttachmentService { // Post-process DOM elements to add file sizes after uploads complete // This avoids blocking file operations during initial DOM processing const elementsNeedingSize = $( - '[data-attachment-id]:not([data-attachment-size])', + '[data-attachment-id]:not([data-attachment-size]):not([data-size])', ); for (const element of elementsNeedingSize.toArray()) { const $el = $(element); @@ -570,7 +627,14 @@ export class ImportAttachmentService { if (processedEntry) { try { const stat = await fs.stat(processedEntry.abs); - $el.attr('data-attachment-size', stat.size.toString()); + const sizeStr = stat.size.toString(); + const tagName = $el.prop('tagName')?.toLowerCase(); + // audio and pdf nodes use data-size, attachment nodes use data-attachment-size + if (tagName === 'audio' || $el.attr('data-type') === 'pdf') { + $el.attr('data-size', sizeStr); + } else { + $el.attr('data-attachment-size', sizeStr); + } } catch (error) { this.logger.debug( `Could not get size for ${processedEntry.abs}:`, diff --git a/apps/server/src/integrations/import/utils/import.utils.ts b/apps/server/src/integrations/import/utils/import.utils.ts index cebe89ea..45f4ed12 100644 --- a/apps/server/src/integrations/import/utils/import.utils.ts +++ b/apps/server/src/integrations/import/utils/import.utils.ts @@ -41,6 +41,15 @@ export function resolveRelativeAttachmentPath( 'ImportUtils', ); } + + // Confluence Server uses "/download/attachments/..." in HTML but the ZIP + // stores files under "attachments/...". Strip the "download/" prefix so + // the path can match candidates from the archive. + const confluenceStripped = mainRel.replace( + /^download\/attachments\//, + 'attachments/', + ); + const fallback = path .normalize(path.join(pageDir, mainRel)) .split(path.sep) @@ -49,9 +58,13 @@ export function resolveRelativeAttachmentPath( if (attachmentCandidates.has(mainRel)) { return mainRel; } + if (confluenceStripped !== mainRel && attachmentCandidates.has(confluenceStripped)) { + return confluenceStripped; + } if (attachmentCandidates.has(fallback)) { return fallback; } + return null; } diff --git a/apps/server/src/integrations/storage/drivers/local.driver.ts b/apps/server/src/integrations/storage/drivers/local.driver.ts index 90f7b7dd..39342c61 100644 --- a/apps/server/src/integrations/storage/drivers/local.driver.ts +++ b/apps/server/src/integrations/storage/drivers/local.driver.ts @@ -66,25 +66,25 @@ export class LocalDriver implements StorageDriver { } async readStream(filePath: string): Promise<Readable> { - try { - return createReadStream(this._fullPath(filePath)); - } catch (err) { - throw new Error(`Failed to read file: ${(err as Error).message}`); + const fullPath = this._fullPath(filePath); + if (!(await fs.pathExists(fullPath))) { + throw new Error(`File not found: ${filePath}`); } + return createReadStream(fullPath); } async readRangeStream( filePath: string, range: { start: number; end: number }, ): Promise<Readable> { - try { - return createReadStream(this._fullPath(filePath), { - start: range.start, - end: range.end, - }); - } catch (err) { - throw new Error(`Failed to read file: ${(err as Error).message}`); + const fullPath = this._fullPath(filePath); + if (!(await fs.pathExists(fullPath))) { + throw new Error(`File not found: ${filePath}`); } + return createReadStream(fullPath, { + start: range.start, + end: range.end, + }); } async exists(filePath: string): Promise<boolean> { diff --git a/package.json b/package.json index 63994abd..623b0407 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@joplin/turndown-plugin-gfm": "^1.0.64", "@sindresorhus/slugify": "3.0.0", "@tiptap/core": "3.20.4", + "@tiptap/extension-audio": "3.20.4", "@tiptap/extension-code-block": "3.20.4", "@tiptap/extension-collaboration": "3.20.4", "@tiptap/extension-collaboration-caret": "3.20.4", @@ -94,8 +95,7 @@ "packageManager": "pnpm@10.4.0", "pnpm": { "patchedDependencies": { - "react-arborist@3.4.0": "patches/react-arborist@3.4.0.patch", - "@tiptap/core": "patches/@tiptap__core.patch" + "react-arborist@3.4.0": "patches/react-arborist@3.4.0.patch" }, "overrides": { "prosemirror-changeset": "2.4.0", diff --git a/packages/editor-ext/src/index.ts b/packages/editor-ext/src/index.ts index e8ea4311..f338bcfa 100644 --- a/packages/editor-ext/src/index.ts +++ b/packages/editor-ext/src/index.ts @@ -11,6 +11,7 @@ export * from "./lib/media-utils"; export * from "./lib/link"; export * from "./lib/selection"; export * from "./lib/attachment"; +export * from "./lib/audio"; export * from "./lib/custom-code-block"; export * from "./lib/drawio"; export * from "./lib/excalidraw"; @@ -27,3 +28,6 @@ export * from "./lib/shared-storage"; export * from "./lib/recreate-transform"; export * from "./lib/columns"; export * from "./lib/status"; +export * from "./lib/pdf"; +export * from "./lib/resizable-nodeview"; + diff --git a/packages/editor-ext/src/lib/audio/audio-upload.ts b/packages/editor-ext/src/lib/audio/audio-upload.ts new file mode 100644 index 00000000..82a41f47 --- /dev/null +++ b/packages/editor-ext/src/lib/audio/audio-upload.ts @@ -0,0 +1,139 @@ +import { MediaUploadOptions, UploadFn } from "../media-utils"; +import { IAttachment } from "../types"; +import { generateNodeId } from "../utils"; +import { Node } from "@tiptap/pm/model"; +import { Command } from "@tiptap/core"; + +const findAudioNodeByPlaceholderId = ( + doc: Node, + placeholderId: string, +): { node: Node; pos: number } | null => { + let result: { node: Node; pos: number } | null = null; + + doc.descendants((node, pos) => { + if (result) return false; + + if ( + node.type.name === "audio" && + node.attrs.placeholder?.id === placeholderId + ) { + result = { node, pos }; + return false; + } + + return true; + }); + + return result; +}; + +const handleAudioUpload = + ({ validateFn, onUpload }: MediaUploadOptions): UploadFn => + async (file, editor, pos, pageId) => { + const validated = validateFn?.(file); + // @ts-ignore + if (!validated) return; + + const objectUrl = URL.createObjectURL(file); + const placeholderId = generateNodeId(); + + let placeholderInserted = false; + + editor.storage.shared.audioPreviews = + editor.storage.shared.audioPreviews || {}; + editor.storage.shared.audioPreviews[placeholderId] = objectUrl; + + const insertPlaceholder = (): Command => { + return ({ tr, state }) => { + const initialPlaceholderNode = state.schema.nodes.audio?.create({ + placeholder: { + id: placeholderId, + name: file.name, + }, + }); + + if (!initialPlaceholderNode) return false; + + const { parent } = tr.doc.resolve(pos); + const isEmptyTextBlock = parent.isTextblock && !parent.childCount; + + if (isEmptyTextBlock) { + tr.replaceRangeWith(pos - 1, pos + 1, initialPlaceholderNode); + } else { + tr.insert(pos, initialPlaceholderNode); + } + + return true; + }; + }; + + const replacePlaceholderWithAudio = (attachment: IAttachment): Command => { + return ({ tr }) => { + const { pos: currentPos = null } = + findAudioNodeByPlaceholderId(tr.doc, placeholderId) || {}; + + if (currentPos === null || !attachment) return; + + tr.setNodeMarkup(currentPos, undefined, { + src: `/api/files/${attachment.id}/${attachment.fileName}`, + attachmentId: attachment.id, + size: attachment.fileSize, + }); + + return true; + }; + }; + + const removePlaceholder = (): Command => { + return ({ tr }) => { + const { pos: currentPos = null } = + findAudioNodeByPlaceholderId(tr.doc, placeholderId) || {}; + + if (currentPos === null) return false; + + tr.delete(currentPos, currentPos + 2); + + return true; + }; + }; + + const insertPlaceholderTimeout = setTimeout(() => { + editor.commands.command(insertPlaceholder()); + placeholderInserted = true; + }, 250); + + const disposePreviewFile = () => { + URL.revokeObjectURL(objectUrl); + + if (editor.storage.shared.audioPreviews) { + delete editor.storage.shared.audioPreviews[placeholderId]; + } + }; + + try { + const attachment: IAttachment = await onUpload(file, pageId); + + clearTimeout(insertPlaceholderTimeout); + + if (placeholderInserted) { + setTimeout(() => { + editor.commands.command(replacePlaceholderWithAudio(attachment)); + disposePreviewFile(); + }, 100); + } else { + editor + .chain() + .command(insertPlaceholder()) + .command(replacePlaceholderWithAudio(attachment)) + .run(); + disposePreviewFile(); + } + } catch (error) { + clearTimeout(insertPlaceholderTimeout); + + editor.commands.command(removePlaceholder()); + disposePreviewFile(); + } + }; + +export { handleAudioUpload }; diff --git a/packages/editor-ext/src/lib/audio/audio.ts b/packages/editor-ext/src/lib/audio/audio.ts new file mode 100644 index 00000000..77d9bff9 --- /dev/null +++ b/packages/editor-ext/src/lib/audio/audio.ts @@ -0,0 +1,134 @@ +import { Node, mergeAttributes } from "@tiptap/core"; +import { ReactNodeViewRenderer } from "@tiptap/react"; +import { normalizeFileUrl } from "../media-utils"; +import { sanitizeUrl, isInternalFileUrl } from "../utils"; + +export interface AudioOptions { + view: any; + HTMLAttributes: Record<string, any>; +} + +export interface AudioAttributes { + src?: string; + attachmentId?: string; + size?: number; + placeholder?: { + id: string; + name: string; + }; +} + +declare module "@tiptap/core" { + interface Commands<ReturnType> { + audioBlock: { + setAudio: (attributes: AudioAttributes) => ReturnType; + }; + } +} + +export const TiptapAudio = Node.create<AudioOptions>({ + name: "audio", + + group: "block", + isolating: true, + atom: true, + defining: true, + draggable: true, + + addOptions() { + return { + view: null, + HTMLAttributes: {}, + }; + }, + + addAttributes() { + return { + src: { + default: "", + parseHTML: (element) => { + const src = element.getAttribute("src"); + const sanitized = sanitizeUrl(src); + return isInternalFileUrl(sanitized) ? sanitized : ""; + }, + renderHTML: (attributes) => ({ + src: isInternalFileUrl(attributes.src) + ? sanitizeUrl(attributes.src) + : "", + }), + }, + attachmentId: { + default: undefined, + parseHTML: (element) => element.getAttribute("data-attachment-id"), + renderHTML: (attributes: AudioAttributes) => ({ + "data-attachment-id": attributes.attachmentId, + }), + }, + size: { + default: null, + parseHTML: (element) => element.getAttribute("data-size"), + renderHTML: (attributes: AudioAttributes) => ({ + "data-size": attributes.size, + }), + }, + placeholder: { + default: null, + rendered: false, + }, + }; + }, + + parseHTML() { + return [ + { + tag: "audio", + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return [ + "audio", + mergeAttributes( + { controls: "true", preload: "metadata" }, + this.options.HTMLAttributes, + HTMLAttributes, + ), + ["source", { src: HTMLAttributes.src }], + ]; + }, + + addCommands() { + return { + setAudio: + (attrs: AudioAttributes) => + ({ commands }) => { + return commands.insertContent({ + type: "audio", + attrs: attrs, + }); + }, + }; + }, + + addNodeView() { + if (this.options.view) { + this.editor.isInitialized = true; + return ReactNodeViewRenderer(this.options.view); + } + + return ({ node, HTMLAttributes }) => { + const dom = document.createElement("div"); + const audio = document.createElement("audio"); + const src = node.attrs.src; + if (src && isInternalFileUrl(src)) { + audio.src = normalizeFileUrl(src); + } + audio.controls = true; + audio.preload = "metadata"; + audio.style.width = "100%"; + dom.append(audio); + return { dom }; + }; + }, +}); diff --git a/packages/editor-ext/src/lib/audio/index.ts b/packages/editor-ext/src/lib/audio/index.ts new file mode 100644 index 00000000..3e21fc3d --- /dev/null +++ b/packages/editor-ext/src/lib/audio/index.ts @@ -0,0 +1,2 @@ +export { TiptapAudio } from "./audio"; +export * from "./audio-upload"; diff --git a/packages/editor-ext/src/lib/drawio.ts b/packages/editor-ext/src/lib/drawio.ts index d3b69c72..4968912b 100644 --- a/packages/editor-ext/src/lib/drawio.ts +++ b/packages/editor-ext/src/lib/drawio.ts @@ -1,5 +1,6 @@ -import { Node, mergeAttributes, ResizableNodeView } from "@tiptap/core"; -import type { ResizableNodeViewDirection } from "@tiptap/core"; +import { Node, mergeAttributes } from "@tiptap/core"; +import { ResizableNodeView } from "./resizable-nodeview"; +import type { ResizableNodeViewDirection } from "./resizable-nodeview"; import { ReactNodeViewRenderer } from "@tiptap/react"; import { normalizeFileUrl } from "./media-utils"; @@ -320,12 +321,11 @@ export const Drawio = Node.create<DrawioOptions>({ // Show skeleton background while image loads from server dom.style.pointerEvents = "none"; - dom.style.background = - "light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))"; + el.classList.add("media-pulse"); el.onload = () => { dom.style.pointerEvents = ""; - dom.style.background = ""; + el.classList.remove("media-pulse"); }; return nodeView; diff --git a/packages/editor-ext/src/lib/embed.ts b/packages/editor-ext/src/lib/embed.ts index a93648b1..0b3cfc2f 100644 --- a/packages/editor-ext/src/lib/embed.ts +++ b/packages/editor-ext/src/lib/embed.ts @@ -64,14 +64,14 @@ export const Embed = Node.create<EmbedOptions>({ }), }, width: { - default: 640, + default: 800, parseHTML: (element) => element.getAttribute("data-width"), renderHTML: (attributes: EmbedAttributes) => ({ "data-width": attributes.width, }), }, height: { - default: 480, + default: 600, parseHTML: (element) => element.getAttribute("data-height"), renderHTML: (attributes: EmbedAttributes) => ({ "data-height": attributes.height, diff --git a/packages/editor-ext/src/lib/excalidraw.ts b/packages/editor-ext/src/lib/excalidraw.ts index 45142776..71c881f1 100644 --- a/packages/editor-ext/src/lib/excalidraw.ts +++ b/packages/editor-ext/src/lib/excalidraw.ts @@ -1,5 +1,6 @@ -import { Node, mergeAttributes, ResizableNodeView } from "@tiptap/core"; -import type { ResizableNodeViewDirection } from "@tiptap/core"; +import { Node, mergeAttributes } from "@tiptap/core"; +import { ResizableNodeView } from "./resizable-nodeview"; +import type { ResizableNodeViewDirection } from "./resizable-nodeview"; import { ReactNodeViewRenderer } from "@tiptap/react"; import { normalizeFileUrl } from "./media-utils"; @@ -320,12 +321,11 @@ export const Excalidraw = Node.create<ExcalidrawOptions>({ // Show skeleton background while image loads from server dom.style.pointerEvents = "none"; - dom.style.background = - "light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))"; + el.classList.add("media-pulse"); el.onload = () => { dom.style.pointerEvents = ""; - dom.style.background = ""; + el.classList.remove("media-pulse"); }; return nodeView; diff --git a/packages/editor-ext/src/lib/image/image.ts b/packages/editor-ext/src/lib/image/image.ts index 8631d7fe..6a921926 100644 --- a/packages/editor-ext/src/lib/image/image.ts +++ b/packages/editor-ext/src/lib/image/image.ts @@ -4,10 +4,10 @@ import { ReactNodeViewRenderer } from "@tiptap/react"; import { mergeAttributes, Range, - ResizableNodeView, } from "@tiptap/core"; +import { ResizableNodeView } from "../resizable-nodeview"; +import type { ResizableNodeViewDirection } from "../resizable-nodeview"; import { normalizeFileUrl } from "../media-utils"; -import type { ResizableNodeViewDirection } from "@tiptap/core"; export type ImageResizeOptions = { enabled: boolean; @@ -362,12 +362,11 @@ export const TiptapImage = Image.extend<ImageOptions>({ // Show skeleton background while image loads from server dom.style.pointerEvents = "none"; - dom.style.background = - "light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))"; + el.classList.add("media-pulse"); el.onload = () => { dom.style.pointerEvents = ""; - dom.style.background = ""; + el.classList.remove("media-pulse"); }; return nodeView; diff --git a/packages/editor-ext/src/lib/pdf/index.ts b/packages/editor-ext/src/lib/pdf/index.ts new file mode 100644 index 00000000..1f81a44c --- /dev/null +++ b/packages/editor-ext/src/lib/pdf/index.ts @@ -0,0 +1,2 @@ +export { TiptapPdf } from "./pdf"; +export * from "./pdf-upload"; diff --git a/packages/editor-ext/src/lib/pdf/pdf-upload.ts b/packages/editor-ext/src/lib/pdf/pdf-upload.ts new file mode 100644 index 00000000..4e545cb4 --- /dev/null +++ b/packages/editor-ext/src/lib/pdf/pdf-upload.ts @@ -0,0 +1,123 @@ +import { MediaUploadOptions, UploadFn } from "../media-utils"; +import { IAttachment } from "../types"; +import { generateNodeId } from "../utils"; +import { Node } from "@tiptap/pm/model"; +import { Command } from "@tiptap/core"; + +const findPdfNodeByPlaceholderId = ( + doc: Node, + placeholderId: string, +): { node: Node; pos: number } | null => { + let result: { node: Node; pos: number } | null = null; + + doc.descendants((node, pos) => { + if (result) return false; + + if ( + node.type.name === "pdf" && + node.attrs.placeholder?.id === placeholderId + ) { + result = { node, pos }; + return false; + } + + return true; + }); + + return result; +}; + +const handlePdfUpload = + ({ validateFn, onUpload }: MediaUploadOptions): UploadFn => + async (file, editor, pos, pageId) => { + const validated = validateFn?.(file); + // @ts-ignore + if (!validated) return; + + const placeholderId = generateNodeId(); + + let placeholderInserted = false; + + const insertPlaceholder = (): Command => { + return ({ tr, state }) => { + const initialPlaceholderNode = state.schema.nodes.pdf?.create({ + placeholder: { + id: placeholderId, + name: file.name, + }, + }); + + if (!initialPlaceholderNode) return false; + + const { parent } = tr.doc.resolve(pos); + const isEmptyTextBlock = parent.isTextblock && !parent.childCount; + + if (isEmptyTextBlock) { + tr.replaceRangeWith(pos - 1, pos + 1, initialPlaceholderNode); + } else { + tr.insert(pos, initialPlaceholderNode); + } + + return true; + }; + }; + + const replacePlaceholderWithPdf = (attachment: IAttachment): Command => { + return ({ tr }) => { + const { pos: currentPos = null } = + findPdfNodeByPlaceholderId(tr.doc, placeholderId) || {}; + + if (currentPos === null || !attachment) return; + + tr.setNodeMarkup(currentPos, undefined, { + src: `/api/files/${attachment.id}/${attachment.fileName}`, + name: attachment.fileName, + attachmentId: attachment.id, + size: attachment.fileSize, + }); + + return true; + }; + }; + + const removePlaceholder = (): Command => { + return ({ tr }) => { + const { pos: currentPos = null } = + findPdfNodeByPlaceholderId(tr.doc, placeholderId) || {}; + + if (currentPos === null) return false; + + tr.delete(currentPos, currentPos + 2); + + return true; + }; + }; + + const insertPlaceholderTimeout = setTimeout(() => { + editor.commands.command(insertPlaceholder()); + placeholderInserted = true; + }, 250); + + try { + const attachment: IAttachment = await onUpload(file, pageId); + + clearTimeout(insertPlaceholderTimeout); + + if (placeholderInserted) { + setTimeout(() => { + editor.commands.command(replacePlaceholderWithPdf(attachment)); + }, 100); + } else { + editor + .chain() + .command(insertPlaceholder()) + .command(replacePlaceholderWithPdf(attachment)) + .run(); + } + } catch (error) { + clearTimeout(insertPlaceholderTimeout); + editor.commands.command(removePlaceholder()); + } + }; + +export { handlePdfUpload }; diff --git a/packages/editor-ext/src/lib/pdf/pdf.ts b/packages/editor-ext/src/lib/pdf/pdf.ts new file mode 100644 index 00000000..c0e2dbe7 --- /dev/null +++ b/packages/editor-ext/src/lib/pdf/pdf.ts @@ -0,0 +1,156 @@ +import { ReactNodeViewRenderer } from "@tiptap/react"; +import { Node, mergeAttributes } from "@tiptap/core"; +import { sanitizeUrl, isInternalFileUrl } from "../utils"; + +export type PdfOptions = { + view: any; + HTMLAttributes: Record<string, any>; +}; + +export type PdfAttributes = { + src?: string; + name?: string; + attachmentId?: string; + size?: number; + width?: number; + height?: number; + placeholder?: { + id: string; + name: string; + }; +}; + +declare module "@tiptap/core" { + interface Commands<ReturnType> { + pdfBlock: { + setPdf: (attributes: PdfAttributes) => ReturnType; + }; + } +} + +export const TiptapPdf = Node.create<PdfOptions>({ + name: "pdf", + + group: "block", + isolating: true, + atom: true, + defining: true, + draggable: true, + + addOptions() { + return { + view: null, + HTMLAttributes: {}, + }; + }, + + addAttributes() { + return { + src: { + default: "", + parseHTML: (element) => { + const src = element.getAttribute("src"); + const sanitized = sanitizeUrl(src); + return isInternalFileUrl(sanitized) ? sanitized : ""; + }, + renderHTML: (attributes) => ({ + src: isInternalFileUrl(attributes.src) ? sanitizeUrl(attributes.src) : "", + }), + }, + name: { + default: undefined, + parseHTML: (element) => element.getAttribute("data-name"), + renderHTML: (attributes: PdfAttributes) => ({ + "data-name": attributes.name, + }), + }, + attachmentId: { + default: undefined, + parseHTML: (element) => element.getAttribute("data-attachment-id"), + renderHTML: (attributes: PdfAttributes) => ({ + "data-attachment-id": attributes.attachmentId, + }), + }, + size: { + default: null, + parseHTML: (element) => element.getAttribute("data-size"), + renderHTML: (attributes: PdfAttributes) => ({ + "data-size": attributes.size, + }), + }, + width: { + default: 800, + parseHTML: (element) => { + const raw = element.getAttribute("width"); + if (!raw) return null; + const num = parseFloat(raw); + return isNaN(num) ? null : num; + }, + renderHTML: (attributes: PdfAttributes) => ({ + width: attributes.width, + }), + }, + height: { + default: 600, + parseHTML: (element) => { + const raw = element.getAttribute("height"); + if (!raw) return null; + const num = parseFloat(raw); + return isNaN(num) ? null : num; + }, + renderHTML: (attributes: PdfAttributes) => ({ + height: attributes.height, + }), + }, + placeholder: { + default: null, + rendered: false, + }, + }; + }, + + parseHTML() { + return [ + { + tag: `div[data-type="${this.name}"]`, + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return [ + "div", + mergeAttributes( + { "data-type": this.name }, + this.options.HTMLAttributes, + HTMLAttributes, + ), + [ + "iframe", + { + src: isInternalFileUrl(HTMLAttributes.src) ? sanitizeUrl(HTMLAttributes.src) : "", + width: HTMLAttributes.width || 800, + height: HTMLAttributes.height || 600, + }, + ], + ]; + }, + + addCommands() { + return { + setPdf: + (attrs: PdfAttributes) => + ({ commands }) => { + return commands.insertContent({ + type: "pdf", + attrs, + }); + }, + }; + }, + + addNodeView() { + this.editor.isInitialized = true; + return ReactNodeViewRenderer(this.options.view); + }, +}); diff --git a/packages/editor-ext/src/lib/resizable-nodeview.ts b/packages/editor-ext/src/lib/resizable-nodeview.ts new file mode 100644 index 00000000..d0352848 --- /dev/null +++ b/packages/editor-ext/src/lib/resizable-nodeview.ts @@ -0,0 +1,1098 @@ +// https://github.com/ueberdosis/tiptap/blob/91c51be53c4655ef07e29ec489471524debfa0ca/packages/core/src/lib/ResizableNodeView.ts - MIT +import type { Node as PMNode } from '@tiptap/pm/model'; +import type { Decoration, DecorationSource, NodeView } from '@tiptap/pm/view'; +import type { Editor } from '@tiptap/core'; + +const isTouchEvent = (e: MouseEvent | TouchEvent): e is TouchEvent => { + return 'touches' in e; +}; + +/** + * Directions where resize handles can be placed + * + * @example + * - `'top'` - Top edge handle + * - `'bottom-right'` - Bottom-right corner handle + */ +export type ResizableNodeViewDirection = + | 'top' + | 'right' + | 'bottom' + | 'left' + | 'top-right' + | 'top-left' + | 'bottom-right' + | 'bottom-left'; + +/** + * Dimensions for the resizable node in pixels + */ +export type ResizableNodeDimensions = { + /** Width in pixels */ + width: number; + /** Height in pixels */ + height: number; +}; + +/** + * Configuration options for creating a ResizableNodeView + * + * @example + * ```ts + * new ResizableNodeView({ + * element: imgElement, + * node, + * getPos, + * onResize: (width, height) => { + * imgElement.style.width = `${width}px` + * imgElement.style.height = `${height}px` + * }, + * onCommit: (width, height) => { + * editor.commands.updateAttributes('image', { width, height }) + * }, + * onUpdate: (node) => true, + * options: { + * directions: ['bottom-right', 'bottom-left'], + * min: { width: 100, height: 100 }, + * preserveAspectRatio: true + * } + * }) + * ``` + */ +export type ResizableNodeViewOptions = { + /** + * The DOM element to make resizable (e.g., an img, video, or iframe element) + */ + element: HTMLElement; + + /** + * The DOM element that will hold the editable content element + */ + contentElement?: HTMLElement; + + /** + * The ProseMirror node instance + */ + node: PMNode; + + /** + * The Tiptap editor instance + */ + editor: Editor; + + /** + * Function that returns the current position of the node in the document + */ + getPos: () => number | undefined; + + /** + * Callback fired continuously during resize with current dimensions. + * Use this to update the element's visual size in real-time. + * + * @param width - Current width in pixels + * @param height - Current height in pixels + * + * @example + * ```ts + * onResize: (width, height) => { + * element.style.width = `${width}px` + * element.style.height = `${height}px` + * } + * ``` + */ + onResize?: (width: number, height: number) => void; + + /** + * Callback fired once when resize completes with final dimensions. + * Use this to persist the new size to the node's attributes. + * + * @param width - Final width in pixels + * @param height - Final height in pixels + * + * @example + * ```ts + * onCommit: (width, height) => { + * const pos = getPos() + * if (pos !== undefined) { + * editor.commands.updateAttributes('image', { width, height }) + * } + * } + * ``` + */ + onCommit: (width: number, height: number) => void; + + /** + * Callback for handling node updates. + * Return `true` to accept the update, `false` to reject it. + * + * @example + * ```ts + * onUpdate: (node, decorations, innerDecorations) => { + * if (node.type !== this.node.type) return false + * return true + * } + * ``` + */ + onUpdate: NodeView['update']; + + /** + * Optional configuration for resize behavior and styling + */ + options?: { + /** + * Which resize handles to display. + * @default ['bottom-left', 'bottom-right', 'top-left', 'top-right'] + * + * @example + * ```ts + * // Only show corner handles + * directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'] + * + * // Only show right edge handle + * directions: ['right'] + * ``` + */ + directions?: ResizableNodeViewDirection[]; + + /** + * Minimum dimensions in pixels + * @default { width: 8, height: 8 } + * + * @example + * ```ts + * min: { width: 100, height: 50 } + * ``` + */ + min?: Partial<ResizableNodeDimensions>; + + /** + * Maximum dimensions in pixels + * @default undefined (no maximum) + * + * @example + * ```ts + * max: { width: 1000, height: 800 } + * ``` + */ + max?: Partial<ResizableNodeDimensions>; + + /** + * Always preserve aspect ratio when resizing. + * When `false`, aspect ratio is preserved only when Shift key is pressed. + * @default false + * + * @example + * ```ts + * preserveAspectRatio: true // Always lock aspect ratio + * ``` + */ + preserveAspectRatio?: boolean; + + /** + * Custom CSS class names for styling + * + * @example + * ```ts + * className: { + * container: 'resize-container', + * wrapper: 'resize-wrapper', + * handle: 'resize-handle', + * resizing: 'is-resizing' + * } + * ``` + */ + className?: { + /** Class for the outer container element */ + container?: string; + /** Class for the wrapper element that contains the resizable element */ + wrapper?: string; + /** Class applied to all resize handles */ + handle?: string; + /** Class added to container while actively resizing */ + resizing?: string; + }; + + /** + * Optional callback for creating custom resize handle elements. + * + * This function allows developers to define their own handle element + * (e.g., custom icons, classes, or styles) for a given resize direction. + * It is called internally for each handle direction. + * + * @param direction - The direction of the handle being created (e.g., 'top', 'bottom-right'). + * @returns The custom handle HTMLElement. + * + * @example + * ```ts + * createCustomHandle: (direction) => { + * const handle = document.createElement('div') + * handle.dataset.resizeHandle = direction + * handle.style.position = 'absolute' + * handle.className = 'tiptap-custom-handle' + * + * const isTop = direction.includes('top') + * const isBottom = direction.includes('bottom') + * const isLeft = direction.includes('left') + * const isRight = direction.includes('right') + * + * if (isTop) handle.style.top = '0' + * if (isBottom) handle.style.bottom = '0' + * if (isLeft) handle.style.left = '0' + * if (isRight) handle.style.right = '0' + * + * // Edge handles span the full width or height + * if (direction === 'top' || direction === 'bottom') { + * handle.style.left = '0' + * handle.style.right = '0' + * } + * + * if (direction === 'left' || direction === 'right') { + * handle.style.top = '0' + * handle.style.bottom = '0' + * } + * + * return handle + * } + * ``` + */ + createCustomHandle?: (direction: ResizableNodeViewDirection) => HTMLElement; + }; +}; + +/** + * A NodeView implementation that adds resize handles to any DOM element. + * + * This class creates a resizable node view for Tiptap/ProseMirror editors. + * It wraps your element with resize handles and manages the resize interaction, + * including aspect ratio preservation, min/max constraints, and keyboard modifiers. + * + * @example + * ```ts + * // Basic usage in a Tiptap extension + * addNodeView() { + * return ({ node, getPos }) => { + * const img = document.createElement('img') + * img.src = node.attrs.src + * + * return new ResizableNodeView({ + * element: img, + * node, + * getPos, + * onResize: (width, height) => { + * img.style.width = `${width}px` + * img.style.height = `${height}px` + * }, + * onCommit: (width, height) => { + * this.editor.commands.updateAttributes('image', { width, height }) + * }, + * onUpdate: () => true, + * options: { + * min: { width: 100, height: 100 }, + * preserveAspectRatio: true + * } + * }) + * } + * } + * ``` + */ +export class ResizableNodeView { + /** The ProseMirror node instance */ + node: PMNode; + + /** The Tiptap editor instance */ + editor: Editor; + + /** The DOM element being made resizable */ + element: HTMLElement; + + /** The editable DOM element inside the DOM */ + contentElement?: HTMLElement; + + /** The outer container element (returned as NodeView.dom) */ + container: HTMLElement; + + /** The wrapper element that contains the element and handles */ + wrapper: HTMLElement; + + /** Function to get the current node position */ + getPos: () => number | undefined; + + /** Callback fired during resize */ + onResize?: (width: number, height: number) => void; + + /** Callback fired when resize completes */ + onCommit: (width: number, height: number) => void; + + /** Callback for node updates */ + onUpdate?: NodeView['update']; + + /** Active resize handle directions */ + directions: ResizableNodeViewDirection[] = [ + 'bottom-left', + 'bottom-right', + 'top-left', + 'top-right', + ]; + + /** Minimum allowed dimensions */ + minSize: ResizableNodeDimensions = { + height: 8, + width: 8, + }; + + /** Maximum allowed dimensions (optional) */ + maxSize?: Partial<ResizableNodeDimensions>; + + /** Whether to always preserve aspect ratio */ + preserveAspectRatio: boolean = false; + + /** CSS class names for elements */ + classNames = { + container: '', + wrapper: '', + handle: '', + resizing: '', + }; + + /** Optional callback for creating custom resize handles */ + createCustomHandle?: (direction: ResizableNodeViewDirection) => HTMLElement; + + /** Initial width of the element (for aspect ratio calculation) */ + private initialWidth: number = 0; + + /** Initial height of the element (for aspect ratio calculation) */ + private initialHeight: number = 0; + + /** Calculated aspect ratio (width / height) */ + private aspectRatio: number = 1; + + /** Whether a resize operation is currently active */ + private isResizing: boolean = false; + + /** The handle currently being dragged */ + private activeHandle: ResizableNodeViewDirection | null = null; + + /** Starting mouse X position when resize began */ + private startX: number = 0; + + /** Starting mouse Y position when resize began */ + private startY: number = 0; + + /** Element width when resize began */ + private startWidth: number = 0; + + /** Element height when resize began */ + private startHeight: number = 0; + + /** Whether Shift key is currently pressed (for temporary aspect ratio lock) */ + private isShiftKeyPressed: boolean = false; + + /** Last known editable state of the editor */ + private lastEditableState: boolean | undefined = undefined; + + /** Map of handle elements by direction */ + private handleMap = new Map<ResizableNodeViewDirection, HTMLElement>(); + + /** + * Creates a new ResizableNodeView instance. + * + * The constructor sets up the resize handles, applies initial sizing from + * node attributes, and configures all resize behavior options. + * + * @param options - Configuration options for the resizable node view + */ + constructor(options: ResizableNodeViewOptions) { + this.node = options.node; + this.editor = options.editor; + this.element = options.element; + this.contentElement = options.contentElement; + + this.getPos = options.getPos; + + this.onResize = options.onResize; + this.onCommit = options.onCommit; + this.onUpdate = options.onUpdate; + + if (options.options?.min) { + this.minSize = { + ...this.minSize, + ...options.options.min, + }; + } + + if (options.options?.max) { + this.maxSize = options.options.max; + } + + if (options?.options?.directions) { + this.directions = options.options.directions; + } + + if (options.options?.preserveAspectRatio) { + this.preserveAspectRatio = options.options.preserveAspectRatio; + } + + if (options.options?.className) { + this.classNames = { + container: options.options.className.container || '', + wrapper: options.options.className.wrapper || '', + handle: options.options.className.handle || '', + resizing: options.options.className.resizing || '', + }; + } + + if (options.options?.createCustomHandle) { + this.createCustomHandle = options.options.createCustomHandle; + } + + this.wrapper = this.createWrapper(); + this.container = this.createContainer(); + + this.applyInitialSize(); + + if (this.editor.isEditable) { + this.attachHandles(); + } + + this.editor.on('update', this.handleEditorUpdate.bind(this)); + } + + /** + * Returns the top-level DOM node that should be placed in the editor. + * + * This is required by the ProseMirror NodeView interface. The container + * includes the wrapper, handles, and the actual content element. + * + * @returns The container element to be inserted into the editor + */ + get dom() { + return this.container; + } + + get contentDOM(): HTMLElement | null { + return this.contentElement ?? null; + } + + private handleEditorUpdate() { + const isEditable = this.editor.isEditable; + + // Only if state actually changed + if (isEditable === this.lastEditableState) { + return; + } + + this.lastEditableState = isEditable; + + if (!isEditable) { + this.removeHandles(); + } else if (isEditable && this.handleMap.size === 0) { + this.attachHandles(); + } + } + + /** + * Called when the node's content or attributes change. + * + * Updates the internal node reference. If a custom `onUpdate` callback + * was provided, it will be called to handle additional update logic. + * + * @param node - The new/updated node + * @param decorations - Node decorations + * @param innerDecorations - Inner decorations + * @returns `false` if the node type has changed (requires full rebuild), otherwise the result of `onUpdate` or `true` + */ + update( + node: PMNode, + decorations: readonly Decoration[], + innerDecorations: DecorationSource, + ): boolean { + if (node.type !== this.node.type) { + return false; + } + + this.node = node; + + if (this.onUpdate) { + return this.onUpdate(node, decorations, innerDecorations); + } + + return true; + } + + /** + * Cleanup method called when the node view is being removed. + * + * Removes all event listeners to prevent memory leaks. This is required + * by the ProseMirror NodeView interface. If a resize is active when + * destroy is called, it will be properly cancelled. + */ + destroy() { + if (this.isResizing) { + this.container.dataset.resizeState = 'false'; + + if (this.classNames.resizing) { + this.container.classList.remove(this.classNames.resizing); + } + + document.removeEventListener('mousemove', this.handleMouseMove); + document.removeEventListener('touchmove', this.handleTouchMove); + document.removeEventListener('mouseup', this.handleMouseUp); + document.removeEventListener('touchend', this.handleMouseUp); + window.removeEventListener('blur', this.handleMouseUp); + document.removeEventListener('keydown', this.handleKeyDown); + document.removeEventListener('keyup', this.handleKeyUp); + this.isResizing = false; + this.activeHandle = null; + } + + this.editor.off('update', this.handleEditorUpdate.bind(this)); + + this.container.remove(); + } + + /** + * Creates the outer container element. + * + * The container is the top-level element returned by the NodeView and + * wraps the entire resizable node. It's set up with flexbox to handle + * alignment and includes data attributes for styling and identification. + * + * @returns The container element + */ + createContainer() { + const element = document.createElement('div'); + element.dataset.resizeContainer = ''; + element.dataset.node = this.node.type.name; + element.style.display = 'flex'; + + if (this.classNames.container) { + element.className = this.classNames.container; + } + + element.appendChild(this.wrapper); + + return element; + } + + /** + * Creates the wrapper element that contains the content and handles. + * + * The wrapper uses relative positioning so that resize handles can be + * positioned absolutely within it. This is the direct parent of the + * content element being made resizable. + * + * @returns The wrapper element + */ + createWrapper() { + const element = document.createElement('div'); + element.style.position = 'relative'; + element.style.display = 'block'; + element.dataset.resizeWrapper = ''; + + if (this.classNames.wrapper) { + element.className = this.classNames.wrapper; + } + + element.appendChild(this.element); + + return element; + } + + /** + * Creates a resize handle element for a specific direction. + * + * Each handle is absolutely positioned and includes a data attribute + * identifying its direction for styling purposes. + * + * @param direction - The resize direction for this handle + * @returns The handle element + */ + private createHandle(direction: ResizableNodeViewDirection): HTMLElement { + const handle = document.createElement('div'); + handle.dataset.resizeHandle = direction; + handle.style.position = 'absolute'; + + if (this.classNames.handle) { + handle.className = this.classNames.handle; + } + + return handle; + } + + /** + * Positions a handle element according to its direction. + * + * Corner handles (e.g., 'top-left') are positioned at the intersection + * of two edges. Edge handles (e.g., 'top') span the full width or height. + * + * @param handle - The handle element to position + * @param direction - The direction determining the position + */ + private positionHandle( + handle: HTMLElement, + direction: ResizableNodeViewDirection, + ): void { + const isTop = direction.includes('top'); + const isBottom = direction.includes('bottom'); + const isLeft = direction.includes('left'); + const isRight = direction.includes('right'); + + if (isTop) { + handle.style.top = '0'; + } + + if (isBottom) { + handle.style.bottom = '0'; + } + + if (isLeft) { + handle.style.left = '0'; + } + + if (isRight) { + handle.style.right = '0'; + } + + // Edge handles span the full width or height + if (direction === 'top' || direction === 'bottom') { + handle.style.left = '0'; + handle.style.right = '0'; + } + + if (direction === 'left' || direction === 'right') { + handle.style.top = '0'; + handle.style.bottom = '0'; + } + } + + /** + * Creates and attaches all resize handles to the wrapper. + * + * Iterates through the configured directions, creates a handle for each, + * positions it, attaches the mousedown listener, and appends it to the DOM. + */ + private attachHandles(): void { + this.directions.forEach((direction) => { + let handle: HTMLElement; + + if (this.createCustomHandle) { + handle = this.createCustomHandle(direction); + } else { + handle = this.createHandle(direction); + } + + if (!(handle instanceof HTMLElement)) { + console.warn( + `[ResizableNodeView] createCustomHandle("${direction}") did not return an HTMLElement. Falling back to default handle.`, + ); + handle = this.createHandle(direction); + } + + if (!this.createCustomHandle) { + this.positionHandle(handle, direction); + } + + handle.addEventListener('mousedown', (event) => + this.handleResizeStart(event, direction), + ); + handle.addEventListener('touchstart', (event) => + this.handleResizeStart(event as unknown as MouseEvent, direction), + ); + + this.handleMap.set(direction, handle); + + this.wrapper.appendChild(handle); + }); + } + + /** + * Removes all resize handles from the wrapper. + * + * Cleans up the handle map and removes each handle element from the DOM. + */ + private removeHandles(): void { + this.handleMap.forEach((el) => el.remove()); + this.handleMap.clear(); + } + + /** + * Applies initial sizing from node attributes to the element. + * + * If width/height attributes exist on the node, they're applied to the element. + * Otherwise, the element's natural/current dimensions are measured. The aspect + * ratio is calculated for later use in aspect-ratio-preserving resizes. + */ + private applyInitialSize(): void { + const width = this.node.attrs.width as number | undefined; + const height = this.node.attrs.height as number | undefined; + + if (width) { + this.element.style.width = `${width}px`; + this.initialWidth = width; + } else { + this.initialWidth = this.element.offsetWidth; + } + + if (height) { + this.element.style.height = `${height}px`; + this.initialHeight = height; + } else { + this.initialHeight = this.element.offsetHeight; + } + + // Calculate aspect ratio for use during resizing + if (this.initialWidth > 0 && this.initialHeight > 0) { + this.aspectRatio = this.initialWidth / this.initialHeight; + } + } + + /** + * Initiates a resize operation when a handle is clicked. + * + * Captures the starting mouse position and element dimensions, sets up + * the resize state, adds the resizing class and state attribute, and + * attaches document-level listeners for mouse movement and keyboard input. + * + * @param event - The mouse down event + * @param direction - The direction of the handle being dragged + */ + private handleResizeStart( + event: MouseEvent | TouchEvent, + direction: ResizableNodeViewDirection, + ): void { + event.preventDefault(); + event.stopPropagation(); + + // Capture initial state + this.isResizing = true; + this.activeHandle = direction; + + if (isTouchEvent(event)) { + this.startX = event.touches[0].clientX; + this.startY = event.touches[0].clientY; + } else { + this.startX = event.clientX; + this.startY = event.clientY; + } + + this.startWidth = this.element.offsetWidth; + this.startHeight = this.element.offsetHeight; + + // Recalculate aspect ratio at resize start for accuracy + if (this.startWidth > 0 && this.startHeight > 0) { + this.aspectRatio = this.startWidth / this.startHeight; + } + + const pos = this.getPos(); + if (pos !== undefined) { + // TODO: Select the node in the editor + } + + // Update UI state + this.container.dataset.resizeState = 'true'; + + if (this.classNames.resizing) { + this.container.classList.add(this.classNames.resizing); + } + + // Attach document-level listeners for resize + document.addEventListener('mousemove', this.handleMouseMove); + document.addEventListener('touchmove', this.handleTouchMove); + document.addEventListener('mouseup', this.handleMouseUp); + document.addEventListener('touchend', this.handleMouseUp); + window.addEventListener('blur', this.handleMouseUp); + document.addEventListener('keydown', this.handleKeyDown); + document.addEventListener('keyup', this.handleKeyUp); + } + + /** + * Handles mouse movement during an active resize. + * + * Calculates the delta from the starting position, computes new dimensions + * based on the active handle direction, applies constraints and aspect ratio, + * then updates the element's style and calls the onResize callback. + * + * @param event - The mouse move event + */ + private handleMouseMove = (event: MouseEvent): void => { + if (!this.isResizing || !this.activeHandle) { + return; + } + + const deltaX = event.clientX - this.startX; + const deltaY = event.clientY - this.startY; + + this.handleResize(deltaX, deltaY); + }; + + private handleTouchMove = (event: TouchEvent): void => { + if (!this.isResizing || !this.activeHandle) { + return; + } + + const touch = event.touches[0]; + if (!touch) { + return; + } + + const deltaX = touch.clientX - this.startX; + const deltaY = touch.clientY - this.startY; + + this.handleResize(deltaX, deltaY); + }; + + private handleResize(deltaX: number, deltaY: number) { + if (!this.activeHandle) { + return; + } + + const shouldPreserveAspectRatio = + this.preserveAspectRatio || this.isShiftKeyPressed; + const { width, height } = this.calculateNewDimensions( + this.activeHandle, + deltaX, + deltaY, + ); + const constrained = this.applyConstraints( + width, + height, + shouldPreserveAspectRatio, + ); + + this.element.style.width = `${constrained.width}px`; + this.element.style.height = `${constrained.height}px`; + + if (this.onResize) { + this.onResize(constrained.width, constrained.height); + } + } + + /** + * Completes the resize operation when the mouse button is released. + * + * Captures final dimensions, calls the onCommit callback to persist changes, + * removes the resizing state and class, and cleans up document-level listeners. + */ + private handleMouseUp = (): void => { + if (!this.isResizing) { + return; + } + + const finalWidth = this.element.offsetWidth; + const finalHeight = this.element.offsetHeight; + + this.onCommit(finalWidth, finalHeight); + + this.isResizing = false; + this.activeHandle = null; + + // Remove UI state + this.container.dataset.resizeState = 'false'; + + if (this.classNames.resizing) { + this.container.classList.remove(this.classNames.resizing); + } + + // Clean up document-level listeners + document.removeEventListener('mousemove', this.handleMouseMove); + document.removeEventListener('touchmove', this.handleTouchMove); + document.removeEventListener('mouseup', this.handleMouseUp); + document.removeEventListener('touchend', this.handleMouseUp); + window.removeEventListener('blur', this.handleMouseUp); + document.removeEventListener('keydown', this.handleKeyDown); + document.removeEventListener('keyup', this.handleKeyUp); + }; + + /** + * Tracks Shift key state to enable temporary aspect ratio locking. + * + * When Shift is pressed during resize, aspect ratio is preserved even if + * preserveAspectRatio is false. + * + * @param event - The keyboard event + */ + private handleKeyDown = (event: KeyboardEvent): void => { + if (event.key === 'Shift') { + this.isShiftKeyPressed = true; + } + }; + + /** + * Tracks Shift key release to disable temporary aspect ratio locking. + * + * @param event - The keyboard event + */ + private handleKeyUp = (event: KeyboardEvent): void => { + if (event.key === 'Shift') { + this.isShiftKeyPressed = false; + } + }; + + /** + * Calculates new dimensions based on mouse delta and resize direction. + * + * Takes the starting dimensions and applies the mouse movement delta + * according to the handle direction. For corner handles, both dimensions + * are affected. For edge handles, only one dimension changes. If aspect + * ratio should be preserved, delegates to applyAspectRatio. + * + * @param direction - The active resize handle direction + * @param deltaX - Horizontal mouse movement since resize start + * @param deltaY - Vertical mouse movement since resize start + * @returns The calculated width and height + */ + private calculateNewDimensions( + direction: ResizableNodeViewDirection, + deltaX: number, + deltaY: number, + ): ResizableNodeDimensions { + let newWidth = this.startWidth; + let newHeight = this.startHeight; + + const isRight = direction.includes('right'); + const isLeft = direction.includes('left'); + const isBottom = direction.includes('bottom'); + const isTop = direction.includes('top'); + + // Apply horizontal delta + if (isRight) { + newWidth = this.startWidth + deltaX; + } else if (isLeft) { + newWidth = this.startWidth - deltaX; + } + + // Apply vertical delta + if (isBottom) { + newHeight = this.startHeight + deltaY; + } else if (isTop) { + newHeight = this.startHeight - deltaY; + } + + // For pure horizontal/vertical handles, only one dimension changes + if (direction === 'right' || direction === 'left') { + newWidth = this.startWidth + (isRight ? deltaX : -deltaX); + } + + if (direction === 'top' || direction === 'bottom') { + newHeight = this.startHeight + (isBottom ? deltaY : -deltaY); + } + + const shouldPreserveAspectRatio = + this.preserveAspectRatio || this.isShiftKeyPressed; + + if (shouldPreserveAspectRatio) { + return this.applyAspectRatio(newWidth, newHeight, direction); + } + + return { width: newWidth, height: newHeight }; + } + + /** + * Applies min/max constraints to dimensions. + * + * When aspect ratio is NOT preserved, constraints are applied independently + * to width and height. When aspect ratio IS preserved, constraints are + * applied while maintaining the aspect ratio—if one dimension hits a limit, + * the other is recalculated proportionally. + * + * This ensures that aspect ratio is never broken when constrained. + * + * @param width - The unconstrained width + * @param height - The unconstrained height + * @param preserveAspectRatio - Whether to maintain aspect ratio while constraining + * @returns The constrained dimensions + */ + private applyConstraints( + width: number, + height: number, + preserveAspectRatio: boolean, + ): ResizableNodeDimensions { + if (!preserveAspectRatio) { + // Independent constraints for each dimension + let constrainedWidth = Math.max(this.minSize.width, width); + let constrainedHeight = Math.max(this.minSize.height, height); + + if (this.maxSize?.width) { + constrainedWidth = Math.min(this.maxSize.width, constrainedWidth); + } + + if (this.maxSize?.height) { + constrainedHeight = Math.min(this.maxSize.height, constrainedHeight); + } + + return { width: constrainedWidth, height: constrainedHeight }; + } + + // Aspect-ratio-aware constraints: adjust both dimensions proportionally + let constrainedWidth = width; + let constrainedHeight = height; + + // Check minimum constraints + if (constrainedWidth < this.minSize.width) { + constrainedWidth = this.minSize.width; + constrainedHeight = constrainedWidth / this.aspectRatio; + } + + if (constrainedHeight < this.minSize.height) { + constrainedHeight = this.minSize.height; + constrainedWidth = constrainedHeight * this.aspectRatio; + } + + // Check maximum constraints + if (this.maxSize?.width && constrainedWidth > this.maxSize.width) { + constrainedWidth = this.maxSize.width; + constrainedHeight = constrainedWidth / this.aspectRatio; + } + + if (this.maxSize?.height && constrainedHeight > this.maxSize.height) { + constrainedHeight = this.maxSize.height; + constrainedWidth = constrainedHeight * this.aspectRatio; + } + + return { width: constrainedWidth, height: constrainedHeight }; + } + + /** + * Adjusts dimensions to maintain the original aspect ratio. + * + * For horizontal handles (left/right), uses width as the primary dimension + * and calculates height from it. For vertical handles (top/bottom), uses + * height as primary and calculates width. For corner handles, uses width + * as the primary dimension. + * + * @param width - The new width + * @param height - The new height + * @param direction - The active resize direction + * @returns Dimensions adjusted to preserve aspect ratio + */ + private applyAspectRatio( + width: number, + height: number, + direction: ResizableNodeViewDirection, + ): ResizableNodeDimensions { + const isHorizontal = direction === 'left' || direction === 'right'; + const isVertical = direction === 'top' || direction === 'bottom'; + + if (isHorizontal) { + // For horizontal resize, width is primary + return { + width, + height: width / this.aspectRatio, + }; + } + + if (isVertical) { + // For vertical resize, height is primary + return { + width: height * this.aspectRatio, + height, + }; + } + + // For corner resize, width is primary + return { + width, + height: width / this.aspectRatio, + }; + } +} diff --git a/packages/editor-ext/src/lib/utils.ts b/packages/editor-ext/src/lib/utils.ts index 4c0b798e..3bfd0177 100644 --- a/packages/editor-ext/src/lib/utils.ts +++ b/packages/editor-ext/src/lib/utils.ts @@ -382,6 +382,12 @@ export function sanitizeUrl(url: string | undefined): string { return sanitized === "about:blank" ? "" : sanitized; } +export function isInternalFileUrl(url: string | undefined): boolean { + if (!url) return false; + const normalized = url.trim(); + return normalized.startsWith("/api/files/") || normalized.startsWith("/files/"); +} + const alphabet = "abcdefghijklmnopqrstuvwxyz"; export const generateNodeId = customAlphabet(alphabet, 12); diff --git a/packages/editor-ext/src/lib/video/video.ts b/packages/editor-ext/src/lib/video/video.ts index 75bc3910..c6d7356b 100644 --- a/packages/editor-ext/src/lib/video/video.ts +++ b/packages/editor-ext/src/lib/video/video.ts @@ -1,7 +1,8 @@ import { ReactNodeViewRenderer } from "@tiptap/react"; -import { Range, Node, mergeAttributes, ResizableNodeView } from "@tiptap/core"; +import { Range, Node, mergeAttributes } from "@tiptap/core"; +import { ResizableNodeView } from "../resizable-nodeview"; +import type { ResizableNodeViewDirection } from "../resizable-nodeview"; import { normalizeFileUrl } from "../media-utils"; -import type { ResizableNodeViewDirection } from "@tiptap/core"; export type VideoResizeOptions = { enabled: boolean; @@ -328,12 +329,11 @@ export const TiptapVideo = Node.create<VideoOptions>({ // Show skeleton background while video loads from server dom.style.pointerEvents = "none"; - dom.style.background = - "light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))"; + el.classList.add("media-pulse"); el.onloadedmetadata = () => { dom.style.pointerEvents = ""; - dom.style.background = ""; + el.classList.remove("media-pulse"); }; return nodeView; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22d805cd..2c6effe4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,9 +34,6 @@ overrides: yaml@>=2.0.0 <2.8.3: 2.8.3 patchedDependencies: - '@tiptap/core': - hash: efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00 - path: patches/@tiptap__core.patch react-arborist@3.4.0: hash: 419b3b02e24afe928cc006a006f6e906666aff19aa6fd7daaa788ccc2202678a path: patches/react-arborist@3.4.0.patch @@ -65,7 +62,7 @@ importers: version: 3.4.4(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) '@hocuspocus/transformer': specifier: 3.4.4 - version: 3.4.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30) + version: 3.4.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30) '@joplin/turndown': specifier: ^4.0.82 version: 4.0.82 @@ -77,85 +74,88 @@ importers: version: 3.0.0 '@tiptap/core': specifier: 3.20.4 - version: 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/pm@3.20.4) + '@tiptap/extension-audio': + specifier: 3.20.4 + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-code-block': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-collaboration': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30) '@tiptap/extension-collaboration-caret': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30)) '@tiptap/extension-color': specifier: 3.20.4 - version: 3.20.4(@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))) + version: 3.20.4(@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))) '@tiptap/extension-document': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-heading': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-highlight': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-history': specifier: 3.20.4 - version: 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) '@tiptap/extension-image': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-link': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-list': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-placeholder': specifier: 3.20.4 - version: 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) '@tiptap/extension-subscript': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-superscript': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-table': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-text': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-text-align': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-text-style': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-typography': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/extension-unique-id': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/extension-youtube': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/html': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4) '@tiptap/pm': specifier: 3.20.4 version: 3.20.4 '@tiptap/react': specifier: 3.20.4 - version: 3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tiptap/starter-kit': specifier: 3.20.4 version: 3.20.4 '@tiptap/suggestion': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/y-tiptap': specifier: 3.0.2 version: 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) @@ -4662,6 +4662,11 @@ packages: peerDependencies: '@tiptap/pm': ^3.20.4 + '@tiptap/extension-audio@3.20.4': + resolution: {integrity: sha512-zX90pxpEYpV5jSrwtQw8Nmh2uK4WC+xwSG5MXVh4VLG8SnSE/vg/vCCqFiSHjXNfw68dctd6HJ0MJigwnuS0lw==} + peerDependencies: + '@tiptap/core': ^3.20.4 + '@tiptap/extension-blockquote@3.20.4': resolution: {integrity: sha512-9sskyyhYj2oKat//lyZVXCp9YrPt4oJAZnGHYWXS0xlskjsLElrfKKlM4vpbhGss3VrhQRoEGqWLnIaJYPF1zw==} peerDependencies: @@ -12707,9 +12712,9 @@ snapshots: - bufferutil - utf-8-validate - '@hocuspocus/transformer@3.4.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)': + '@hocuspocus/transformer@3.4.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(y-prosemirror@1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 '@tiptap/starter-kit': 3.20.4 y-prosemirror: 1.3.7(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) @@ -15281,191 +15286,195 @@ snapshots: '@tanstack/query-core': 5.90.17 react: 18.3.1 - '@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)': + '@tiptap/core@3.20.4(@tiptap/pm@3.20.4)': dependencies: '@tiptap/pm': 3.20.4 - '@tiptap/extension-blockquote@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-audio@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-bold@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-blockquote@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-bubble-menu@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-bold@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': + dependencies: + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) + + '@tiptap/extension-bubble-menu@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: '@floating-ui/dom': 1.7.4 - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 optional: true - '@tiptap/extension-bullet-list@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-bullet-list@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-code-block@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-code-block@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/extension-code@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-code@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-collaboration-caret@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))': + '@tiptap/extension-collaboration-caret@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 '@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) - '@tiptap/extension-collaboration@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)': + '@tiptap/extension-collaboration@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30))(yjs@13.6.30)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 '@tiptap/y-tiptap': 3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30) yjs: 13.6.30 - '@tiptap/extension-color@3.20.4(@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)))': + '@tiptap/extension-color@3.20.4(@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)))': dependencies: - '@tiptap/extension-text-style': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) + '@tiptap/extension-text-style': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) - '@tiptap/extension-document@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-document@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-dropcursor@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-dropcursor@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-floating-menu@3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-floating-menu@3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: '@floating-ui/dom': 1.7.3 - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 optional: true - '@tiptap/extension-gapcursor@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-gapcursor@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-hard-break@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-hard-break@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-heading@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-heading@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-highlight@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-highlight@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-history@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-history@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-horizontal-rule@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-horizontal-rule@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/extension-image@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-image@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-italic@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-italic@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-link@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-link@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 linkifyjs: 4.3.2 - '@tiptap/extension-list-item@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-list-item@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-list-keymap@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-list-keymap@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/extension-ordered-list@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-ordered-list@3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-paragraph@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-paragraph@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-placeholder@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': + '@tiptap/extension-placeholder@3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-strike@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-strike@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-subscript@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-subscript@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/extension-superscript@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-superscript@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/extension-table@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-table@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/extension-text-align@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-text-align@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-text-style@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-text@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-text@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-typography@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-typography@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-underline@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-underline@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extension-unique-id@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extension-unique-id@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 uuid: 10.0.0 - '@tiptap/extension-youtube@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))': + '@tiptap/extension-youtube@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) - '@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/html@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4)': + '@tiptap/html@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 happy-dom: 20.8.4 @@ -15490,9 +15499,9 @@ snapshots: prosemirror-transform: 1.10.4 prosemirror-view: 1.40.0 - '@tiptap/react@3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tiptap/react@3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -15502,41 +15511,41 @@ snapshots: react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: - '@tiptap/extension-bubble-menu': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-floating-menu': 3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-bubble-menu': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-floating-menu': 3.20.4(@floating-ui/dom@1.7.3)(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) transitivePeerDependencies: - '@floating-ui/dom' '@tiptap/starter-kit@3.20.4': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) - '@tiptap/extension-blockquote': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-bold': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-bullet-list': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) - '@tiptap/extension-code': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-code-block': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-document': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-dropcursor': 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) - '@tiptap/extension-gapcursor': 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) - '@tiptap/extension-hard-break': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-heading': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-horizontal-rule': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-italic': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-link': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) - '@tiptap/extension-list-item': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) - '@tiptap/extension-list-keymap': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) - '@tiptap/extension-ordered-list': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) - '@tiptap/extension-paragraph': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-strike': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-text': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extension-underline': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4)) - '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) + '@tiptap/extension-blockquote': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-bold': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-bullet-list': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-code': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-code-block': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-document': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-dropcursor': 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-gapcursor': 3.20.4(@tiptap/extensions@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-hard-break': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-heading': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-horizontal-rule': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-italic': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-link': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) + '@tiptap/extension-list-item': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-list-keymap': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-ordered-list': 3.20.4(@tiptap/extension-list@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)) + '@tiptap/extension-paragraph': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-strike': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-text': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extension-underline': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) + '@tiptap/extensions': 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/suggestion@3.20.4(@tiptap/core@3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': + '@tiptap/suggestion@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)': dependencies: - '@tiptap/core': 3.20.4(patch_hash=efe36d923d71b90e115d2d468ea1ddaf04a78c2e43c811a1a4b667989c39ea00)(@tiptap/pm@3.20.4) + '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 '@tiptap/y-tiptap@3.0.2(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.30))(yjs@13.6.30)': From 859f16740bd76ae72f9c90b8a913cb0a11053a78 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:19:00 +0000 Subject: [PATCH 17/44] tooltip portal --- .../editor/components/table/table-menu.tsx | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/apps/client/src/features/editor/components/table/table-menu.tsx b/apps/client/src/features/editor/components/table/table-menu.tsx index 66fe4d7b..4adafb20 100644 --- a/apps/client/src/features/editor/components/table/table-menu.tsx +++ b/apps/client/src/features/editor/components/table/table-menu.tsx @@ -34,7 +34,7 @@ export const TableMenu = React.memo( if (isTextSelected(editor)) return false; return editor.isActive("table") && !isCellSelection(state.selection); }, - [editor] + [editor], ); const getReferencedVirtualElement = useCallback(() => { @@ -121,7 +121,11 @@ export const TableMenu = React.memo( shouldShow={shouldShow} > <div className={classes.toolbar}> - <Tooltip position="top" label={t("Add left column")}> + <Tooltip + position="top" + label={t("Add left column")} + withinPortal={false} + > <ActionIcon onClick={addColumnLeft} variant="subtle" @@ -132,7 +136,11 @@ export const TableMenu = React.memo( </ActionIcon> </Tooltip> - <Tooltip position="top" label={t("Add right column")}> + <Tooltip + position="top" + label={t("Add right column")} + withinPortal={false} + > <ActionIcon onClick={addColumnRight} variant="subtle" @@ -143,7 +151,11 @@ export const TableMenu = React.memo( </ActionIcon> </Tooltip> - <Tooltip position="top" label={t("Delete column")}> + <Tooltip + position="top" + label={t("Delete column")} + withinPortal={false} + > <ActionIcon onClick={deleteColumn} variant="subtle" @@ -156,7 +168,11 @@ export const TableMenu = React.memo( <div className={classes.divider} /> - <Tooltip position="top" label={t("Add row above")}> + <Tooltip + position="top" + label={t("Add row above")} + withinPortal={false} + > <ActionIcon onClick={addRowAbove} variant="subtle" @@ -167,7 +183,11 @@ export const TableMenu = React.memo( </ActionIcon> </Tooltip> - <Tooltip position="top" label={t("Add row below")}> + <Tooltip + position="top" + label={t("Add row below")} + withinPortal={false} + > <ActionIcon onClick={addRowBelow} variant="subtle" @@ -178,7 +198,7 @@ export const TableMenu = React.memo( </ActionIcon> </Tooltip> - <Tooltip position="top" label={t("Delete row")}> + <Tooltip position="top" label={t("Delete row")} withinPortal={false}> <ActionIcon onClick={deleteRow} variant="subtle" @@ -191,7 +211,11 @@ export const TableMenu = React.memo( <div className={classes.divider} /> - <Tooltip position="top" label={t("Toggle header row")}> + <Tooltip + position="top" + label={t("Toggle header row")} + withinPortal={false} + > <ActionIcon onClick={toggleHeaderRow} variant="subtle" @@ -202,7 +226,11 @@ export const TableMenu = React.memo( </ActionIcon> </Tooltip> - <Tooltip position="top" label={t("Toggle header column")}> + <Tooltip + position="top" + label={t("Toggle header column")} + withinPortal={false} + > <ActionIcon onClick={toggleHeaderColumn} variant="subtle" @@ -215,7 +243,11 @@ export const TableMenu = React.memo( <div className={classes.divider} /> - <Tooltip position="top" label={t("Delete table")}> + <Tooltip + position="top" + label={t("Delete table")} + withinPortal={false} + > <ActionIcon onClick={deleteTable} variant="subtle" @@ -228,7 +260,7 @@ export const TableMenu = React.memo( </div> </BubbleMenu> ); - } + }, ); export default TableMenu; From 17da762984918a11fb46c1b04ea73f5e8601dee8 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:28:22 +0000 Subject: [PATCH 18/44] overrides --- apps/server/package.json | 4 ++-- package.json | 4 +++- pnpm-lock.yaml | 48 +++++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index 94a9c935..d8bab08c 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -74,7 +74,7 @@ "class-validator": "^0.15.1", "cookie": "^1.1.1", "fs-extra": "^11.3.4", - "happy-dom": "20.8.4", + "happy-dom": "20.8.9", "ioredis": "^5.10.1", "jsonwebtoken": "^9.0.3", "kysely": "^0.28.14", @@ -89,7 +89,7 @@ "nestjs-cls": "^6.2.0", "nestjs-kysely": "^3.1.2", "nestjs-pino": "^4.6.1", - "nodemailer": "^8.0.3", + "nodemailer": "^8.0.4", "openid-client": "^6.8.2", "otpauth": "^9.5.0", "p-limit": "^7.3.0", diff --git a/package.json b/package.json index 623b0407..346204c8 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,9 @@ "picomatch@>=4.0.0 <4.0.4": "4.0.4", "fastify": "5.8.3", "yaml@>=1.0.0 <1.10.3": "1.10.3", - "yaml@>=2.0.0 <2.8.3": "2.8.3" + "yaml@>=2.0.0 <2.8.3": "2.8.3", + "path-to-regexp@^8": "8.4.0", + "brace-expansion@^5": "5.0.5" }, "neverBuiltDependencies": [] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c6effe4..5b5cf19c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,8 @@ overrides: fastify: 5.8.3 yaml@>=1.0.0 <1.10.3: 1.10.3 yaml@>=2.0.0 <2.8.3: 2.8.3 + path-to-regexp@^8: 8.4.0 + brace-expansion@^5: 5.0.5 patchedDependencies: react-arborist@3.4.0: @@ -143,7 +145,7 @@ importers: version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) '@tiptap/html': specifier: 3.20.4 - version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4) + version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.9) '@tiptap/pm': specifier: 3.20.4 version: 3.20.4 @@ -582,8 +584,8 @@ importers: specifier: ^11.3.4 version: 11.3.4 happy-dom: - specifier: 20.8.4 - version: 20.8.4 + specifier: 20.8.9 + version: 20.8.9 ioredis: specifier: ^5.10.1 version: 5.10.1 @@ -627,8 +629,8 @@ importers: specifier: ^4.6.1 version: 4.6.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) nodemailer: - specifier: ^8.0.3 - version: 8.0.3 + specifier: ^8.0.4 + version: 8.0.4 openid-client: specifier: ^6.8.2 version: 6.8.2 @@ -5866,8 +5868,8 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - brace-expansion@5.0.4: - resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -7268,8 +7270,8 @@ packages: engines: {node: '>=0.4.7'} hasBin: true - happy-dom@20.8.4: - resolution: {integrity: sha512-GKhjq4OQCYB4VLFBzv8mmccUadwlAusOZOI7hC1D9xDIT5HhzkJK17c4el2f6R6C715P9xB4uiMxeKUa2nHMwQ==} + happy-dom@20.8.9: + resolution: {integrity: sha512-Tz23LR9T9jOGVZm2x1EPdXqwA37G/owYMxRwU0E4miurAtFsPMQ1d2Jc2okUaSjZqAFz2oEn3FLXC5a0a+siyA==} engines: {node: '>=20.0.0'} has-bigints@1.0.2: @@ -8639,8 +8641,8 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - nodemailer@8.0.3: - resolution: {integrity: sha512-JQNBqvK+bj3NMhUFR3wmCl3SYcOeMotDiwDBvIoCuQdF0PvlIY0BH+FJ2CG7u4cXKPChplE78oowlH/Otsc4ZQ==} + nodemailer@8.0.4: + resolution: {integrity: sha512-k+jf6N8PfQJ0Fe8ZhJlgqU5qJU44Lpvp2yvidH3vp1lPnVQMgi4yEEMPXg5eJS1gFIJTVq1NHBk7Ia9ARdSBdQ==} engines: {node: '>=6.0.0'} normalize-path@3.0.0: @@ -8914,8 +8916,8 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + path-to-regexp@8.4.0: + resolution: {integrity: sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -13543,7 +13545,7 @@ snapshots: '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 tslib: 2.8.1 @@ -13587,7 +13589,7 @@ snapshots: fastify-plugin: 5.1.0 find-my-way: 9.5.0 light-my-request: 6.6.0 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.0 reusify: 1.1.0 tslib: 2.8.1 optionalDependencies: @@ -15472,11 +15474,11 @@ snapshots: '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - '@tiptap/html@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.4)': + '@tiptap/html@3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4))(@tiptap/pm@3.20.4)(happy-dom@20.8.9)': dependencies: '@tiptap/core': 3.20.4(@tiptap/pm@3.20.4) '@tiptap/pm': 3.20.4 - happy-dom: 20.8.4 + happy-dom: 20.8.9 '@tiptap/pm@3.20.4': dependencies: @@ -16660,7 +16662,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.4: + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 @@ -18351,7 +18353,7 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - happy-dom@20.8.4: + happy-dom@20.8.9: dependencies: '@types/node': 25.5.0 '@types/whatwg-mimetype': 3.0.2 @@ -19737,7 +19739,7 @@ snapshots: minimatch@10.2.4: dependencies: - brace-expansion: 5.0.4 + brace-expansion: 5.0.5 minimatch@3.1.5: dependencies: @@ -19862,7 +19864,7 @@ snapshots: node-releases@2.0.27: {} - nodemailer@8.0.3: {} + nodemailer@8.0.4: {} normalize-path@3.0.0: {} @@ -20203,7 +20205,7 @@ snapshots: lru-cache: 11.2.7 minipass: 7.1.3 - path-to-regexp@8.3.0: {} + path-to-regexp@8.4.0: {} path-type@4.0.0: {} @@ -21088,7 +21090,7 @@ snapshots: depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.0 transitivePeerDependencies: - supports-color From 3829b6cbefe8b7f547c7eb236c92288a67be3059 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:32:52 +0000 Subject: [PATCH 19/44] feat(ee): viewer comments (#2060) --- .../public/locales/en-US/translation.json | 3 + apps/client/src/ee/features.ts | 1 + .../space-viewer-comments-toggle.tsx | 61 +++++++ .../features/comment/atoms/comment-atom.ts | 12 ++ .../comment/components/comment-dialog.tsx | 52 +++++- .../components/comment-list-with-tabs.tsx | 6 +- .../comment/components/comment-menu.tsx | 2 +- .../features/comment/types/comment.types.ts | 4 + .../bubble-menu/readonly-bubble-menu.tsx | 159 ++++++++++++++++++ .../src/features/editor/full-editor.tsx | 3 + .../src/features/editor/page-editor.tsx | 11 ++ .../space/components/settings-modal.tsx | 23 +++ .../space/components/space-details.tsx | 10 +- .../components/space-security-settings.tsx | 34 ++++ .../src/features/space/types/space.types.ts | 6 + apps/client/src/pages/page/page.tsx | 4 + .../collaboration/collaboration.handler.ts | 51 +++++- apps/server/src/collaboration/yjs.util.ts | 2 +- apps/server/src/common/features.ts | 22 +++ .../src/core/comment/comment.controller.ts | 15 +- .../server/src/core/comment/comment.module.ts | 2 + .../src/core/comment/comment.service.ts | 42 ++++- .../core/comment/dto/create-comment.dto.ts | 27 ++- .../page/page-access/page-access.service.ts | 23 +++ .../src/core/space/dto/update-space.dto.ts | 4 + .../src/core/space/services/space.service.ts | 44 ++++- .../workspace/services/workspace.service.ts | 3 +- .../src/database/repos/space/space.repo.ts | 22 +++ apps/server/src/ee | 2 +- 29 files changed, 608 insertions(+), 42 deletions(-) create mode 100644 apps/client/src/ee/security/components/space-viewer-comments-toggle.tsx create mode 100644 apps/client/src/features/editor/components/bubble-menu/readonly-bubble-menu.tsx create mode 100644 apps/client/src/features/space/components/space-security-settings.tsx create mode 100644 apps/server/src/common/features.ts diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 68bef0f2..b0dd7d53 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -449,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Prevent members from sharing pages publicly.", "Toggle public sharing": "Toggle public sharing", "Toggle space public sharing": "Toggle space public sharing", + "Allow viewers to comment": "Allow viewers to comment", + "Allow viewers to add comments on pages in this space.": "Allow viewers to add comments on pages in this space.", + "Toggle viewer comments": "Toggle viewer comments", "Public sharing is disabled at the workspace level": "Public sharing is disabled at the workspace level", "Prevent pages in this space from being shared publicly.": "Prevent pages in this space from being shared publicly.", "Page permissions": "Page permissions", diff --git a/apps/client/src/ee/features.ts b/apps/client/src/ee/features.ts index 70438cba..4cd802fa 100644 --- a/apps/client/src/ee/features.ts +++ b/apps/client/src/ee/features.ts @@ -16,4 +16,5 @@ export const Feature = { AUDIT_LOGS: 'audit:logs', RETENTION: 'retention', SHARING_CONTROLS: 'sharing:controls', + VIEWER_COMMENTS: 'comment:viewer', } as const; diff --git a/apps/client/src/ee/security/components/space-viewer-comments-toggle.tsx b/apps/client/src/ee/security/components/space-viewer-comments-toggle.tsx new file mode 100644 index 00000000..88fac67f --- /dev/null +++ b/apps/client/src/ee/security/components/space-viewer-comments-toggle.tsx @@ -0,0 +1,61 @@ +import { Group, Text, Switch, Tooltip } from "@mantine/core"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ISpace } from "@/features/space/types/space.types.ts"; +import { useUpdateSpaceMutation } from "@/features/space/queries/space-query.ts"; +import { useHasFeature } from "@/ee/hooks/use-feature.ts"; +import { Feature } from "@/ee/features.ts"; +import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label.ts"; + +type SpaceViewerCommentsToggleProps = { + space: ISpace; +}; + +export default function SpaceViewerCommentsToggle({ + space, +}: SpaceViewerCommentsToggleProps) { + const { t } = useTranslation(); + const hasViewerComments = useHasFeature(Feature.VIEWER_COMMENTS); + const upgradeLabel = useUpgradeLabel(); + const isDisabled = !hasViewerComments; + const [checked, setChecked] = useState( + space.settings?.comments?.allowViewerComments === true, + ); + const updateSpaceMutation = useUpdateSpaceMutation(); + + const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => { + const value = event.currentTarget.checked; + try { + await updateSpaceMutation.mutateAsync({ + spaceId: space.id, + allowViewerComments: value, + }); + setChecked(value); + } catch { + // error handled by mutation + } + }; + + return ( + <Group justify="space-between" wrap="nowrap" gap="xl"> + <div> + <Text size="md">{t("Allow viewers to comment")}</Text> + <Text size="sm" c="dimmed"> + {t("Allow viewers to add comments on pages in this space.")} + </Text> + </div> + <Tooltip + label={upgradeLabel} + disabled={!isDisabled} + refProp="rootRef" + > + <Switch + checked={checked} + onChange={handleChange} + disabled={isDisabled} + aria-label={t("Toggle viewer comments")} + /> + </Tooltip> + </Group> + ); +} diff --git a/apps/client/src/features/comment/atoms/comment-atom.ts b/apps/client/src/features/comment/atoms/comment-atom.ts index 384a2f3d..374e7f7f 100644 --- a/apps/client/src/features/comment/atoms/comment-atom.ts +++ b/apps/client/src/features/comment/atoms/comment-atom.ts @@ -3,3 +3,15 @@ import { atom } from 'jotai'; export const showCommentPopupAtom = atom<boolean>(false); export const activeCommentIdAtom = atom<string>(''); export const draftCommentIdAtom = atom<string>(''); + +// Read-only comment state +export const showReadOnlyCommentPopupAtom = atom<boolean>(false); +export type YjsSelection = { + anchor: any; + head: any; +}; +export type ReadOnlyCommentData = { + yjsSelection: YjsSelection; + selectedText: string; +}; +export const readOnlyCommentDataAtom = atom<ReadOnlyCommentData | null>(null); diff --git a/apps/client/src/features/comment/components/comment-dialog.tsx b/apps/client/src/features/comment/components/comment-dialog.tsx index 6248e913..24781a94 100644 --- a/apps/client/src/features/comment/components/comment-dialog.tsx +++ b/apps/client/src/features/comment/components/comment-dialog.tsx @@ -6,6 +6,8 @@ import { activeCommentIdAtom, draftCommentIdAtom, showCommentPopupAtom, + showReadOnlyCommentPopupAtom, + readOnlyCommentDataAtom, } from "@/features/comment/atoms/comment-atom"; import CommentEditor from "@/features/comment/components/comment-editor"; import CommentActions from "@/features/comment/components/comment-actions"; @@ -19,12 +21,15 @@ import { useTranslation } from "react-i18next"; interface CommentDialogProps { editor: ReturnType<typeof useEditor>; pageId: string; + readOnly?: boolean; } -function CommentDialog({ editor, pageId }: CommentDialogProps) { +function CommentDialog({ editor, pageId, readOnly }: CommentDialogProps) { const { t } = useTranslation(); const [comment, setComment] = useState(""); const [, setShowCommentPopup] = useAtom(showCommentPopupAtom); + const [, setShowReadOnlyCommentPopup] = useAtom(showReadOnlyCommentPopupAtom); + const [readOnlyCommentData, setReadOnlyCommentData] = useAtom(readOnlyCommentDataAtom); const [, setActiveCommentId] = useAtom(activeCommentIdAtom); const [draftCommentId, setDraftCommentId] = useAtom(draftCommentIdAtom); const [currentUser] = useAtom(currentUserAtom); @@ -34,11 +39,17 @@ function CommentDialog({ editor, pageId }: CommentDialogProps) { handleDialogClose(); }); const createCommentMutation = useCreateCommentMutation(); - const { isPending } = createCommentMutation; + const isPending = createCommentMutation.isPending; const handleDialogClose = () => { - setShowCommentPopup(false); - editor.chain().focus().unsetCommentDecoration().run(); + if (readOnly) { + setShowReadOnlyCommentPopup(false); + // @ts-ignore + setReadOnlyCommentData(null); + } else { + setShowCommentPopup(false); + editor.chain().focus().unsetCommentDecoration().run(); + } }; const getSelectedText = () => { @@ -47,6 +58,11 @@ function CommentDialog({ editor, pageId }: CommentDialogProps) { }; const handleAddComment = async () => { + if (readOnly) { + await handleAddReadOnlyComment(); + return; + } + try { const selectedText = getSelectedText(); const commentData = { @@ -65,7 +81,6 @@ function CommentDialog({ editor, pageId }: CommentDialogProps) { .run(); setActiveCommentId(createdComment.id); - //unselect text to close bubble menu editor.commands.setTextSelection({ from: editor.view.state.selection.from, to: editor.view.state.selection.from }); setAsideState({ tab: "comments", isAsideOpen: true }); @@ -85,6 +100,33 @@ function CommentDialog({ editor, pageId }: CommentDialogProps) { } }; + const handleAddReadOnlyComment = async () => { + if (!readOnlyCommentData) return; + + try { + const createdComment = await createCommentMutation.mutateAsync({ + pageId, + content: JSON.stringify(comment), + selection: readOnlyCommentData.selectedText, + type: "inline", + yjsSelection: readOnlyCommentData.yjsSelection, + }); + + setActiveCommentId(createdComment.id); + setAsideState({ tab: "comments", isAsideOpen: true }); + + setTimeout(() => { + const selector = `div[data-comment-id="${createdComment.id}"]`; + const commentElement = document.querySelector(selector); + commentElement?.scrollIntoView({ behavior: "smooth", block: "center" }); + }, 400); + } finally { + setShowReadOnlyCommentPopup(false); + // @ts-ignore + setReadOnlyCommentData(null); + } + }; + const handleCommentEditorChange = (newContent: any) => { setComment(newContent); }; diff --git a/apps/client/src/features/comment/components/comment-list-with-tabs.tsx b/apps/client/src/features/comment/components/comment-list-with-tabs.tsx index 022ea2fa..94d9e020 100644 --- a/apps/client/src/features/comment/components/comment-list-with-tabs.tsx +++ b/apps/client/src/features/comment/components/comment-list-with-tabs.tsx @@ -44,7 +44,9 @@ function CommentListWithTabs() { const [isLoading, setIsLoading] = useState(false); const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug); - const canComment = page?.permissions?.canEdit ?? false; + const canComment = + (page?.permissions?.canEdit ?? false) || + (space?.settings?.comments?.allowViewerComments === true); // Separate active and resolved comments const { activeComments, resolvedComments } = useMemo(() => { @@ -153,7 +155,7 @@ function CommentListWithTabs() { )} </Paper> ), - [comments, handleAddReply, isLoading, space?.membership?.role], + [comments, handleAddReply, isLoading, space?.membership?.role, canComment], ); if (isCommentsLoading) { diff --git a/apps/client/src/features/comment/components/comment-menu.tsx b/apps/client/src/features/comment/components/comment-menu.tsx index 201a0702..b9cd1e0e 100644 --- a/apps/client/src/features/comment/components/comment-menu.tsx +++ b/apps/client/src/features/comment/components/comment-menu.tsx @@ -75,7 +75,7 @@ function CommentMenu({ {isResolved ? t("Re-open comment") : t("Resolve comment")} </Menu.Item> ) : ( - <Tooltip label={upgradeLabel} position="left"> + <Tooltip label={upgradeLabel} position="left" withPortal={false}> <Menu.Item disabled leftSection={<IconCircleCheck size={14} />}> {t("Resolve comment")} </Menu.Item> diff --git a/apps/client/src/features/comment/types/comment.types.ts b/apps/client/src/features/comment/types/comment.types.ts index 6c8cc909..164e63dc 100644 --- a/apps/client/src/features/comment/types/comment.types.ts +++ b/apps/client/src/features/comment/types/comment.types.ts @@ -17,6 +17,10 @@ export interface IComment { deletedAt?: Date; creator: IUser; resolvedBy?: IUser; + yjsSelection?: { + anchor: any; + head: any; + }; } export interface ICommentData { diff --git a/apps/client/src/features/editor/components/bubble-menu/readonly-bubble-menu.tsx b/apps/client/src/features/editor/components/bubble-menu/readonly-bubble-menu.tsx new file mode 100644 index 00000000..7998becf --- /dev/null +++ b/apps/client/src/features/editor/components/bubble-menu/readonly-bubble-menu.tsx @@ -0,0 +1,159 @@ +import type { Editor } from "@tiptap/react"; +import { TextSelection } from "@tiptap/pm/state"; +import { FC, useCallback, useEffect, useRef, useState } from "react"; +import { IconMessage } from "@tabler/icons-react"; +import classes from "./bubble-menu.module.css"; +import { ActionIcon, Tooltip } from "@mantine/core"; +import { useAtom } from "jotai"; +import { + showReadOnlyCommentPopupAtom, + readOnlyCommentDataAtom, +} from "@/features/comment/atoms/comment-atom"; +import { useTranslation } from "react-i18next"; +import { getRelativeSelection, ySyncPluginKey } from "@tiptap/y-tiptap"; + +type ReadonlyBubbleMenuProps = { + editor: Editor; +}; + +export const ReadonlyBubbleMenu: FC<ReadonlyBubbleMenuProps> = ({ editor }) => { + const { t } = useTranslation(); + const [showReadOnlyCommentPopup, setShowReadOnlyCommentPopup] = useAtom( + showReadOnlyCommentPopupAtom, + ); + const [, setReadOnlyCommentData] = useAtom(readOnlyCommentDataAtom); + const menuRef = useRef<HTMLDivElement>(null); + const [visible, setVisible] = useState(false); + const [position, setPosition] = useState({ top: 0, left: 0 }); + const isInteractingRef = useRef(false); + + const updateMenuPosition = useCallback(() => { + if (isInteractingRef.current) return; + + const pmSelection = editor.state.selection; + if (!(pmSelection instanceof TextSelection) || pmSelection.empty) { + setVisible(false); + return; + } + + const selection = window.getSelection(); + if ( + !selection || + selection.isCollapsed || + selection.rangeCount === 0 || + showReadOnlyCommentPopup + ) { + setVisible(false); + return; + } + + const editorDom = editor.view.dom; + if ( + !editorDom.contains(selection.anchorNode) || + !editorDom.contains(selection.focusNode) + ) { + setVisible(false); + return; + } + + const range = selection.getRangeAt(0); + const rect = range.getBoundingClientRect(); + + if (rect.width === 0) { + setVisible(false); + return; + } + + const editorRect = editorDom + .closest(".editor-container") + ?.getBoundingClientRect(); + if (!editorRect) { + setVisible(false); + return; + } + + setPosition({ + top: rect.top - editorRect.top - 44, + left: rect.left - editorRect.left + rect.width / 2, + }); + setVisible(true); + }, [editor, showReadOnlyCommentPopup]); + + useEffect(() => { + const handleSelectionChange = () => { + updateMenuPosition(); + }; + + document.addEventListener("selectionchange", handleSelectionChange); + return () => { + document.removeEventListener("selectionchange", handleSelectionChange); + }; + }, [updateMenuPosition]); + + useEffect(() => { + if (showReadOnlyCommentPopup) { + setVisible(false); + } + }, [showReadOnlyCommentPopup]); + + const handleCommentClick = () => { + if (!editor) return; + + const view = editor.view; + const ystate = ySyncPluginKey.getState(view.state); + + if (ystate?.binding) { + const selection = getRelativeSelection(ystate.binding, view.state); + const { from, to } = editor.state.selection; + const selectedText = editor.state.doc.textBetween(from, to); + + // @ts-ignore + setReadOnlyCommentData({ + yjsSelection: { + anchor: selection.anchor, + head: selection.head, + }, + selectedText, + }); + + setShowReadOnlyCommentPopup(true); + setVisible(false); + } + }; + + if (!visible) return null; + + return ( + <div + ref={menuRef} + style={{ + position: "absolute", + top: position.top, + left: position.left, + transform: "translateX(-50%)", + zIndex: 199, + }} + > + <div className={classes.bubbleMenu}> + <Tooltip label={t("Comment")} withArrow withinPortal={false}> + <ActionIcon + variant="default" + size="lg" + radius="6px" + aria-label={t("Comment")} + style={{ border: "none" }} + onMouseDown={(e) => { + e.preventDefault(); + e.stopPropagation(); + isInteractingRef.current = true; + handleCommentClick(); + isInteractingRef.current = false; + }} + > + <IconMessage size={16} stroke={2} /> + </ActionIcon> + </Tooltip> + </div> + </div> + ); +}; diff --git a/apps/client/src/features/editor/full-editor.tsx b/apps/client/src/features/editor/full-editor.tsx index cbe16adb..89eb7ca8 100644 --- a/apps/client/src/features/editor/full-editor.tsx +++ b/apps/client/src/features/editor/full-editor.tsx @@ -16,6 +16,7 @@ export interface FullEditorProps { content: string; spaceSlug: string; editable: boolean; + canComment?: boolean; } export function FullEditor({ @@ -25,6 +26,7 @@ export function FullEditor({ content, spaceSlug, editable, + canComment, }: FullEditorProps) { const [user] = useAtom(userAtom); const fullPageWidth = user.settings?.preferences?.fullPageWidth; @@ -46,6 +48,7 @@ export function FullEditor({ pageId={pageId} editable={editable} content={content} + canComment={canComment} /> </Container> ); diff --git a/apps/client/src/features/editor/page-editor.tsx b/apps/client/src/features/editor/page-editor.tsx index d51a5a4c..7cc4723a 100644 --- a/apps/client/src/features/editor/page-editor.tsx +++ b/apps/client/src/features/editor/page-editor.tsx @@ -37,9 +37,11 @@ import { asideStateAtom } from "@/components/layouts/global/hooks/atoms/sidebar- import { activeCommentIdAtom, showCommentPopupAtom, + showReadOnlyCommentPopupAtom, } from "@/features/comment/atoms/comment-atom"; import CommentDialog from "@/features/comment/components/comment-dialog"; import { EditorBubbleMenu } from "@/features/editor/components/bubble-menu/bubble-menu"; +import { ReadonlyBubbleMenu } from "@/features/editor/components/bubble-menu/readonly-bubble-menu"; import TableCellMenu from "@/features/editor/components/table/table-cell-menu.tsx"; import TableMenu from "@/features/editor/components/table/table-menu.tsx"; import ImageMenu from "@/features/editor/components/image/image-menu.tsx"; @@ -74,12 +76,14 @@ interface PageEditorProps { pageId: string; editable: boolean; content: any; + canComment?: boolean; } export default function PageEditor({ pageId, editable, content, + canComment, }: PageEditorProps) { const collaborationURL = useCollaborationUrl(); const isComponentMounted = useRef(false); @@ -94,6 +98,7 @@ export default function PageEditor({ const [, setAsideState] = useAtom(asideStateAtom); const [, setActiveCommentId] = useAtom(activeCommentIdAtom); const [showCommentPopup, setShowCommentPopup] = useAtom(showCommentPopupAtom); + const [showReadOnlyCommentPopup] = useAtom(showReadOnlyCommentPopupAtom); const [isLocalSynced, setIsLocalSynced] = useState(false); const [isRemoteSynced, setIsRemoteSynced] = useState(false); const [yjsConnectionStatus, setYjsConnectionStatus] = useAtom( @@ -423,7 +428,13 @@ export default function PageEditor({ <ColumnsMenu editor={editor} /> </div> )} + {editor && !editorIsEditable && (editable || canComment) && providersRef.current && ( + <ReadonlyBubbleMenu editor={editor} /> + )} {showCommentPopup && <CommentDialog editor={editor} pageId={pageId} />} + {showReadOnlyCommentPopup && ( + <CommentDialog editor={editor} pageId={pageId} readOnly /> + )} </div> <div onClick={() => editor.commands.focus("end")} diff --git a/apps/client/src/features/space/components/settings-modal.tsx b/apps/client/src/features/space/components/settings-modal.tsx index 7fb07b39..fb8d24f6 100644 --- a/apps/client/src/features/space/components/settings-modal.tsx +++ b/apps/client/src/features/space/components/settings-modal.tsx @@ -3,6 +3,7 @@ import SpaceMembersList from "@/features/space/components/space-members.tsx"; import AddSpaceMembersModal from "@/features/space/components/add-space-members-modal.tsx"; import React from "react"; import SpaceDetails from "@/features/space/components/space-details.tsx"; +import SpaceSecuritySettings from "@/features/space/components/space-security-settings.tsx"; import { useSpaceQuery } from "@/features/space/queries/space-query.ts"; import { useSpaceAbility } from "@/features/space/permissions/use-space-ability.ts"; import { @@ -59,6 +60,14 @@ export default function SpaceSettingsModal({ <Tabs.Tab fw={500} value="members"> {t("Members")} </Tabs.Tab> + {spaceAbility.can( + SpaceCaslAction.Manage, + SpaceCaslSubject.Settings, + ) && ( + <Tabs.Tab fw={500} value="security"> + {t("Security")} + </Tabs.Tab> + )} </Tabs.List> <Tabs.Panel value="general"> @@ -91,6 +100,20 @@ export default function SpaceSettingsModal({ )} /> </Tabs.Panel> + + <Tabs.Panel value="security"> + <ScrollArea h={580} scrollbarSize={5} pr={8}> + <div style={{ paddingBottom: "100px" }}> + <SpaceSecuritySettings + space={space} + readOnly={spaceAbility.cannot( + SpaceCaslAction.Manage, + SpaceCaslSubject.Settings, + )} + /> + </div> + </ScrollArea> + </Tabs.Panel> </Tabs> </div> </Modal.Body> diff --git a/apps/client/src/features/space/components/space-details.tsx b/apps/client/src/features/space/components/space-details.tsx index 5aea40aa..746d1bbf 100644 --- a/apps/client/src/features/space/components/space-details.tsx +++ b/apps/client/src/features/space/components/space-details.tsx @@ -18,7 +18,7 @@ import { ResponsiveSettingsControl, ResponsiveSettingsRow, } from "@/components/ui/responsive-settings-row.tsx"; -import SpacePublicSharingToggle from "@/ee/security/components/space-public-sharing-toggle.tsx"; + interface SpaceDetailsProps { spaceId: string; @@ -27,7 +27,6 @@ interface SpaceDetailsProps { export default function SpaceDetails({ spaceId, readOnly }: SpaceDetailsProps) { const { t } = useTranslation(); const { data: space, isLoading, refetch } = useSpaceQuery(spaceId); - const showSharingToggle = !readOnly; const [exportOpened, { open: openExportModal, close: closeExportModal }] = useDisclosure(false); const [isIconUploading, setIsIconUploading] = useState(false); @@ -89,13 +88,6 @@ export default function SpaceDetails({ spaceId, readOnly }: SpaceDetailsProps) { <EditSpaceForm space={space} readOnly={readOnly} /> - {showSharingToggle && ( - <> - <Divider my="lg" /> - <SpacePublicSharingToggle space={space} /> - </> - )} - {!readOnly && ( <> <Divider my="lg" /> diff --git a/apps/client/src/features/space/components/space-security-settings.tsx b/apps/client/src/features/space/components/space-security-settings.tsx new file mode 100644 index 00000000..c606cf68 --- /dev/null +++ b/apps/client/src/features/space/components/space-security-settings.tsx @@ -0,0 +1,34 @@ +import { Text, Divider } from "@mantine/core"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { ISpace } from "@/features/space/types/space.types.ts"; +import SpacePublicSharingToggle from "@/ee/security/components/space-public-sharing-toggle.tsx"; +import SpaceViewerCommentsToggle from "@/ee/security/components/space-viewer-comments-toggle.tsx"; + +type SpaceSecuritySettingsProps = { + space: ISpace; + readOnly?: boolean; +}; + +export default function SpaceSecuritySettings({ + space, + readOnly, +}: SpaceSecuritySettingsProps) { + const { t } = useTranslation(); + + if (readOnly) return null; + + return ( + <div> + <Text my="md" fw={600}> + {t("Security")} + </Text> + + <SpacePublicSharingToggle space={space} /> + + <Divider my="lg" /> + + <SpaceViewerCommentsToggle space={space} /> + </div> + ); +} diff --git a/apps/client/src/features/space/types/space.types.ts b/apps/client/src/features/space/types/space.types.ts index f7dcc11a..c856d88a 100644 --- a/apps/client/src/features/space/types/space.types.ts +++ b/apps/client/src/features/space/types/space.types.ts @@ -9,8 +9,13 @@ export interface ISpaceSharingSettings { disabled?: boolean; } +export interface ISpaceCommentsSettings { + allowViewerComments?: boolean; +} + export interface ISpaceSettings { sharing?: ISpaceSharingSettings; + comments?: ISpaceCommentsSettings; } export interface ISpace { @@ -29,6 +34,7 @@ export interface ISpace { settings?: ISpaceSettings; // for updates disablePublicSharing?: boolean; + allowViewerComments?: boolean; } interface IMembership { diff --git a/apps/client/src/pages/page/page.tsx b/apps/client/src/pages/page/page.tsx index fc564b4b..f0fc93fa 100644 --- a/apps/client/src/pages/page/page.tsx +++ b/apps/client/src/pages/page/page.tsx @@ -53,6 +53,9 @@ function PageContent({ pageSlug }: { pageSlug: string | undefined }) { const { data: space } = useGetSpaceBySlugQuery(page?.space?.slug); const canEdit = page?.permissions?.canEdit ?? false; + const canComment = + canEdit || + (space?.settings?.comments?.allowViewerComments === true); if (isLoading) { return <></>; @@ -104,6 +107,7 @@ function PageContent({ pageSlug }: { pageSlug: string | undefined }) { slugId={page.slugId} spaceSlug={page?.space?.slug} editable={canEdit} + canComment={canComment} /> <MemoizedHistoryModal pageId={page.id} /> </div> diff --git a/apps/server/src/collaboration/collaboration.handler.ts b/apps/server/src/collaboration/collaboration.handler.ts index 87dc5010..992f9b74 100644 --- a/apps/server/src/collaboration/collaboration.handler.ts +++ b/apps/server/src/collaboration/collaboration.handler.ts @@ -5,6 +5,7 @@ import { prosemirrorNodeToYElement, tiptapExtensions, } from './collaboration.util'; +import { setYjsMark, updateYjsMarkAttribute, YjsSelection } from './yjs.util'; import * as Y from 'yjs'; import { User } from '@docmost/db/types/entity.types'; @@ -27,6 +28,53 @@ export class CollaborationHandler { // const fragment = doc.getXmlFragment('default'); //}); }, + setCommentMark: async ( + documentName: string, + payload: { + yjsSelection: YjsSelection; + commentId: string; + resolved: boolean; + user: User; + }, + ) => { + const { yjsSelection, commentId, resolved, user } = payload; + await this.withYdocConnection( + hocuspocus, + documentName, + { user }, + (doc) => { + const fragment = doc.getXmlFragment('default'); + setYjsMark(doc, fragment, yjsSelection, 'comment', { + commentId, + resolved, + }); + }, + ); + }, + resolveCommentMark: async ( + documentName: string, + payload: { + commentId: string; + resolved: boolean; + user: User; + }, + ) => { + const { commentId, resolved, user } = payload; + await this.withYdocConnection( + hocuspocus, + documentName, + { user }, + (doc) => { + const fragment = doc.getXmlFragment('default'); + updateYjsMarkAttribute( + fragment, + 'comment', + { name: 'commentId', value: commentId }, + { resolved }, + ); + }, + ); + }, updatePageContent: async ( documentName: string, payload: { @@ -58,8 +106,7 @@ export class CollaborationHandler { } else { const newContent = prosemirrorJson.content || []; const yElements = newContent.map(prosemirrorNodeToYElement); - const position = - operation === 'prepend' ? 0 : fragment.length; + const position = operation === 'prepend' ? 0 : fragment.length; fragment.insert(position, yElements); } }, diff --git a/apps/server/src/collaboration/yjs.util.ts b/apps/server/src/collaboration/yjs.util.ts index 3e494bbc..863b149a 100644 --- a/apps/server/src/collaboration/yjs.util.ts +++ b/apps/server/src/collaboration/yjs.util.ts @@ -1,7 +1,7 @@ import { initProseMirrorDoc, relativePositionToAbsolutePosition, -} from 'y-prosemirror'; +} from '@tiptap/y-tiptap'; import * as Y from 'yjs'; import { Document } from '@hocuspocus/server'; import { getSchema } from '@tiptap/core'; diff --git a/apps/server/src/common/features.ts b/apps/server/src/common/features.ts new file mode 100644 index 00000000..b893e0b9 --- /dev/null +++ b/apps/server/src/common/features.ts @@ -0,0 +1,22 @@ +export const Feature = { + SSO_CUSTOM: 'sso:custom', + SSO_GOOGLE: 'sso:google', + MFA: 'mfa', + API_KEYS: 'api:keys', + COMMENT_RESOLUTION: 'comment:resolution', + PAGE_PERMISSIONS: 'page:permissions', + AI: 'ai', + CONFLUENCE_IMPORT: 'import:confluence', + DOCX_IMPORT: 'import:docx', + ATTACHMENT_INDEXING: 'attachment:indexing', + SECURITY_SETTINGS: 'security:settings', + MCP: 'mcp', + SCIM: 'scim', + PAGE_VERIFICATION: 'page:verification', + AUDIT_LOGS: 'audit:logs', + RETENTION: 'retention', + SHARING_CONTROLS: 'sharing:controls', + VIEWER_COMMENTS: 'comment:viewer', +} as const; + +export type FeatureKey = (typeof Feature)[keyof typeof Feature]; diff --git a/apps/server/src/core/comment/comment.controller.ts b/apps/server/src/core/comment/comment.controller.ts index 99e51384..22458848 100644 --- a/apps/server/src/core/comment/comment.controller.ts +++ b/apps/server/src/core/comment/comment.controller.ts @@ -58,13 +58,13 @@ export class CommentController { throw new NotFoundException('Page not found'); } - await this.pageAccessService.validateCanEdit(page, user); + await this.pageAccessService.validateCanComment(page, user, workspace.id); const comment = await this.commentService.create( { - userId: user.id, page, workspaceId: workspace.id, + user, }, createCommentDto, ); @@ -120,7 +120,7 @@ export class CommentController { @HttpCode(HttpStatus.OK) @Post('update') - async update(@Body() dto: UpdateCommentDto, @AuthUser() user: User) { + async update(@Body() dto: UpdateCommentDto, @AuthUser() user: User, @AuthWorkspace() workspace: Workspace) { const comment = await this.commentRepo.findById(dto.commentId, { includeCreator: true, includeResolvedBy: true, @@ -134,14 +134,14 @@ export class CommentController { throw new NotFoundException('Page not found'); } - await this.pageAccessService.validateCanEdit(page, user); + await this.pageAccessService.validateCanComment(page, user, workspace.id); return this.commentService.update(comment, dto, user); } @HttpCode(HttpStatus.OK) @Post('delete') - async delete(@Body() input: CommentIdDto, @AuthUser() user: User) { + async delete(@Body() input: CommentIdDto, @AuthUser() user: User, @AuthWorkspace() workspace: Workspace) { const comment = await this.commentRepo.findById(input.commentId); if (!comment) { throw new NotFoundException('Comment not found'); @@ -152,8 +152,7 @@ export class CommentController { throw new NotFoundException('Page not found'); } - // Check page-level edit permission first - await this.pageAccessService.validateCanEdit(page, user); + await this.pageAccessService.validateCanComment(page, user, workspace.id); // Check if user is the comment owner const isOwner = comment.creatorId === user.id; @@ -169,7 +168,7 @@ export class CommentController { // Space admin can delete any comment if (ability.cannot(SpaceCaslAction.Manage, SpaceCaslSubject.Settings)) { throw new ForbiddenException( - 'You can only delete your own comments or must be a space admin', + 'You can only delete your own comments', ); } await this.commentRepo.deleteComment(comment.id); diff --git a/apps/server/src/core/comment/comment.module.ts b/apps/server/src/core/comment/comment.module.ts index 02cb6d81..e08f3610 100644 --- a/apps/server/src/core/comment/comment.module.ts +++ b/apps/server/src/core/comment/comment.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { CommentService } from './comment.service'; import { CommentController } from './comment.controller'; +import { CollaborationModule } from '../../collaboration/collaboration.module'; @Module({ + imports: [CollaborationModule], controllers: [CommentController], providers: [CommentService], exports: [CommentService], diff --git a/apps/server/src/core/comment/comment.service.ts b/apps/server/src/core/comment/comment.service.ts index 9fa5e24c..e888ef50 100644 --- a/apps/server/src/core/comment/comment.service.ts +++ b/apps/server/src/core/comment/comment.service.ts @@ -7,7 +7,8 @@ import { } from '@nestjs/common'; import { InjectQueue } from '@nestjs/bullmq'; import { Queue } from 'bullmq'; -import { CreateCommentDto } from './dto/create-comment.dto'; +import { CreateCommentDto, yjsSelectionSchema } from './dto/create-comment.dto'; +import { CollaborationGateway } from '../../collaboration/collaboration.gateway'; import { UpdateCommentDto } from './dto/update-comment.dto'; import { CommentRepo } from '@docmost/db/repos/comment/comment.repo'; import { Comment, Page, User } from '@docmost/db/types/entity.types'; @@ -27,6 +28,7 @@ export class CommentService { private commentRepo: CommentRepo, private pageRepo: PageRepo, private wsService: WsService, + private collaborationGateway: CollaborationGateway, @InjectQueue(QueueName.GENERAL_QUEUE) private generalQueue: Queue, @InjectQueue(QueueName.NOTIFICATION_QUEUE) @@ -45,10 +47,10 @@ export class CommentService { } async create( - opts: { userId: string; page: Page; workspaceId: string }, + opts: { page: Page; workspaceId: string; user: User }, createCommentDto: CreateCommentDto, ) { - const { userId, page, workspaceId } = opts; + const { page, workspaceId, user } = opts; const commentContent = JSON.parse(createCommentDto.content); if (createCommentDto.parentCommentId) { @@ -71,11 +73,39 @@ export class CommentService { selection: createCommentDto?.selection?.substring(0, 250) ?? null, type: createCommentDto.type ?? 'page', parentCommentId: createCommentDto?.parentCommentId, - creatorId: userId, + creatorId: user.id, workspaceId: workspaceId, spaceId: page.spaceId, }); + if (createCommentDto.yjsSelection) { + const parsed = yjsSelectionSchema.safeParse(createCommentDto.yjsSelection); + if (!parsed.success) { + this.logger.warn( + `Invalid yjsSelection for comment ${inserted.id}: ${parsed.error.message}`, + ); + } else { + const documentName = `page.${page.id}`; + try { + await this.collaborationGateway.handleYjsEvent( + 'setCommentMark', + documentName, + { + yjsSelection: parsed.data, + commentId: inserted.id, + resolved: false, + user, + }, + ); + } catch (error) { + this.logger.warn( + `Failed to apply comment mark for comment ${inserted.id}, comment saved without inline highlight`, + error, + ); + } + } + } + const comment = await this.commentRepo.findById(inserted.id, { includeCreator: true, includeResolvedBy: true, @@ -83,7 +113,7 @@ export class CommentService { this.generalQueue .add(QueueJob.ADD_PAGE_WATCHERS, { - userIds: [userId], + userIds: [user.id], pageId: page.id, spaceId: page.spaceId, workspaceId, @@ -101,7 +131,7 @@ export class CommentService { page.id, page.spaceId, workspaceId, - userId, + user.id, !isReply, createCommentDto.parentCommentId, ); diff --git a/apps/server/src/core/comment/dto/create-comment.dto.ts b/apps/server/src/core/comment/dto/create-comment.dto.ts index ca21f47b..c82ae187 100644 --- a/apps/server/src/core/comment/dto/create-comment.dto.ts +++ b/apps/server/src/core/comment/dto/create-comment.dto.ts @@ -1,4 +1,22 @@ -import { IsIn, IsJSON, IsOptional, IsString, IsUUID } from 'class-validator'; +import { IsIn, IsJSON, IsObject, IsOptional, IsString, IsUUID } from 'class-validator'; +import { z } from 'zod'; + +const yjsIdSchema = z.object({ + client: z.number().int().nonnegative(), + clock: z.number().int().nonnegative(), +}); + +const yjsRelativePositionSchema = z.object({ + type: yjsIdSchema, + tname: z.string().nullable(), + item: yjsIdSchema.nullable(), + assoc: z.number().int(), +}); + +export const yjsSelectionSchema = z.object({ + anchor: yjsRelativePositionSchema, + head: yjsRelativePositionSchema, +}); export class CreateCommentDto { @IsString() @@ -18,4 +36,11 @@ export class CreateCommentDto { @IsOptional() @IsUUID() parentCommentId: string; + + @IsOptional() + @IsObject() + yjsSelection?: { + anchor: any; + head: any; + }; } diff --git a/apps/server/src/core/page/page-access/page-access.service.ts b/apps/server/src/core/page/page-access/page-access.service.ts index 07395ed4..6d6db03f 100644 --- a/apps/server/src/core/page/page-access/page-access.service.ts +++ b/apps/server/src/core/page/page-access/page-access.service.ts @@ -6,12 +6,14 @@ import { SpaceCaslAction, SpaceCaslSubject, } from '../../casl/interfaces/space-ability.type'; +import { SpaceRepo } from '@docmost/db/repos/space/space.repo'; @Injectable() export class PageAccessService { constructor( private readonly pagePermissionRepo: PagePermissionRepo, private readonly spaceAbility: SpaceAbilityFactory, + private readonly spaceRepo: SpaceRepo, ) {} /** @@ -99,4 +101,25 @@ export class PageAccessService { return { hasRestriction: hasAnyRestriction }; } + + async validateCanComment( + page: Page, + user: User, + workspaceId: string, + ): Promise<void> { + try { + await this.validateCanEdit(page, user); + return; + } catch { + // User cannot edit — check if reader commenting is enabled + } + + await this.validateCanView(page, user); + + const space = await this.spaceRepo.findById(page.spaceId, workspaceId); + const settings = space?.settings as Record<string, any> | null; + if (!settings?.comments?.allowViewerComments) { + throw new ForbiddenException(); + } + } } diff --git a/apps/server/src/core/space/dto/update-space.dto.ts b/apps/server/src/core/space/dto/update-space.dto.ts index 47f1529b..8b40e894 100644 --- a/apps/server/src/core/space/dto/update-space.dto.ts +++ b/apps/server/src/core/space/dto/update-space.dto.ts @@ -11,4 +11,8 @@ export class UpdateSpaceDto extends PartialType(CreateSpaceDto) { @IsOptional() @IsBoolean() disablePublicSharing: boolean; + + @IsOptional() + @IsBoolean() + allowViewerComments: boolean; } diff --git a/apps/server/src/core/space/services/space.service.ts b/apps/server/src/core/space/services/space.service.ts index e512e644..2675a9e6 100644 --- a/apps/server/src/core/space/services/space.service.ts +++ b/apps/server/src/core/space/services/space.service.ts @@ -13,6 +13,7 @@ import { Space, User } from '@docmost/db/types/entity.types'; import { UpdateSpaceDto } from '../dto/update-space.dto'; import { executeTx } from '@docmost/db/utils'; import { InjectKysely } from 'nestjs-kysely'; +import { Feature } from '../../../common/features'; import { SpaceMemberService } from './space-member.service'; import { SpaceRole } from '../../../common/helpers/types/permission'; import { QueueJob, QueueName } from 'src/integrations/queue/constants'; @@ -133,17 +134,34 @@ export class SpaceService { } } - if (typeof updateSpaceDto.disablePublicSharing !== 'undefined') { + if ( + typeof updateSpaceDto.disablePublicSharing !== 'undefined' || + typeof updateSpaceDto.allowViewerComments !== 'undefined' + ) { const workspace = await this.workspaceRepo.findById(workspaceId, { withLicenseKey: true, }); if ( - !this.licenseCheckService.hasFeature(workspace.licenseKey, 'security:settings', workspace.plan) + typeof updateSpaceDto.disablePublicSharing !== 'undefined' && + !this.licenseCheckService.hasFeature( + workspace.licenseKey, + Feature.SECURITY_SETTINGS, + workspace.plan, + ) ) { - throw new ForbiddenException( - 'This feature requires a valid license', - ); + throw new ForbiddenException('This feature requires a valid license'); + } + + if ( + typeof updateSpaceDto.allowViewerComments !== 'undefined' && + !this.licenseCheckService.hasFeature( + workspace.licenseKey, + Feature.VIEWER_COMMENTS, + workspace.plan, + ) + ) { + throw new ForbiddenException('This feature requires a valid license'); } } @@ -179,6 +197,22 @@ export class SpaceService { } } + if (typeof updateSpaceDto.allowViewerComments !== 'undefined') { + const prev = settingsBefore?.comments?.allowViewerComments ?? false; + if (prev !== updateSpaceDto.allowViewerComments) { + before.allowViewerComments = prev; + after.allowViewerComments = updateSpaceDto.allowViewerComments; + } + + await this.spaceRepo.updateCommentSettings( + updateSpaceDto.spaceId, + workspaceId, + 'allowViewerComments', + updateSpaceDto.allowViewerComments, + trx, + ); + } + updatedSpace = await this.spaceRepo.updateSpace( { name: updateSpaceDto.name, diff --git a/apps/server/src/core/workspace/services/workspace.service.ts b/apps/server/src/core/workspace/services/workspace.service.ts index c3b6550f..d0bd27c6 100644 --- a/apps/server/src/core/workspace/services/workspace.service.ts +++ b/apps/server/src/core/workspace/services/workspace.service.ts @@ -18,6 +18,7 @@ import { WorkspaceRepo } from '@docmost/db/repos/workspace/workspace.repo'; import { KyselyDB, KyselyTransaction } from '@docmost/db/types/kysely.types'; import { executeTx } from '@docmost/db/utils'; import { InjectKysely } from 'nestjs-kysely'; +import { Feature } from '../../../common/features'; import { User } from '@docmost/db/types/entity.types'; import { GroupUserRepo } from '@docmost/db/repos/group/group-user.repo'; import { GroupRepo } from '@docmost/db/repos/group/group.repo'; @@ -352,7 +353,7 @@ export class WorkspaceService { typeof updateWorkspaceDto.trashRetentionDays !== 'undefined' || typeof updateWorkspaceDto.restrictApiToAdmins !== 'undefined' ) { - if (!this.licenseCheckService.hasFeature(ws.licenseKey, 'security:settings', ws.plan)) { + if (!this.licenseCheckService.hasFeature(ws.licenseKey, Feature.SECURITY_SETTINGS, ws.plan)) { throw new ForbiddenException( 'This feature requires a valid license', ); diff --git a/apps/server/src/database/repos/space/space.repo.ts b/apps/server/src/database/repos/space/space.repo.ts index 8ec5904c..0b389665 100644 --- a/apps/server/src/database/repos/space/space.repo.ts +++ b/apps/server/src/database/repos/space/space.repo.ts @@ -111,6 +111,28 @@ export class SpaceRepo { .executeTakeFirst(); } + async updateCommentSettings( + spaceId: string, + workspaceId: string, + prefKey: string, + prefValue: string | boolean, + trx?: KyselyTransaction, + ) { + const db = dbOrTx(this.db, trx); + return db + .updateTable('spaces') + .set({ + settings: sql`COALESCE(settings, '{}'::jsonb) + || jsonb_build_object('comments', COALESCE(settings->'comments', '{}'::jsonb) + || jsonb_build_object('${sql.raw(prefKey)}', ${sql.lit(prefValue)}))`, + updatedAt: new Date(), + }) + .where('id', '=', spaceId) + .where('workspaceId', '=', workspaceId) + .returningAll() + .executeTakeFirst(); + } + async insertSpace( insertableSpace: InsertableSpace, trx?: KyselyTransaction, diff --git a/apps/server/src/ee b/apps/server/src/ee index a258ca36..c70a29cb 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit a258ca366077d6e97b340c4faf56747ca6a3ca78 +Subproject commit c70a29cb2532364d235f00409588448f9d995e6b From aa27d576243b4a8d8dd24526b68cd03543a73754 Mon Sep 17 00:00:00 2001 From: Julien Fontanet <julien.fontanet@isonoe.net> Date: Sat, 28 Mar 2026 21:23:21 +0100 Subject: [PATCH 20/44] fix: notification items are now real links (#2039) Replace UnstyledButton with UnstyledButton component={Link} so each notification renders as a real anchor element. Regular left-clicks use SPA navigation and close the popover; Ctrl/Cmd/middle-click open the page in a new tab. All click types mark the notification as read. --- .../components/notification-item.tsx | 38 +++++++++++-------- .../notification/notification.module.css | 1 + 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/apps/client/src/features/notification/components/notification-item.tsx b/apps/client/src/features/notification/components/notification-item.tsx index 0d7db515..0ef81e44 100644 --- a/apps/client/src/features/notification/components/notification-item.tsx +++ b/apps/client/src/features/notification/components/notification-item.tsx @@ -13,7 +13,7 @@ import { import { CustomAvatar } from "@/components/ui/custom-avatar"; import { INotification } from "../types/notification.types"; import { Trans, useTranslation } from "react-i18next"; -import { useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; import { useState } from "react"; import { useMarkReadMutation } from "../queries/notification-query"; import { buildPageUrl } from "@/features/page/page.utils"; @@ -30,7 +30,6 @@ export function NotificationItem({ onNavigate, }: NotificationItemProps) { const { t } = useTranslation(); - const navigate = useNavigate(); const markRead = useMarkReadMutation(); const [hovered, setHovered] = useState(false); @@ -55,32 +54,39 @@ export function NotificationItem({ } }; - const handleClick = () => { - if (notification.page && notification.space) { - if (isUnread) { - markRead.mutate([notification.id]); - } - navigate( - buildPageUrl( + const pageUrl = + notification.page && notification.space + ? buildPageUrl( notification.space.slug, notification.page.slugId, notification.page.title, - ), - ); - onNavigate(); - } - }; + ) + : undefined; - const handleMarkRead = (e: React.MouseEvent) => { - e.stopPropagation(); + const markReadIfNeeded = () => { if (isUnread) { markRead.mutate([notification.id]); } }; + const handleClick = () => { + markReadIfNeeded(); + onNavigate(); + }; + + const handleMarkRead = (e: React.MouseEvent) => { + e.stopPropagation(); + markReadIfNeeded(); + }; + return ( <UnstyledButton + component={Link} + to={pageUrl ?? ""} onClick={handleClick} + // auxclick fires for all non-primary buttons; guard to middle-click only (button 1) + // so that right-click (button 2, context menu) does not mark as read + onAuxClick={(e: React.MouseEvent) => e.button === 1 && markReadIfNeeded()} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} w="100%" diff --git a/apps/client/src/features/notification/notification.module.css b/apps/client/src/features/notification/notification.module.css index 8a80bcf8..54e790cc 100644 --- a/apps/client/src/features/notification/notification.module.css +++ b/apps/client/src/features/notification/notification.module.css @@ -1,4 +1,5 @@ .notificationItem { + display: block; padding: 8px 12px; border-radius: 4px; cursor: pointer; From cbdb37ed0a5ce3dbafd89a8f43251ab082f046fa Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sat, 28 Mar 2026 20:29:06 +0000 Subject: [PATCH 21/44] New Crowdin updates (#2061) --- .../public/locales/de-DE/translation.json | 10 ++++++++++ .../public/locales/en-US/translation.json | 18 +++++++++--------- .../public/locales/es-ES/translation.json | 10 ++++++++++ .../public/locales/fr-FR/translation.json | 12 +++++++++++- .../public/locales/it-IT/translation.json | 10 ++++++++++ .../public/locales/ja-JP/translation.json | 10 ++++++++++ .../public/locales/ko-KR/translation.json | 10 ++++++++++ .../public/locales/nl-NL/translation.json | 10 ++++++++++ .../public/locales/pt-BR/translation.json | 10 ++++++++++ .../public/locales/ru-RU/translation.json | 10 ++++++++++ .../public/locales/uk-UA/translation.json | 10 ++++++++++ .../public/locales/zh-CN/translation.json | 10 ++++++++++ 12 files changed, 120 insertions(+), 10 deletions(-) diff --git a/apps/client/public/locales/de-DE/translation.json b/apps/client/public/locales/de-DE/translation.json index feb8b46b..437181d4 100644 --- a/apps/client/public/locales/de-DE/translation.json +++ b/apps/client/public/locales/de-DE/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Horizontale Trennlinie einfügen", "Upload any image from your device.": "Laden Sie ein beliebiges Bild von Ihrem Gerät hoch.", "Upload any video from your device.": "Laden Sie ein beliebiges Video von Ihrem Gerät hoch.", + "Upload any audio from your device.": "Laden Sie beliebige Audiodateien von Ihrem Gerät hoch.", "Upload any file from your device.": "Laden Sie eine beliebige Datei von Ihrem Gerät hoch.", "Uploading {{name}}": "Lade {{name}} hoch", "Uploading file": "Datei wird hochgeladen", @@ -351,6 +352,12 @@ "Divider": "Trennlinie", "Quote": "Zitat", "Image": "Bild", + "Audio": "Audio.", + "Embed PDF": "PDF einbetten", + "Upload and embed a PDF file.": "Laden Sie eine PDF-Datei hoch und betten Sie sie ein.", + "Embed as PDF": "Als PDF einbetten", + "Failed to load PDF": "Fehler beim Laden der PDF", + "Convert to attachment": "In Anhang umwandeln", "File attachment": "Dateianhang", "Toggle block": "Block umschalten", "Callout": "Hinweisbox", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Verhindern Sie, dass Mitglieder Seiten öffentlich teilen.", "Toggle public sharing": "Öffentliches Teilen umschalten", "Toggle space public sharing": "Öffentliches Teilen im Bereich umschalten", + "Allow viewers to comment": "Zuschauern erlauben, Kommentare zu hinterlassen", + "Allow viewers to add comments on pages in this space.": "Erlauben Sie Zuschauern, Kommentare auf Seiten in diesem Bereich hinzuzufügen.", + "Toggle viewer comments": "Zuschauerkommentare umschalten", "Public sharing is disabled at the workspace level": "Öffentliches Teilen ist auf der Arbeitsbereichsebene deaktiviert", "Prevent pages in this space from being shared publicly.": "Verhindern Sie, dass Seiten in diesem Bereich öffentlich geteilt werden.", "Page permissions": "Seitenberechtigungen", diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index b0dd7d53..97a5abb3 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -184,20 +184,20 @@ "Updated successfully": "Updated successfully.", "User": "User.", "Workspace": "Workspace.", - "Workspace Name": "Workspace Name.", + "Workspace Name": "Workspace name.", "Workspace settings": "Workspace settings.", "You can change your password here.": "You can change your password here.", - "Your Email": "Your Email.", + "Your Email": "Your email.", "Your import is complete.": "Your import is complete.", "Your name": "Your name.", - "Your Name": "Your Name.", + "Your Name": "Your name.", "Your password": "Your password.", "Your password must be a minimum of 8 characters.": "Your password must be a minimum of 8 characters.", "Sidebar toggle": "Sidebar toggle.", "Comments": "Comments.", "404 page not found": "404 page not found.", "Sorry, we can't find the page you are looking for.": "Sorry, we can't find the page you are looking for.", - "Take me back to homepage": "Take me back to homepage.", + "Take me back to homepage": "Take me back to the homepage.", "Forgot password": "Forgot password.", "Forgot your password?": "Forgot your password?", "A password reset link has been sent to your email. Please check your inbox.": "A password reset link has been sent to your email. Please check your inbox.", @@ -223,12 +223,12 @@ "Failed to delete comment": "Failed to delete comment", "Comment resolved successfully": "Comment resolved successfully", "Comment re-opened successfully": "Comment re-opened successfully.", - "Comment unresolved successfully": "Comment unresolved successfully.", + "Comment unresolved successfully": "Comment marked as unresolved successfully.", "Failed to resolve comment": "Failed to resolve comment", "Resolve comment": "Resolve comment.", - "Unresolve comment": "Unresolve comment.", - "Resolve Comment Thread": "Resolve Comment Thread.", - "Unresolve Comment Thread": "Unresolve Comment Thread.", + "Unresolve comment": "Mark comment as unresolved.", + "Resolve Comment Thread": "Resolve comment thread.", + "Unresolve Comment Thread": "Mark comment thread as unresolved.", "Are you sure you want to resolve this comment thread? This will mark it as completed.": "Are you sure you want to resolve this comment thread? This will mark it as completed.", "Are you sure you want to unresolve this comment thread?": "Are you sure you want to unresolve this comment thread?", "Resolved": "Resolved.", @@ -370,7 +370,7 @@ "Insert mermaid diagram": "Insert mermaid diagram.", "Insert and design Drawio diagrams": "Insert and design Drawio diagrams.", "Insert current date": "Insert current date.", - "Draw and sketch excalidraw diagrams": "Draw and sketch excalidraw diagrams.", + "Draw and sketch excalidraw diagrams": "Draw and sketch Excalidraw diagrams.", "Multiple": "Multiple.", "Turn into": "Turn into", "Text align": "Text align", diff --git a/apps/client/public/locales/es-ES/translation.json b/apps/client/public/locales/es-ES/translation.json index 797523d7..2b6e88da 100644 --- a/apps/client/public/locales/es-ES/translation.json +++ b/apps/client/public/locales/es-ES/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Insertar regla horizontal", "Upload any image from your device.": "Sube cualquier imagen desde tu dispositivo.", "Upload any video from your device.": "Sube cualquier video desde tu dispositivo.", + "Upload any audio from your device.": "Sube cualquier audio desde tu dispositivo.", "Upload any file from your device.": "Sube cualquier archivo desde tu dispositivo.", "Uploading {{name}}": "Subiendo {{name}}", "Uploading file": "Subiendo archivo", @@ -351,6 +352,12 @@ "Divider": "Divisor", "Quote": "Cita", "Image": "Imagen", + "Audio": "Audio.", + "Embed PDF": "Adjuntar PDF", + "Upload and embed a PDF file.": "Sube y adjunta un archivo PDF.", + "Embed as PDF": "Adjuntar como PDF", + "Failed to load PDF": "Error al cargar el PDF", + "Convert to attachment": "Convertir en adjunto", "File attachment": "Adjunto de archivo", "Toggle block": "Alternar bloque", "Callout": "Aviso", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Evitar que los miembros compartan páginas públicamente.", "Toggle public sharing": "Alternar el uso compartido público", "Toggle space public sharing": "Alternar el uso compartido público del espacio", + "Allow viewers to comment": "Permitir que los espectadores comenten", + "Allow viewers to add comments on pages in this space.": "Permitir que los espectadores agreguen comentarios en las páginas de este espacio.", + "Toggle viewer comments": "Activar/desactivar comentarios de los espectadores", "Public sharing is disabled at the workspace level": "El uso compartido público está desactivado a nivel de espacio de trabajo", "Prevent pages in this space from being shared publicly.": "Evitar que las páginas en este espacio se compartan públicamente.", "Page permissions": "Permisos de la página},{", diff --git a/apps/client/public/locales/fr-FR/translation.json b/apps/client/public/locales/fr-FR/translation.json index d3fdf33f..288dfe84 100644 --- a/apps/client/public/locales/fr-FR/translation.json +++ b/apps/client/public/locales/fr-FR/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Insérer un séparateur de règle horizontale", "Upload any image from your device.": "Téléchargez n'importe quelle image depuis votre appareil.", "Upload any video from your device.": "Téléchargez n'importe quelle vidéo depuis votre appareil.", + "Upload any audio from your device.": "Téléchargez n'importe quel fichier audio depuis votre appareil.", "Upload any file from your device.": "Téléchargez n'importe quel fichier depuis votre appareil.", "Uploading {{name}}": "Téléchargement de {{name}}", "Uploading file": "Téléchargement du fichier", @@ -351,6 +352,12 @@ "Divider": "Diviseur", "Quote": "Citation", "Image": "Image", + "Audio": "Audio.", + "Embed PDF": "Intégrer un PDF", + "Upload and embed a PDF file.": "Téléchargez et intégrez un fichier PDF.", + "Embed as PDF": "Intégrer comme PDF", + "Failed to load PDF": "Échec du chargement du PDF", + "Convert to attachment": "Convertir en pièce jointe", "File attachment": "Pièce jointe", "Toggle block": "Basculer le bloc", "Callout": "Appel", @@ -415,7 +422,7 @@ "Move page": "Déplacer la page", "Move page to a different space.": "Déplacer la page vers un autre espace.", "Real-time editor connection lost. Retrying...": "Connexion avec l'éditeur en temps réel perdue. Nouvelle tentative...", - "Table of contents": "", + "Table of contents": "Table des matières.", "Add headings (H1, H2, H3) to generate a table of contents.": "Ajoutez des titres (H1, H2, H3) pour générer une table des matières.", "Share": "Partager", "Public sharing": "Partage public", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Empêcher les membres de partager des pages publiquement.", "Toggle public sharing": "Basculer le partage public", "Toggle space public sharing": "Basculer le partage public de l'espace", + "Allow viewers to comment": "Autoriser les spectateurs à commenter", + "Allow viewers to add comments on pages in this space.": "Autoriser les spectateurs à ajouter des commentaires sur les pages de cet espace.", + "Toggle viewer comments": "Basculer les commentaires des spectateurs", "Public sharing is disabled at the workspace level": "Le partage public est désactivé au niveau de l'espace de travail", "Prevent pages in this space from being shared publicly.": "Empêcher les pages de cet espace d'être partagées publiquement.", "Page permissions": "Autorisations de la page", diff --git a/apps/client/public/locales/it-IT/translation.json b/apps/client/public/locales/it-IT/translation.json index fdcaa8d1..f69650f0 100644 --- a/apps/client/public/locales/it-IT/translation.json +++ b/apps/client/public/locales/it-IT/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Inserisci divisore di regola orizzontale", "Upload any image from your device.": "Carica un'immagine dal tuo dispositivo.", "Upload any video from your device.": "Carica qualsiasi video dal tuo dispositivo.", + "Upload any audio from your device.": "Carica qualsiasi audio dal tuo dispositivo.", "Upload any file from your device.": "Carica qualsiasi file dal tuo dispositivo.", "Uploading {{name}}": "Caricamento di {{name}}", "Uploading file": "Caricamento file", @@ -351,6 +352,12 @@ "Divider": "Divisore", "Quote": "Preventivo", "Image": "Immagine", + "Audio": "Audio.", + "Embed PDF": "Incorpora PDF", + "Upload and embed a PDF file.": "Carica e incorpora un file PDF.", + "Embed as PDF": "Incorpora come PDF", + "Failed to load PDF": "Caricamento del PDF non riuscito", + "Convert to attachment": "Converti in allegato", "File attachment": "Allegato file", "Toggle block": "Attiva blocco", "Callout": "Avviso", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Impedisci ai membri di condividere pubblicamente le pagine.", "Toggle public sharing": "Attiva/disattiva la condivisione pubblica", "Toggle space public sharing": "Attiva/disattiva la condivisione pubblica nello spazio", + "Allow viewers to comment": "Consenti agli utenti di commentare", + "Allow viewers to add comments on pages in this space.": "Consenti agli utenti di aggiungere commenti alle pagine in questo spazio.", + "Toggle viewer comments": "Attiva/disattiva i commenti degli utenti", "Public sharing is disabled at the workspace level": "La condivisione pubblica è disabilitata a livello di area di lavoro", "Prevent pages in this space from being shared publicly.": "Impedisci che le pagine in questo spazio vengano condivise pubblicamente.", "Page permissions": "Autorizzazioni della pagina.", diff --git a/apps/client/public/locales/ja-JP/translation.json b/apps/client/public/locales/ja-JP/translation.json index b14adb53..1d913114 100644 --- a/apps/client/public/locales/ja-JP/translation.json +++ b/apps/client/public/locales/ja-JP/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "区切り線を挿入します", "Upload any image from your device.": "デバイスから画像をアップロードします", "Upload any video from your device.": "デバイスから動画をアップロードします", + "Upload any audio from your device.": "デバイスから音声ファイルをアップロードします。", "Upload any file from your device.": "デバイスからファイルをアップロードします", "Uploading {{name}}": "{{name}} をアップロード中", "Uploading file": "ファイルをアップロード中", @@ -351,6 +352,12 @@ "Divider": "区切り線", "Quote": "引用", "Image": "画像", + "Audio": "音声。", + "Embed PDF": "PDFを埋め込む", + "Upload and embed a PDF file.": "PDFファイルをアップロードして埋め込みます。", + "Embed as PDF": "PDFとして埋め込む", + "Failed to load PDF": "PDFの読み込みに失敗しました", + "Convert to attachment": "添付ファイルに変換", "File attachment": "ファイル添付", "Toggle block": "ブロックを切り替える", "Callout": "コールアウト", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "メンバーがページを公開で共有するのを防ぐ。", "Toggle public sharing": "公開共有を切り替える", "Toggle space public sharing": "スペースの公開共有を切り替える", + "Allow viewers to comment": "閲覧者によるコメントを許可", + "Allow viewers to add comments on pages in this space.": "このスペース内のページに閲覧者がコメントを追加できるようにします。", + "Toggle viewer comments": "閲覧者コメントの切り替え", "Public sharing is disabled at the workspace level": "ワークスペースレベルで公開共有が無効になっています", "Prevent pages in this space from being shared publicly.": "このスペース内のページが公開で共有されるのを防ぐ。", "Page permissions": "ページのアクセス権", diff --git a/apps/client/public/locales/ko-KR/translation.json b/apps/client/public/locales/ko-KR/translation.json index 8bc26c55..24186a6f 100644 --- a/apps/client/public/locales/ko-KR/translation.json +++ b/apps/client/public/locales/ko-KR/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "가로 구분선 삽입", "Upload any image from your device.": "기기에서 이미지를 업로드하세요.", "Upload any video from your device.": "기기에서 비디오를 업로드하세요.", + "Upload any audio from your device.": "기기에서 오디오를 업로드하세요.", "Upload any file from your device.": "기기에서 파일을 업로드하세요.", "Uploading {{name}}": "{{name}} 업로드 중", "Uploading file": "파일 업로드 중", @@ -351,6 +352,12 @@ "Divider": "구분선", "Quote": "인용", "Image": "이미지", + "Audio": "오디오.", + "Embed PDF": "PDF 임베드", + "Upload and embed a PDF file.": "PDF 파일을 업로드하고 임베드하세요.", + "Embed as PDF": "PDF로 임베드", + "Failed to load PDF": "PDF 로드 실패", + "Convert to attachment": "첨부 파일로 변환", "File attachment": "파일 첨부", "Toggle block": "블록 토글", "Callout": "경고 상자", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "멤버들이 페이지를 공개적으로 공유하지 못하도록 방지하십시오.", "Toggle public sharing": "공유 전환", "Toggle space public sharing": "공간 공유 전환", + "Allow viewers to comment": "뷰어가 댓글을 달 수 있도록 허용", + "Allow viewers to add comments on pages in this space.": "이 공간 내 페이지에 뷰어가 댓글을 추가할 수 있도록 허용합니다.", + "Toggle viewer comments": "뷰어 댓글 전환", "Public sharing is disabled at the workspace level": "워크스페이스 수준에서 공유가 비활성화되었습니다.", "Prevent pages in this space from being shared publicly.": "이 공간의 페이지가 공개적으로 공유되지 않도록 방지하십시오.", "Page permissions": "페이지 권한},{", diff --git a/apps/client/public/locales/nl-NL/translation.json b/apps/client/public/locales/nl-NL/translation.json index d6eb9d5e..f1349d0f 100644 --- a/apps/client/public/locales/nl-NL/translation.json +++ b/apps/client/public/locales/nl-NL/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Horizontale lijn invoegen", "Upload any image from your device.": "Upload een afbeelding vanaf uw apparaat.", "Upload any video from your device.": "Upload een video vanaf uw apparaat.", + "Upload any audio from your device.": "Upload een audio vanaf uw apparaat.", "Upload any file from your device.": "Upload een bestand vanaf uw apparaat.", "Uploading {{name}}": "Uploaden {{name}}", "Uploading file": "Bestand uploaden", @@ -351,6 +352,12 @@ "Divider": "Scheidingslijn", "Quote": "Quote", "Image": "Afbeelding", + "Audio": "Audio.", + "Embed PDF": "PDF insluiten", + "Upload and embed a PDF file.": "Upload en sluit een PDF-bestand in.", + "Embed as PDF": "Insluiten als PDF", + "Failed to load PDF": "Laden van PDF mislukt", + "Convert to attachment": "Converteren naar bijlage", "File attachment": "Bestand bijlage", "Toggle block": "Schakel blok in/uit", "Callout": "Opmerking", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Voorkom dat leden pagina's openbaar delen.", "Toggle public sharing": "Wissel openbaar delen", "Toggle space public sharing": "Wissel openbaar delen van ruimte", + "Allow viewers to comment": "Toestaan dat kijkers reageren", + "Allow viewers to add comments on pages in this space.": "Sta kijkers toe om reacties toe te voegen op pagina\u0019s in deze ruimte.", + "Toggle viewer comments": "Reacties van kijkers in- of uitschakelen", "Public sharing is disabled at the workspace level": "Openbaar delen is uitgeschakeld op werkruimteniveau", "Prevent pages in this space from being shared publicly.": "Voorkom dat pagina's in deze ruimte openbaar worden gedeeld.", "Page permissions": "Pagina rechten", diff --git a/apps/client/public/locales/pt-BR/translation.json b/apps/client/public/locales/pt-BR/translation.json index 94750861..04ae0830 100644 --- a/apps/client/public/locales/pt-BR/translation.json +++ b/apps/client/public/locales/pt-BR/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Insira um divisor horizontal", "Upload any image from your device.": "Envie qualquer imagem do seu dispositivo.", "Upload any video from your device.": "Envie qualquer vídeo do seu dispositivo.", + "Upload any audio from your device.": "Envie qualquer áudio do seu dispositivo.", "Upload any file from your device.": "Envie qualquer arquivo do seu dispositivo.", "Uploading {{name}}": "Enviando {{name}}", "Uploading file": "Enviando arquivo", @@ -351,6 +352,12 @@ "Divider": "Divisor", "Quote": "Citação", "Image": "Imagem", + "Audio": "Áudio.", + "Embed PDF": "Incorporar PDF", + "Upload and embed a PDF file.": "Envie e incorpore um arquivo PDF.", + "Embed as PDF": "Incorporar como PDF", + "Failed to load PDF": "Falha ao carregar PDF", + "Convert to attachment": "Converter em anexo", "File attachment": "Anexo de arquivo", "Toggle block": "Bloco colapsável", "Callout": "Aviso", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Impedir que os membros compartilhem páginas publicamente.", "Toggle public sharing": "Alternar compartilhamento público", "Toggle space public sharing": "Alternar compartilhamento público do espaço", + "Allow viewers to comment": "Permitir que os visualizadores comentem", + "Allow viewers to add comments on pages in this space.": "Permitir que os visualizadores adicionem comentários em páginas deste espaço.", + "Toggle viewer comments": "Ativar/desativar comentários de visualizadores", "Public sharing is disabled at the workspace level": "O compartilhamento público está desativado no nível do espaço de trabalho", "Prevent pages in this space from being shared publicly.": "Impedir que as páginas neste espaço sejam compartilhadas publicamente.", "Page permissions": "Permissões da página},{", diff --git a/apps/client/public/locales/ru-RU/translation.json b/apps/client/public/locales/ru-RU/translation.json index f602bdd6..02a4b861 100644 --- a/apps/client/public/locales/ru-RU/translation.json +++ b/apps/client/public/locales/ru-RU/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Вставить горизонтальный разделитель", "Upload any image from your device.": "Загрузить любое изображение с вашего устройства.", "Upload any video from your device.": "Загрузить любое видео с вашего устройства.", + "Upload any audio from your device.": "Загрузите любой аудиофайл с вашего устройства.", "Upload any file from your device.": "Загрузить любой файл с вашего устройства.", "Uploading {{name}}": "Загрузка {{name}}", "Uploading file": "Загрузка файла", @@ -351,6 +352,12 @@ "Divider": "Разделитель", "Quote": "Цитата", "Image": "Изображение", + "Audio": "Аудио.", + "Embed PDF": "Встроить PDF", + "Upload and embed a PDF file.": "Загрузите и встроите PDF-файл.", + "Embed as PDF": "Встроить как PDF", + "Failed to load PDF": "Не удалось загрузить PDF", + "Convert to attachment": "Преобразовать в вложение", "File attachment": "Прикрепленный файл", "Toggle block": "Сворачиваемый блок", "Callout": "Выноска", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Запретить участникам делиться страницами публично.", "Toggle public sharing": "Переключить общий доступ", "Toggle space public sharing": "Переключить общий доступ для пространства", + "Allow viewers to comment": "Разрешить зрителям комментировать", + "Allow viewers to add comments on pages in this space.": "Разрешить зрителям добавлять комментарии на страницах в этом пространстве.", + "Toggle viewer comments": "Переключить комментарии зрителей", "Public sharing is disabled at the workspace level": "Общий доступ отключен на уровне рабочего пространства", "Prevent pages in this space from being shared publicly.": "Запретить делиться страницами в этом пространстве публично.", "Page permissions": "Права доступа к странице},{", diff --git a/apps/client/public/locales/uk-UA/translation.json b/apps/client/public/locales/uk-UA/translation.json index 96ec50a9..194cf161 100644 --- a/apps/client/public/locales/uk-UA/translation.json +++ b/apps/client/public/locales/uk-UA/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "Вставити горизонтальний роздільник", "Upload any image from your device.": "Завантажити будь-яке зображення з вашого пристрою.", "Upload any video from your device.": "Завантажити будь-яке відео з вашого пристрою.", + "Upload any audio from your device.": "Завантажте будь-який аудіофайл зі свого пристрою.", "Upload any file from your device.": "Завантажити будь-який файл з вашого пристрою.", "Uploading {{name}}": "Завантаження {{name}}", "Uploading file": "Завантаження файлу", @@ -351,6 +352,12 @@ "Divider": "Роздільник", "Quote": "Цитата", "Image": "Зображення", + "Audio": "Аудіо.", + "Embed PDF": "Вбудувати PDF", + "Upload and embed a PDF file.": "Завантажте та вбудуйте файл PDF.", + "Embed as PDF": "Вбудувати як PDF", + "Failed to load PDF": "Не вдалося завантажити PDF", + "Convert to attachment": "Перетворити на вкладення", "File attachment": "Прикріплений файл", "Toggle block": "Блок, що згортається", "Callout": "Виноска", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "Перешкодити учасникам публічно ділитися сторінками.", "Toggle public sharing": "Перемикання публічного доступу", "Toggle space public sharing": "Перемикання публічного доступу до просторів", + "Allow viewers to comment": "Дозволити глядачам коментувати", + "Allow viewers to add comments on pages in this space.": "Дозволити глядачам додавати коментарі на сторінках у цьому просторі.", + "Toggle viewer comments": "Увімкнути або вимкнути коментарі глядачів", "Public sharing is disabled at the workspace level": "Публічний доступ вимкнуто на рівні робочого простору", "Prevent pages in this space from being shared publicly.": "Перешкодити публічному поширенню сторінок у цьому просторі.", "Page permissions": "Права доступу до сторінки.", diff --git a/apps/client/public/locales/zh-CN/translation.json b/apps/client/public/locales/zh-CN/translation.json index 9ff28f8e..13f4f334 100644 --- a/apps/client/public/locales/zh-CN/translation.json +++ b/apps/client/public/locales/zh-CN/translation.json @@ -341,6 +341,7 @@ "Insert horizontal rule divider": "插入水平分割线", "Upload any image from your device.": "从设备上传任何图像", "Upload any video from your device.": "从设备上传任何视频", + "Upload any audio from your device.": "从您的设备上传任意音频文件。", "Upload any file from your device.": "从设备上传任何文件", "Uploading {{name}}": "正在上传{{name}}", "Uploading file": "正在上传文件", @@ -351,6 +352,12 @@ "Divider": "分割线", "Quote": "引用", "Image": "图像", + "Audio": "音频。", + "Embed PDF": "嵌入 PDF", + "Upload and embed a PDF file.": "上传并嵌入 PDF 文件。", + "Embed as PDF": "作为 PDF 嵌入", + "Failed to load PDF": "加载 PDF 失败", + "Convert to attachment": "转换为附件", "File attachment": "文件附件", "Toggle block": "切换块", "Callout": "标注块", @@ -442,6 +449,9 @@ "Prevent members from sharing pages publicly.": "阻止成员公开分享页面。", "Toggle public sharing": "切换公开分享", "Toggle space public sharing": "切换空间公开分享", + "Allow viewers to comment": "允许观众评论", + "Allow viewers to add comments on pages in this space.": "允许观众在此空间的页面上添加评论。", + "Toggle viewer comments": "切换观众评论", "Public sharing is disabled at the workspace level": "公开分享在工作区级别被禁用", "Prevent pages in this space from being shared publicly.": "阻止此空间中的页面被公开分享。", "Page permissions": "页面权限},{", From ccb35517bb248e62c7f428b7746d259e71b8ca1c Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 28 Mar 2026 20:29:31 +0000 Subject: [PATCH 22/44] sync --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index c70a29cb..f4867260 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit c70a29cb2532364d235f00409588448f9d995e6b +Subproject commit f48672608889233c0247c6d4ef7fcddd29540315 From 642c92f7790977c0467212ed6289227988a55a3e Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 28 Mar 2026 20:34:44 +0000 Subject: [PATCH 23/44] fix select --- apps/client/src/features/notification/notification.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/client/src/features/notification/notification.module.css b/apps/client/src/features/notification/notification.module.css index 54e790cc..d56986ac 100644 --- a/apps/client/src/features/notification/notification.module.css +++ b/apps/client/src/features/notification/notification.module.css @@ -3,6 +3,7 @@ padding: 8px 12px; border-radius: 4px; cursor: pointer; + user-select: none; } .notificationItem:hover { From a42ac3d45085ee85bc34fdbfaa6ed1b5b8407f81 Mon Sep 17 00:00:00 2001 From: Olivier Lambert <olivier.lambert@vates.fr> Date: Sat, 28 Mar 2026 23:26:47 +0100 Subject: [PATCH 24/44] fix: strip trailing whitespace-only paragraphs from pasted content (#2050) --- .../editor/extensions/markdown-clipboard.ts | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/apps/client/src/features/editor/extensions/markdown-clipboard.ts b/apps/client/src/features/editor/extensions/markdown-clipboard.ts index e1e3707d..0d6ab263 100644 --- a/apps/client/src/features/editor/extensions/markdown-clipboard.ts +++ b/apps/client/src/features/editor/extensions/markdown-clipboard.ts @@ -1,7 +1,7 @@ // adapted from: https://github.com/aguingand/tiptap-markdown/blob/main/src/extensions/tiptap/clipboard.js - MIT import { Extension } from "@tiptap/core"; import { Plugin, PluginKey } from "@tiptap/pm/state"; -import { DOMParser } from "@tiptap/pm/model"; +import { DOMParser, Fragment, Slice } from "@tiptap/pm/model"; import { find } from "linkifyjs"; import { markdownToHtml } from "@docmost/editor-ext"; @@ -40,7 +40,7 @@ export const MarkdownClipboard = Extension.create({ const { tr } = view.state; const { from, to } = view.state.selection; - const html = markdownToHtml(text); + const html = markdownToHtml(text.replace(/\n+$/, "")); const contentNodes = DOMParser.fromSchema( this.editor.schema, @@ -53,6 +53,37 @@ export const MarkdownClipboard = Extension.create({ view.dispatch(tr); return true; }, + // Strip trailing whitespace-only paragraphs from pasted content. + // Terminals (GNOME Terminal, etc.) often include trailing + // whitespace in their HTML clipboard data, which ProseMirror + // parses as an extra paragraph. Inside a list item this creates + // an orphan empty line that breaks the list structure. + transformPasted: (slice) => { + let { content, openStart, openEnd } = slice; + + // Remove trailing paragraphs that contain only whitespace + while (content.childCount > 1) { + const lastChild = content.lastChild; + if ( + lastChild?.type.name === "paragraph" && + lastChild.textContent.trim() === "" + ) { + const children = []; + for (let i = 0; i < content.childCount - 1; i++) { + children.push(content.child(i)); + } + content = Fragment.from(children); + } else { + break; + } + } + + if (content !== slice.content) { + return new Slice(content, openStart, Math.max(openEnd, 1)); + } + + return slice; + }, clipboardTextParser: (text, context, plainText) => { const link = find(text, { defaultProtocol: "http", @@ -64,7 +95,7 @@ export const MarkdownClipboard = Extension.create({ return null; } - const parsed = markdownToHtml(text); + const parsed = markdownToHtml(text.replace(/\n+$/, "")); return DOMParser.fromSchema(this.editor.schema).parseSlice( elementFromString(parsed), { From 412962204c84e0899474a52a0a06a5e1d85d59d1 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sun, 29 Mar 2026 02:19:09 +0100 Subject: [PATCH 25/44] fix: editor fixes (#2067) * autojoiner * fix marked * return clipboardTextSerializer as markdown * fix clipboardTextSerializer for single lines * cleanup two preceeding spaces in ordered lists item * fix extra paragraph in task list * don't zip sinple page exports --- .../features/editor/extensions/autojoiner.ts | 105 ++++++++++++++++++ .../features/editor/extensions/extensions.ts | 6 +- .../editor/extensions/markdown-clipboard.ts | 25 ++++- .../integrations/export/export.controller.ts | 30 +++-- .../src/integrations/export/export.service.ts | 9 +- .../src/lib/markdown/utils/marked.utils.ts | 21 ++-- .../src/lib/markdown/utils/turndown.utils.ts | 55 ++++++--- 7 files changed, 217 insertions(+), 34 deletions(-) create mode 100644 apps/client/src/features/editor/extensions/autojoiner.ts diff --git a/apps/client/src/features/editor/extensions/autojoiner.ts b/apps/client/src/features/editor/extensions/autojoiner.ts new file mode 100644 index 00000000..b69ed042 --- /dev/null +++ b/apps/client/src/features/editor/extensions/autojoiner.ts @@ -0,0 +1,105 @@ +// https://github.com/NiclasDev63/tiptap-extension-auto-joiner - MIT +import { Extension } from "@tiptap/core"; +import { Plugin, PluginKey } from "@tiptap/pm/state"; +import { canJoin } from "@tiptap/pm/transform"; +import { getNodeType } from "@tiptap/react"; +import { NodeType } from "@tiptap/pm/model"; +import { Transaction } from "@tiptap/pm/state"; + +// https://discuss.prosemirror.net/t/how-to-autojoin-all-the-time/2957/4 +// Adapted from prosemirror-commands wrapDispatchForJoin +function autoJoin( + transactions: readonly Transaction[], + newTr: Transaction, + nodeTypes: NodeType[] +) { + // Collect changed ranges across all transactions, mapping earlier ranges + // forward through later mappings so every position lands in newTr.doc space. + let ranges: number[] = []; + for (const tr of transactions) { + for (let i = 0; i < tr.mapping.maps.length; i++) { + let map = tr.mapping.maps[i]; + if (!map) continue; + for (let j = 0; j < ranges.length; j++) ranges[j] = map.map(ranges[j]!); + map.forEach((_s, _e, from, to) => ranges.push(from, to)); + } + } + + // Figure out which joinable points exist inside those ranges, + // by checking all node boundaries in their parent nodes. + // Resolve against newTr.doc — the same document we will join on. + let joinable: number[] = []; + for (let i = 0; i < ranges.length; i += 2) { + let from = ranges[i]!, + to = ranges[i + 1]!; + let $from = newTr.doc.resolve(from), + depth = $from.sharedDepth(to), + parent = $from.node(depth); + for ( + let index = $from.indexAfter(depth), pos = $from.after(depth + 1); + pos <= to; + ++index + ) { + let after = parent.maybeChild(index); + if (!after) break; + if (index && joinable.indexOf(pos) == -1) { + let before = parent.child(index - 1); + if (before.type == after.type && nodeTypes.includes(before.type)) + joinable.push(pos); + } + pos += after.nodeSize; + } + } + + // Join the joinable points (reverse order to preserve earlier positions) + let joined = false; + joinable.sort((a, b) => a - b); + for (let i = joinable.length - 1; i >= 0; i--) { + if (canJoin(newTr.doc, joinable[i]!)) { + newTr.join(joinable[i]!); + joined = true; + } + } + + return joined; +} + +export interface AutoJoinerOptions { + elementsToJoin: string[]; +} + +const AutoJoiner = Extension.create<AutoJoinerOptions>({ + name: "autoJoiner", + + addOptions() { + return { + elementsToJoin: [], + }; + }, + + addProseMirrorPlugins() { + const plugin = new PluginKey(this.name); + const joinableNodes = [ + this.editor.schema.nodes.bulletList, + this.editor.schema.nodes.orderedList, + ]; + this.options.elementsToJoin.forEach((element) => { + const nodeTyp = getNodeType(element, this.editor.schema); + joinableNodes.push(nodeTyp); + }); + + return [ + new Plugin({ + key: plugin, + appendTransaction(transactions, _, newState) { + let newTr = newState.tr; + if (autoJoin(transactions, newTr, joinableNodes as NodeType[])) { + return newTr; + } + }, + }), + ]; + }, +}); + +export default AutoJoiner; diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index 37f173dc..c5ca4cd1 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -49,7 +49,7 @@ import { SharedStorage, Columns, Column, - Status + Status, } from "@docmost/editor-ext"; import { randomElement, @@ -97,6 +97,7 @@ import i18n from "@/i18n.ts"; import { MarkdownClipboard } from "@/features/editor/extensions/markdown-clipboard.ts"; import EmojiCommand from "./emoji-command"; import { countWords } from "alfaaz"; +import AutoJoiner from "@/features/editor/extensions/autojoiner.ts"; const lowlight = createLowlight(common); lowlight.register("mermaid", plaintext); @@ -353,6 +354,9 @@ export const mainExtensions = [ }).configure(), Columns, Column, + AutoJoiner.configure({ + elementsToJoin: [], + }), ] as any; type CollabExtensions = (provider: HocuspocusProvider, user: IUser) => any[]; diff --git a/apps/client/src/features/editor/extensions/markdown-clipboard.ts b/apps/client/src/features/editor/extensions/markdown-clipboard.ts index 0d6ab263..de0ca144 100644 --- a/apps/client/src/features/editor/extensions/markdown-clipboard.ts +++ b/apps/client/src/features/editor/extensions/markdown-clipboard.ts @@ -1,9 +1,9 @@ // adapted from: https://github.com/aguingand/tiptap-markdown/blob/main/src/extensions/tiptap/clipboard.js - MIT import { Extension } from "@tiptap/core"; import { Plugin, PluginKey } from "@tiptap/pm/state"; -import { DOMParser, Fragment, Slice } from "@tiptap/pm/model"; +import { DOMParser, DOMSerializer, Fragment, Slice } from "@tiptap/pm/model"; import { find } from "linkifyjs"; -import { markdownToHtml } from "@docmost/editor-ext"; +import { markdownToHtml, htmlToMarkdown } from "@docmost/editor-ext"; export const MarkdownClipboard = Extension.create({ name: "markdownClipboard", @@ -19,6 +19,27 @@ export const MarkdownClipboard = Extension.create({ new Plugin({ key: new PluginKey("markdownClipboard"), props: { + clipboardTextSerializer: (slice) => { + const listTypes = ["bulletList", "orderedList", "taskList"]; + let topLevelCount = 0; + let hasList = false; + slice.content.forEach((node) => { + if (listTypes.includes(node.type.name)) { + hasList = true; + topLevelCount += node.childCount; + } else { + topLevelCount++; + } + }); + + if (!hasList || topLevelCount < 2) return null; + + const div = document.createElement("div"); + const serializer = DOMSerializer.fromSchema(this.editor.schema); + const fragment = serializer.serializeFragment(slice.content); + div.appendChild(fragment); + return htmlToMarkdown(div.innerHTML); + }, handlePaste: (view, event, slice) => { if (!event.clipboardData) { return false; diff --git a/apps/server/src/integrations/export/export.controller.ts b/apps/server/src/integrations/export/export.controller.ts index 0fc5fb96..1ce3f8c8 100644 --- a/apps/server/src/integrations/export/export.controller.ts +++ b/apps/server/src/integrations/export/export.controller.ts @@ -61,7 +61,7 @@ export class ExportController { await this.pageAccessService.validateCanView(page, user); - const zipFileStream = await this.exportService.exportPages( + const result = await this.exportService.exportPages( dto.pageId, dto.format, dto.includeAttachments, @@ -83,15 +83,29 @@ export class ExportController { }, }); - const fileName = sanitize(page.title || 'untitled') + '.zip'; + if (result.type === 'file') { + const ext = getExportExtension(dto.format); + const fileName = sanitize(page.title || 'untitled') + ext; + const contentType = getMimeType(path.extname(fileName)); - res.headers({ - 'Content-Type': 'application/zip', - 'Content-Disposition': - 'attachment; filename="' + encodeURIComponent(fileName) + '"', - }); + res.headers({ + 'Content-Type': contentType, + 'Content-Disposition': + 'attachment; filename="' + encodeURIComponent(fileName) + '"', + }); - res.send(zipFileStream); + res.send(result.content); + } else { + const fileName = sanitize(page.title || 'untitled') + '.zip'; + + res.headers({ + 'Content-Type': 'application/zip', + 'Content-Disposition': + 'attachment; filename="' + encodeURIComponent(fileName) + '"', + }); + + res.send(result.stream); + } } @UseGuards(JwtAuthGuard) diff --git a/apps/server/src/integrations/export/export.service.ts b/apps/server/src/integrations/export/export.service.ts index 4e1350f3..d93f9ba0 100644 --- a/apps/server/src/integrations/export/export.service.ts +++ b/apps/server/src/integrations/export/export.service.ts @@ -150,6 +150,13 @@ export class ExportService { // set to null to make export of pages with parentId work pages[parentPageIndex].parentPageId = null; + const isSinglePage = pages.length === 1 && !includeAttachments; + + if (isSinglePage) { + const pageContent = await this.exportPage(format, pages[0], true); + return { type: 'file' as const, content: pageContent, page: pages[0] }; + } + const tree = buildTree(pages as Page[]); const baseUrl = await this.getWorkspaceBaseUrl(pages[0].workspaceId); @@ -170,7 +177,7 @@ export class ExportService { compression: 'DEFLATE', }); - return zipFile; + return { type: 'zip' as const, stream: zipFile, page: pages[0] }; } async exportSpace( diff --git a/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts b/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts index 15797711..04dc1978 100644 --- a/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts +++ b/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts @@ -5,18 +5,23 @@ import { mathInlineExtension } from "./math-inline.marked"; marked.use({ renderer: { - // @ts-ignore - list(body: string, isOrdered: boolean, start: number) { - if (isOrdered) { - const startAttr = start !== 1 ? ` start="${start}"` : ""; - return `<ol ${startAttr}>\n${body}</ol>\n`; + list({ ordered, start, items }) { + let body = ""; + for (const item of items) { + body += this.listitem(item); } - const dataType = body.includes(`<input`) ? ' data-type="taskList"' : ""; + if (ordered) { + const startAttr = start !== 1 ? ` start="${start}"` : ""; + return `<ol${startAttr}>\n${body}</ol>\n`; + } + + const isTaskList = items.some((item) => item.task); + const dataType = isTaskList ? ' data-type="taskList"' : ""; return `<ul${dataType}>\n${body}</ul>\n`; }, - // @ts-ignore - listitem({ text, raw, task: isTask, checked: isChecked }): string { + listitem({ tokens, task: isTask, checked: isChecked }) { + const text = this.parser.parse(tokens); if (!isTask) { return `<li>${text}</li>\n`; } diff --git a/packages/editor-ext/src/lib/markdown/utils/turndown.utils.ts b/packages/editor-ext/src/lib/markdown/utils/turndown.utils.ts index 71a2b512..635983df 100644 --- a/packages/editor-ext/src/lib/markdown/utils/turndown.utils.ts +++ b/packages/editor-ext/src/lib/markdown/utils/turndown.utils.ts @@ -21,6 +21,7 @@ export function htmlToMarkdown(html: string): string { callout, preserveDetail, listParagraph, + orderedListItem, mathInline, mathBlock, iframeEmbed, @@ -41,6 +42,40 @@ function listParagraph(turndownService: _TurndownService) { }); } +function orderedListItem(turndownService: _TurndownService) { + turndownService.addRule('orderedListItem', { + filter: function (node: HTMLInputElement) { + return node.nodeName === 'LI' && node.getAttribute('data-type') !== 'taskItem'; + }, + replacement: (content: string, node: HTMLInputElement, options: any) => { + const parent = node.parentNode as HTMLElement; + if (parent.nodeName !== 'OL' && parent.nodeName !== 'UL') { + return content; + } + + content = content + .replace(/^\n+/, '') + .replace(/\n+$/, '\n') + .replace(/\n/gm, '\n '); + + let prefix: string; + if (parent.nodeName === 'OL') { + const start = parseInt(parent.getAttribute('start') || '1', 10); + const index = Array.prototype.indexOf.call(parent.children, node); + prefix = `${start + index}. `; + } else { + prefix = `${options.bulletListMarker} `; + } + + return ( + prefix + + content + + (node.nextSibling && !/\n$/.test(content) ? '\n' : '') + ); + }, + }); +} + function callout(turndownService: _TurndownService) { turndownService.addRule('callout', { filter: function (node: HTMLInputElement) { @@ -63,25 +98,17 @@ function taskList(turndownService: _TurndownService) { node.parentNode.nodeName === 'UL' ); }, - replacement: function (content: string, node: HTMLInputElement) { - const checkbox = node.querySelector( - 'input[type="checkbox"]', - ) as HTMLInputElement; - const isChecked = checkbox.checked; + replacement: function (_content: string, node: HTMLInputElement) { + const isChecked = node.getAttribute('data-checked') === 'true'; + const div = node.querySelector('div'); + const text = div ? div.textContent.trim() : node.textContent.trim(); - // Process content like regular list items - content = content - .replace(/^\n+/, '') // remove leading newlines - .replace(/\n+$/, '\n') // replace trailing newlines with just a single one - .replace(/\n/gm, '\n '); // indent nested content with 2 spaces - - // Create the checkbox prefix const prefix = `- ${isChecked ? '[x]' : '[ ]'} `; return ( prefix + - content + - (node.nextSibling && !/\n$/.test(content) ? '\n' : '') + text + + (node.nextSibling && !/\n$/.test(text) ? '\n' : '') ); }, }); From c9cdfa0f17f57351b867bb9a81cf24d085da68e5 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sun, 29 Mar 2026 02:20:56 +0100 Subject: [PATCH 26/44] fix --- apps/client/src/features/comment/components/comment-menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/features/comment/components/comment-menu.tsx b/apps/client/src/features/comment/components/comment-menu.tsx index b9cd1e0e..fe047232 100644 --- a/apps/client/src/features/comment/components/comment-menu.tsx +++ b/apps/client/src/features/comment/components/comment-menu.tsx @@ -75,7 +75,7 @@ function CommentMenu({ {isResolved ? t("Re-open comment") : t("Resolve comment")} </Menu.Item> ) : ( - <Tooltip label={upgradeLabel} position="left" withPortal={false}> + <Tooltip label={upgradeLabel} position="left" withinPortal={false}> <Menu.Item disabled leftSection={<IconCircleCheck size={14} />}> {t("Resolve comment")} </Menu.Item> From bca85a49d65967c3bd7bd721db64808e1f4f6a0c Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sun, 29 Mar 2026 03:03:35 +0100 Subject: [PATCH 27/44] pin marked version --- packages/editor-ext/package.json | 4 +++- pnpm-lock.yaml | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/editor-ext/package.json b/packages/editor-ext/package.json index 022c4b6a..23ddcaff 100644 --- a/packages/editor-ext/package.json +++ b/packages/editor-ext/package.json @@ -9,5 +9,7 @@ "main": "dist/index.js", "module": "./src/index.ts", "types": "dist/index.d.ts", - "dependencies": {} + "dependencies": { + "marked": "17.0.5" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b5cf19c..f0389d23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -801,7 +801,11 @@ importers: specifier: ^8.57.1 version: 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) - packages/editor-ext: {} + packages/editor-ext: + dependencies: + marked: + specifier: 17.0.5 + version: 17.0.5 packages: From 5cea30cc5ca3b8dbbd7e7b2fb57bc243618e882a Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:11:21 +0100 Subject: [PATCH 28/44] fix markdown paste --- .../editor/extensions/markdown-clipboard.ts | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/client/src/features/editor/extensions/markdown-clipboard.ts b/apps/client/src/features/editor/extensions/markdown-clipboard.ts index de0ca144..230798c5 100644 --- a/apps/client/src/features/editor/extensions/markdown-clipboard.ts +++ b/apps/client/src/features/editor/extensions/markdown-clipboard.ts @@ -1,6 +1,6 @@ // adapted from: https://github.com/aguingand/tiptap-markdown/blob/main/src/extensions/tiptap/clipboard.js - MIT import { Extension } from "@tiptap/core"; -import { Plugin, PluginKey } from "@tiptap/pm/state"; +import { Plugin, PluginKey, TextSelection } from "@tiptap/pm/state"; import { DOMParser, DOMSerializer, Fragment, Slice } from "@tiptap/pm/model"; import { find } from "linkifyjs"; import { markdownToHtml, htmlToMarkdown } from "@docmost/editor-ext"; @@ -50,26 +50,46 @@ export const MarkdownClipboard = Extension.create({ } const text = event.clipboardData.getData("text/plain"); + const html = event.clipboardData.getData("text/html"); const vscode = event.clipboardData.getData("vscode-editor-data"); const vscodeData = vscode ? JSON.parse(vscode) : undefined; const language = vscodeData?.mode; - if (language !== "markdown") { + const isVscodeMarkdown = language === "markdown"; + const isPlainTextOnly = !html && !vscode && !!text; + + if (!isVscodeMarkdown && !isPlainTextOnly) { return false; } + if (isPlainTextOnly) { + if ((view as any).input?.shiftKey || !this.options.transformPastedText) { + return false; + } + + const link = find(text, { + defaultProtocol: "http", + }).find((item) => item.isLink && item.value === text); + + if (link) { + return false; + } + } + const { tr } = view.state; const { from, to } = view.state.selection; - const html = markdownToHtml(text.replace(/\n+$/, "")); + const parsed = markdownToHtml(text.replace(/\n+$/, "")); const contentNodes = DOMParser.fromSchema( this.editor.schema, - ).parseSlice(elementFromString(html), { + ).parseSlice(elementFromString(parsed), { preserveWhitespace: true, }); tr.replaceRange(from, to, contentNodes); + const insertEnd = tr.mapping.map(from, 1); + tr.setSelection(TextSelection.near(tr.doc.resolve(Math.max(from, insertEnd - 2)), -1)); tr.setMeta('paste', true) view.dispatch(tr); return true; @@ -105,26 +125,6 @@ export const MarkdownClipboard = Extension.create({ return slice; }, - clipboardTextParser: (text, context, plainText) => { - const link = find(text, { - defaultProtocol: "http", - }).find((item) => item.isLink && item.value === text); - - if (plainText || !this.options.transformPastedText || link) { - // don't parse plaintext link to allow link paste handler to work - // pasting with shift key prevents formatting - return null; - } - - const parsed = markdownToHtml(text.replace(/\n+$/, "")); - return DOMParser.fromSchema(this.editor.schema).parseSlice( - elementFromString(parsed), - { - preserveWhitespace: true, - context, - }, - ); - }, }, }), ]; From 2d6d82958181069ca7749cff3195458aebda1e22 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:25:45 +0100 Subject: [PATCH 29/44] New translations translation.json (English) (#2066) --- apps/client/public/locales/en-US/translation.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 97a5abb3..19149612 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -733,7 +733,5 @@ "Publish": "Publish.", "Security": "Security.", "Enforce SSO": "Enforce SSO.", - "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to log in with email and password.", - "Uploading {{name}}": "Uploading {{name}}", - "Uploading file": "Uploading file" + "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to log in with email and password." } From cbd0dd4a0bd44cf12bb40f4809bd1a9c88e6792f Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:29:12 +0100 Subject: [PATCH 30/44] feat: indexes (#2071) --- .../20260329T163516-add-new-indexes.ts | 333 ++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 apps/server/src/database/migrations/20260329T163516-add-new-indexes.ts diff --git a/apps/server/src/database/migrations/20260329T163516-add-new-indexes.ts b/apps/server/src/database/migrations/20260329T163516-add-new-indexes.ts new file mode 100644 index 00000000..559366b6 --- /dev/null +++ b/apps/server/src/database/migrations/20260329T163516-add-new-indexes.ts @@ -0,0 +1,333 @@ +import { type Kysely, sql } from 'kysely'; + +export async function up(db: Kysely<any>): Promise<void> { + await db.schema + .createIndex('idx_group_users_user_id') + .ifNotExists() + .on('group_users') + .column('user_id') + .execute(); + + await db.schema + .createIndex('idx_space_members_user_id') + .ifNotExists() + .on('space_members') + .column('user_id') + .execute(); + + await db.schema + .createIndex('idx_space_members_group_id') + .ifNotExists() + .on('space_members') + .column('group_id') + .execute(); + + // Page tree + await sql` + CREATE INDEX IF NOT EXISTS idx_pages_space_parent_position + ON pages (space_id, parent_page_id, position COLLATE "C") + WHERE deleted_at IS NULL + `.execute(db); + + await sql` + CREATE INDEX IF NOT EXISTS idx_pages_parent_page_id + ON pages (parent_page_id) + WHERE deleted_at IS NULL + `.execute(db); + + // Recent pages query + await sql` + CREATE INDEX IF NOT EXISTS idx_pages_space_updated + ON pages (space_id, updated_at DESC) + WHERE deleted_at IS NULL + `.execute(db); + + // Trash view + await sql` + CREATE INDEX IF NOT EXISTS idx_pages_space_deleted + ON pages (space_id, deleted_at DESC) + WHERE deleted_at IS NOT NULL + `.execute(db); + + await sql` + CREATE UNIQUE INDEX IF NOT EXISTS idx_workspaces_hostname_lower + ON workspaces (LOWER(hostname)) + `.execute(db); + + await db.schema + .createIndex('idx_workspaces_created_at') + .ifNotExists() + .on('workspaces') + .column('created_at') + .execute(); + + await db.schema + .createIndex('idx_users_workspace_deleted') + .ifNotExists() + .on('users') + .columns(['workspace_id', 'deleted_at']) + .execute(); + + await sql` + CREATE UNIQUE INDEX IF NOT EXISTS idx_spaces_slug_lower_workspace + ON spaces (LOWER(slug), workspace_id) + `.execute(db); + + await db.schema + .createIndex('idx_spaces_workspace_id') + .ifNotExists() + .on('spaces') + .column('workspace_id') + .execute(); + + await sql` + CREATE UNIQUE INDEX IF NOT EXISTS idx_groups_name_lower_workspace + ON groups (LOWER(name), workspace_id) + `.execute(db); + + await db.schema + .createIndex('idx_groups_workspace_id') + .ifNotExists() + .on('groups') + .column('workspace_id') + .execute(); + + await db.schema + .createIndex('idx_shares_page_id') + .ifNotExists() + .on('shares') + .column('page_id') + .execute(); + + await db.schema + .createIndex('idx_attachments_page_id') + .ifNotExists() + .on('attachments') + .column('page_id') + .execute(); + + await db.schema + .createIndex('idx_attachments_space_id') + .ifNotExists() + .on('attachments') + .column('space_id') + .execute(); + + await db.schema + .createIndex('idx_comments_page_id') + .ifNotExists() + .on('comments') + .column('page_id') + .execute(); + + await db.schema + .createIndex('idx_comments_parent_comment_id') + .ifNotExists() + .on('comments') + .column('parent_comment_id') + .execute(); + + await sql` + CREATE INDEX IF NOT EXISTS idx_page_history_page_created + ON page_history (page_id, created_at DESC) + `.execute(db); + + await db.schema + .createIndex('idx_attachments_workspace_id') + .ifNotExists() + .on('attachments') + .column('workspace_id') + .execute(); + + await db.schema + .createIndex('idx_backlinks_target_page_id') + .ifNotExists() + .on('backlinks') + .column('target_page_id') + .execute(); + + await db.schema + .createIndex('idx_pages_workspace_id') + .ifNotExists() + .on('pages') + .column('workspace_id') + .execute(); + + await db.schema + .createIndex('idx_pages_creator_id') + .ifNotExists() + .on('pages') + .column('creator_id') + .execute(); + + // Notifications: FK cascade from pages, spaces, comments + await db.schema + .createIndex('idx_notifications_page_id') + .ifNotExists() + .on('notifications') + .column('page_id') + .execute(); + + await db.schema + .createIndex('idx_notifications_space_id') + .ifNotExists() + .on('notifications') + .column('space_id') + .execute(); + + await db.schema + .createIndex('idx_notifications_comment_id') + .ifNotExists() + .on('notifications') + .column('comment_id') + .execute(); + + // Watchers: cleanup queries and FK cascade + await db.schema + .createIndex('idx_watchers_user_workspace') + .ifNotExists() + .on('watchers') + .columns(['user_id', 'workspace_id']) + .execute(); + + await db.schema + .createIndex('idx_watchers_space_id') + .ifNotExists() + .on('watchers') + .column('space_id') + .execute(); + + // Auth providers: all queries filter by workspaceId + await db.schema + .createIndex('idx_auth_providers_workspace_id') + .ifNotExists() + .on('auth_providers') + .column('workspace_id') + .execute(); + + // Auth accounts: SSO login lookup by provider user + await db.schema + .createIndex('idx_auth_accounts_provider_user_id') + .ifNotExists() + .on('auth_accounts') + .columns(['provider_user_id', 'auth_provider_id']) + .execute(); + + // Workspace invitations: listing and SSO lookup + await db.schema + .createIndex('idx_workspace_invitations_workspace_id') + .ifNotExists() + .on('workspace_invitations') + .column('workspace_id') + .execute(); + + // API keys: query and FK cascade + await db.schema + .createIndex('idx_api_keys_workspace_id') + .ifNotExists() + .on('api_keys') + .column('workspace_id') + .execute(); + + // User sessions: delete queries and FK cascade on all session states + await db.schema + .createIndex('idx_user_sessions_user_workspace') + .ifNotExists() + .on('user_sessions') + .columns(['user_id', 'workspace_id']) + .execute(); +} + +export async function down(db: Kysely<any>): Promise<void> { + await db.schema.dropIndex('idx_group_users_user_id').ifExists().execute(); + await db.schema.dropIndex('idx_space_members_user_id').ifExists().execute(); + await db.schema.dropIndex('idx_space_members_group_id').ifExists().execute(); + await db.schema + .dropIndex('idx_pages_space_parent_position') + .ifExists() + .execute(); + await db.schema.dropIndex('idx_pages_parent_page_id').ifExists().execute(); + await db.schema.dropIndex('idx_pages_space_updated').ifExists().execute(); + await db.schema.dropIndex('idx_pages_space_deleted').ifExists().execute(); + await db.schema + .dropIndex('idx_workspaces_hostname_lower') + .ifExists() + .execute(); + await db.schema.dropIndex('idx_workspaces_created_at').ifExists().execute(); + await db.schema + .dropIndex('idx_users_workspace_deleted') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_spaces_slug_lower_workspace') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_spaces_workspace_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_groups_name_lower_workspace') + .ifExists() + .execute(); + await db.schema.dropIndex('idx_groups_workspace_id').ifExists().execute(); + await db.schema.dropIndex('idx_shares_page_id').ifExists().execute(); + await db.schema.dropIndex('idx_attachments_page_id').ifExists().execute(); + await db.schema.dropIndex('idx_attachments_space_id').ifExists().execute(); + await db.schema.dropIndex('idx_comments_page_id').ifExists().execute(); + await db.schema + .dropIndex('idx_comments_parent_comment_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_page_history_page_created') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_attachments_workspace_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_backlinks_target_page_id') + .ifExists() + .execute(); + await db.schema.dropIndex('idx_pages_workspace_id').ifExists().execute(); + await db.schema.dropIndex('idx_pages_creator_id').ifExists().execute(); + await db.schema + .dropIndex('idx_notifications_page_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_notifications_space_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_notifications_comment_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_watchers_user_workspace') + .ifExists() + .execute(); + await db.schema.dropIndex('idx_watchers_space_id').ifExists().execute(); + await db.schema + .dropIndex('idx_auth_providers_workspace_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_auth_accounts_provider_user_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_workspace_invitations_workspace_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_api_keys_workspace_id') + .ifExists() + .execute(); + await db.schema + .dropIndex('idx_user_sessions_user_workspace') + .ifExists() + .execute(); +} From a062f7a165359e7fce15d6a88e6befb349749cfb Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:16:40 +0100 Subject: [PATCH 31/44] fix: enhance confluence importer (#2072) * fix placeholder * min resize dimensions * fix media links * fix --- .../components/attachment/attachment-view.tsx | 10 +++--- .../editor/components/audio/audio-view.tsx | 7 ++-- .../editor/components/image/image-view.tsx | 4 +-- .../editor/components/pdf/pdf-view.tsx | 20 ++++++----- .../editor/components/video/video-view.tsx | 7 ++-- .../features/editor/extensions/extensions.ts | 16 ++++----- apps/server/src/ee | 2 +- .../services/import-attachment.service.ts | 36 +++++++++++++++---- 8 files changed, 66 insertions(+), 36 deletions(-) diff --git a/apps/client/src/features/editor/components/attachment/attachment-view.tsx b/apps/client/src/features/editor/components/attachment/attachment-view.tsx index f6c13c80..b72bb00a 100644 --- a/apps/client/src/features/editor/components/attachment/attachment-view.tsx +++ b/apps/client/src/features/editor/components/attachment/attachment-view.tsx @@ -10,7 +10,7 @@ import { useCallback } from "react"; export default function AttachmentView(props: NodeViewProps) { const { t } = useTranslation(); const { editor, node, getPos, selected } = props; - const { url, name, size, mime, attachmentId } = node.attrs; + const { url, name, size, mime, attachmentId, placeholder } = node.attrs; const { hovered, ref } = useHover(); const isPdf = mime === "application/pdf" || name?.toLowerCase().endsWith(".pdf"); @@ -49,14 +49,14 @@ export default function AttachmentView(props: NodeViewProps) { h={25} > <Group wrap="nowrap" gap="sm" style={{ minWidth: 0, flex: 1 }}> - {url ? ( - <IconPaperclip size={20} style={{ flexShrink: 0 }} /> - ) : ( + {!url && placeholder ? ( <Loader size={20} style={{ flexShrink: 0 }} /> + ) : ( + <IconPaperclip size={20} style={{ flexShrink: 0 }} /> )} <Text component="span" size="md" truncate="end" style={{ minWidth: 0 }}> - {url ? name : t("Uploading {{name}}", { name })} + {!url && placeholder ? t("Uploading {{name}}", { name }) : name} </Text> <Text component="span" size="sm" c="dimmed" style={{ flexShrink: 0 }}> diff --git a/apps/client/src/features/editor/components/audio/audio-view.tsx b/apps/client/src/features/editor/components/audio/audio-view.tsx index a353ce45..9e5f619f 100644 --- a/apps/client/src/features/editor/components/audio/audio-view.tsx +++ b/apps/client/src/features/editor/components/audio/audio-view.tsx @@ -29,7 +29,7 @@ export default function AudioView(props: NodeViewProps) { return ( <NodeViewWrapper data-drag-handle> - <div className={`${classes.audioWrapper} ${!safeSrc ? classes.skeleton : ''}`}> + <div className={`${classes.audioWrapper} ${!safeSrc && placeholder ? classes.skeleton : ''}`}> {safeSrc && ( <audio className={classes.audio} @@ -49,7 +49,7 @@ export default function AudioView(props: NodeViewProps) { <Loader size={20} pos="absolute" top={6} right={6} /> </Group> )} - {!safeSrc && !previewSrc && ( + {!safeSrc && !previewSrc && placeholder && ( <Group justify="center" wrap="nowrap" gap="xs" maw="100%" px="md" h={54}> <Loader size={20} style={{ flexShrink: 0 }} /> <Text component="span" size="sm" truncate="end"> @@ -59,6 +59,9 @@ export default function AudioView(props: NodeViewProps) { </Text> </Group> )} + {!safeSrc && !previewSrc && !placeholder && ( + <audio className={classes.audio} controls /> + )} </div> </NodeViewWrapper> ); diff --git a/apps/client/src/features/editor/components/image/image-view.tsx b/apps/client/src/features/editor/components/image/image-view.tsx index 7ec3e26f..1f874694 100644 --- a/apps/client/src/features/editor/components/image/image-view.tsx +++ b/apps/client/src/features/editor/components/image/image-view.tsx @@ -33,7 +33,7 @@ export default function ImageView(props: NodeViewProps) { className={clsx( selected && "ProseMirror-selectednode", classes.imageWrapper, - !src && classes.skeleton, + !src && placeholder && classes.skeleton, alignClass, )} style={{ @@ -55,7 +55,7 @@ export default function ImageView(props: NodeViewProps) { <Loader size={20} pos="absolute" bottom={6} right={6} /> </Group> )} - {!src && !previewSrc && ( + {!src && !previewSrc && placeholder && ( <Group justify="center" wrap="nowrap" gap="xs" maw="100%" px="md"> <Loader size={20} style={{ flexShrink: 0 }} /> <Text component="span" size="sm" truncate="end"> diff --git a/apps/client/src/features/editor/components/pdf/pdf-view.tsx b/apps/client/src/features/editor/components/pdf/pdf-view.tsx index 6207da9f..4d06402b 100644 --- a/apps/client/src/features/editor/components/pdf/pdf-view.tsx +++ b/apps/client/src/features/editor/components/pdf/pdf-view.tsx @@ -73,15 +73,17 @@ export default function PdfView(props: NodeViewProps) { if (!src || !safeSrc) { return ( <NodeViewWrapper data-drag-handle> - <div className={`${classes.pdfWrapper} ${classes.skeleton}`} style={{ height: 600 }}> - <Group justify="center" wrap="nowrap" gap="xs" maw="100%" px="md"> - <Loader size={20} style={{ flexShrink: 0 }} /> - <Text component="span" size="sm" truncate="end"> - {placeholder?.name - ? t("Uploading {{name}}", { name: placeholder.name }) - : t("Uploading file")} - </Text> - </Group> + <div className={`${classes.pdfWrapper} ${placeholder ? classes.skeleton : ''}`} style={{ height: placeholder ? 600 : undefined }}> + {placeholder && ( + <Group justify="center" wrap="nowrap" gap="xs" maw="100%" px="md"> + <Loader size={20} style={{ flexShrink: 0 }} /> + <Text component="span" size="sm" truncate="end"> + {placeholder?.name + ? t("Uploading {{name}}", { name: placeholder.name }) + : t("Uploading file")} + </Text> + </Group> + )} </div> </NodeViewWrapper> ); diff --git a/apps/client/src/features/editor/components/video/video-view.tsx b/apps/client/src/features/editor/components/video/video-view.tsx index 1e662640..46ff7908 100644 --- a/apps/client/src/features/editor/components/video/video-view.tsx +++ b/apps/client/src/features/editor/components/video/video-view.tsx @@ -33,7 +33,7 @@ export default function VideoView(props: NodeViewProps) { className={clsx( selected && "ProseMirror-selectednode", classes.videoWrapper, - !src && classes.skeleton, + !src && placeholder && classes.skeleton, alignClass, )} style={{ @@ -60,7 +60,7 @@ export default function VideoView(props: NodeViewProps) { <Loader size={20} pos="absolute" top={6} right={6} /> </Group> )} - {!src && !previewSrc && ( + {!src && !previewSrc && placeholder && ( <Group justify="center" wrap="nowrap" gap="xs" maw="100%" px="md"> <Loader size={20} style={{ flexShrink: 0 }} /> <Text component="span" size="sm" truncate="end"> @@ -70,6 +70,9 @@ export default function VideoView(props: NodeViewProps) { </Text> </Group> )} + {!src && !previewSrc && !placeholder && ( + <video className={classes.video} controls /> + )} </div> </NodeViewWrapper> ); diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index c5ca4cd1..8f6e6cdb 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -253,8 +253,8 @@ export const mainExtensions = [ resize: { enabled: true, directions: ["left", "right"], - minWidth: 80, - minHeight: 40, + minWidth: 24, + minHeight: 16, alwaysPreserveAspectRatio: true, //@ts-ignore createCustomHandle: createImageHandle, @@ -266,8 +266,8 @@ export const mainExtensions = [ resize: { enabled: true, directions: ["left", "right"], - minWidth: 80, - minHeight: 40, + minWidth: 24, + minHeight: 16, alwaysPreserveAspectRatio: true, //@ts-ignore createCustomHandle: createResizeHandle, @@ -297,8 +297,8 @@ export const mainExtensions = [ resize: { enabled: true, directions: ["left", "right"], - minWidth: 80, - minHeight: 40, + minWidth: 24, + minHeight: 16, alwaysPreserveAspectRatio: true, //@ts-ignore createCustomHandle: createResizeHandle, @@ -310,8 +310,8 @@ export const mainExtensions = [ resize: { enabled: true, directions: ["left", "right"], - minWidth: 80, - minHeight: 40, + minWidth: 24, + minHeight: 16, alwaysPreserveAspectRatio: true, //@ts-ignore createCustomHandle: createResizeHandle, diff --git a/apps/server/src/ee b/apps/server/src/ee index f4867260..05f1c816 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit f48672608889233c0247c6d4ef7fcddd29540315 +Subproject commit 05f1c816a839072efc1143cce71322a9ed6b4a0a diff --git a/apps/server/src/integrations/import/services/import-attachment.service.ts b/apps/server/src/integrations/import/services/import-attachment.service.ts index 3c14d854..9100149b 100644 --- a/apps/server/src/integrations/import/services/import-attachment.service.ts +++ b/apps/server/src/integrations/import/services/import-attachment.service.ts @@ -193,6 +193,8 @@ export class ImportAttachmentService { // Build a map from resolved archive path → real filename from Confluence // metadata. Confluence Server archives often store files under numeric IDs // (e.g. "attachments/65601/65602") instead of the original filename. + // Also register aliases so HTML references using the original filename + // (e.g. "attachments/pageId/original.mp3") resolve to the numeric path. const pageDir = path.dirname(pageRelativePath); const attachmentNameByRelPath = new Map<string, string>(); for (const attachment of pageAttachments) { @@ -203,6 +205,13 @@ export class ImportAttachmentService { ); if (relPath && attachment.fileName) { attachmentNameByRelPath.set(relPath, attachment.fileName); + + const dir = path.posix.dirname(relPath); + const aliasKey = `${dir}/${attachment.fileName}`; + if (!attachmentCandidates.has(aliasKey)) { + attachmentCandidates.set(aliasKey, attachmentCandidates.get(relPath)!); + attachmentNameByRelPath.set(aliasKey, attachment.fileName); + } } } @@ -562,18 +571,31 @@ export class ImportAttachmentService { continue; } - // Check if already processed (was referenced in HTML) - if (processed.has(href)) { - continue; - } + // Resolve the metadata href to the actual archive path + const resolvedHref = resolveRelativeAttachmentPath( + href, + pageDir, + attachmentCandidates, + ); + if (!resolvedHref) continue; - // Skip if the file doesn't exist - if (!attachmentCandidates.has(href)) { + // Check if already processed (was referenced in HTML). + // Inline elements may have been processed under an alias key (original + // filename) rather than the numeric archive path, so also check whether + // the underlying absolute file path has already been uploaded. + const absPath = attachmentCandidates.get(resolvedHref); + const alreadyProcessed = + processed.has(resolvedHref) || + (absPath && + Array.from(processed.values()).some( + (entry) => entry.abs === absPath, + )); + if (alreadyProcessed) { continue; } // This attachment was in the list but not referenced in HTML - add it - const { attachmentId, apiFilePath, abs } = processFile(href); + const { attachmentId, apiFilePath, abs } = processFile(resolvedHref); const mime = mimeType || getMimeType(abs); // Add as attachment node at the end From c180d0e48755b614731ff5e92b900eb2365b0543 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:38:44 +0100 Subject: [PATCH 32/44] feat: ratelimits (#2073) * feat: rate limits * ip --- apps/server/package.json | 3 ++ apps/server/src/app.module.ts | 2 + apps/server/src/common/logger/pino.config.ts | 20 +++----- .../middlewares/audit-context.middleware.ts | 20 +------- apps/server/src/core/auth/auth.controller.ts | 5 ++ apps/server/src/ee | 2 +- .../integrations/throttle/throttle.module.ts | 35 ++++++++++++++ apps/server/src/main.ts | 2 + pnpm-lock.yaml | 48 +++++++++++++++++++ 9 files changed, 104 insertions(+), 33 deletions(-) create mode 100644 apps/server/src/integrations/throttle/throttle.module.ts diff --git a/apps/server/package.json b/apps/server/package.json index d8bab08c..a8869302 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -44,6 +44,7 @@ "@langchain/core": "1.1.34", "@langchain/textsplitters": "1.0.1", "@modelcontextprotocol/sdk": "^1.27.1", + "@nest-lab/throttler-storage-redis": "^1.2.0", "@nestjs-labs/nestjs-ioredis": "^11.0.4", "@nestjs/bullmq": "^11.0.4", "@nestjs/cache-manager": "^3.1.0", @@ -58,6 +59,7 @@ "@nestjs/platform-socket.io": "^11.1.17", "@nestjs/schedule": "^6.1.1", "@nestjs/terminus": "^11.1.1", + "@nestjs/throttler": "^6.5.0", "@nestjs/websockets": "^11.1.17", "@node-saml/passport-saml": "^5.1.0", "@react-email/components": "1.0.10", @@ -73,6 +75,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.15.1", "cookie": "^1.1.1", + "fastify-ip": "^2.0.0", "fs-extra": "^11.3.4", "happy-dom": "20.8.9", "ioredis": "^5.10.1", diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts index 6280ee09..b8cfc587 100644 --- a/apps/server/src/app.module.ts +++ b/apps/server/src/app.module.ts @@ -26,6 +26,7 @@ import KeyvRedis from '@keyv/redis'; import { LoggerModule } from './common/logger/logger.module'; import { ClsModule } from 'nestjs-cls'; import { NoopAuditModule } from './integrations/audit/audit.module'; +import { ThrottleModule } from './integrations/throttle/throttle.module'; const enterpriseModules = []; try { @@ -83,6 +84,7 @@ try { EventEmitterModule.forRoot(), SecurityModule, TelemetryModule, + ThrottleModule, ...enterpriseModules, ], controllers: [AppController], diff --git a/apps/server/src/common/logger/pino.config.ts b/apps/server/src/common/logger/pino.config.ts index 7299a8e9..0b8cd11a 100644 --- a/apps/server/src/common/logger/pino.config.ts +++ b/apps/server/src/common/logger/pino.config.ts @@ -50,20 +50,12 @@ export function createPinoConfig(): Params { }, }, serializers: { - req: (req) => { - const forwardedFor = req.headers?.['x-forwarded-for']; - const ip = - req.headers?.['cf-connecting-ip'] || - (typeof forwardedFor === 'string' ? forwardedFor.split(',')[0]?.trim() : undefined) || - req.remoteAddress; - - return { - method: req.method, - url: req.url, - ip, - userAgent: req.headers?.['user-agent'], - }; - }, + req: (req) => ({ + method: req.method, + url: req.url, + ip: req.ip || req.remoteAddress, + userAgent: req.headers?.['user-agent'], + }), res: (res) => ({ statusCode: res.statusCode, }), diff --git a/apps/server/src/common/middlewares/audit-context.middleware.ts b/apps/server/src/common/middlewares/audit-context.middleware.ts index f5066535..52956219 100644 --- a/apps/server/src/common/middlewares/audit-context.middleware.ts +++ b/apps/server/src/common/middlewares/audit-context.middleware.ts @@ -18,7 +18,8 @@ export class AuditContextMiddleware implements NestMiddleware { use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) { const workspaceId = (req as any).workspaceId ?? null; - const ipAddress = this.extractIpAddress(req); + + const ipAddress = (req as any).ip ?? (req as any).socket?.remoteAddress ?? null; const userAgent = (req.headers['user-agent'] as string) ?? null; @@ -35,21 +36,4 @@ export class AuditContextMiddleware implements NestMiddleware { next(); } - - private extractIpAddress(req: FastifyRequest['raw']): string | null { - const xForwardedFor = req.headers['x-forwarded-for']; - if (xForwardedFor) { - const ips = Array.isArray(xForwardedFor) - ? xForwardedFor[0] - : xForwardedFor.split(',')[0]; - return ips?.trim() ?? null; - } - - const xRealIp = req.headers['x-real-ip']; - if (xRealIp) { - return Array.isArray(xRealIp) ? xRealIp[0] : xRealIp; - } - - return (req as any).socket?.remoteAddress ?? null; - } } diff --git a/apps/server/src/core/auth/auth.controller.ts b/apps/server/src/core/auth/auth.controller.ts index 6eab6539..441bfc1c 100644 --- a/apps/server/src/core/auth/auth.controller.ts +++ b/apps/server/src/core/auth/auth.controller.ts @@ -10,6 +10,7 @@ import { UseGuards, Logger, } from '@nestjs/common'; +import { SkipThrottle, ThrottlerGuard } from '@nestjs/throttler'; import { LoginDto } from './dto/login.dto'; import { AuthService } from './services/auth.service'; import { SessionService } from '../session/session.service'; @@ -33,6 +34,7 @@ import { IAuditService, } from '../../integrations/audit/audit.service'; +@UseGuards(ThrottlerGuard) @Controller('auth') export class AuthController { private readonly logger = new Logger(AuthController.name); @@ -111,6 +113,7 @@ export class AuthController { return workspace; } + @SkipThrottle() @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post('change-password') @@ -173,6 +176,7 @@ export class AuthController { return this.authService.verifyUserToken(verifyUserTokenDto, workspace.id); } + @SkipThrottle() @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post('collab-token') @@ -183,6 +187,7 @@ export class AuthController { return this.authService.getCollabToken(user, workspace.id); } + @SkipThrottle() @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post('logout') diff --git a/apps/server/src/ee b/apps/server/src/ee index 05f1c816..350ef574 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 05f1c816a839072efc1143cce71322a9ed6b4a0a +Subproject commit 350ef574e398c318aa57ce5f79ab12e9d8329dcb diff --git a/apps/server/src/integrations/throttle/throttle.module.ts b/apps/server/src/integrations/throttle/throttle.module.ts new file mode 100644 index 00000000..8f080e1d --- /dev/null +++ b/apps/server/src/integrations/throttle/throttle.module.ts @@ -0,0 +1,35 @@ +import { Module } from '@nestjs/common'; +import { ThrottlerModule } from '@nestjs/throttler'; +import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'; +import { EnvironmentService } from '../environment/environment.service'; +import { EnvironmentModule } from '../environment/environment.module'; +import { parseRedisUrl } from '../../common/helpers'; +import Redis from 'ioredis'; + +@Module({ + imports: [ + ThrottlerModule.forRootAsync({ + imports: [EnvironmentModule], + useFactory: (environmentService: EnvironmentService) => { + const redisConfig = parseRedisUrl(environmentService.getRedisUrl()); + + return { + throttlers: [{ name: 'auth', ttl: 60_000, limit: 10 }], + errorMessage: 'Too many requests', + storage: new ThrottlerStorageRedisService( + new Redis({ + host: redisConfig.host, + port: redisConfig.port, + password: redisConfig.password, + db: redisConfig.db, + family: redisConfig.family, + keyPrefix: 'throttle:', + }), + ), + }; + }, + inject: [EnvironmentService], + }), + ], +}) +export class ThrottleModule {} diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts index 0f2a82a1..d47bf547 100644 --- a/apps/server/src/main.ts +++ b/apps/server/src/main.ts @@ -10,6 +10,7 @@ import { TransformHttpResponseInterceptor } from './common/interceptors/http-res import { WsRedisIoAdapter } from './ws/adapter/ws-redis.adapter'; import fastifyMultipart from '@fastify/multipart'; import fastifyCookie from '@fastify/cookie'; +import fastifyIp from 'fastify-ip'; import { InternalLogFilter } from './common/logger/internal-log-filter'; async function bootstrap() { @@ -45,6 +46,7 @@ async function bootstrap() { app.useWebSocketAdapter(redisIoAdapter); + await app.register(fastifyIp); await app.register(fastifyMultipart); await app.register(fastifyCookie); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0389d23..6599b07a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -493,6 +493,9 @@ importers: '@modelcontextprotocol/sdk': specifier: ^1.27.1 version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) + '@nest-lab/throttler-storage-redis': + specifier: ^1.2.0 + version: 1.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2) '@nestjs-labs/nestjs-ioredis': specifier: ^11.0.4 version: 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1) @@ -535,6 +538,9 @@ importers: '@nestjs/terminus': specifier: ^11.1.1 version: 11.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/throttler': + specifier: ^6.5.0 + version: 6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2) '@nestjs/websockets': specifier: ^11.1.17 version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -580,6 +586,9 @@ importers: cookie: specifier: ^1.1.1 version: 1.1.1 + fastify-ip: + specifier: ^2.0.0 + version: 2.0.0 fs-extra: specifier: ^11.3.4 version: 11.3.4 @@ -2925,6 +2934,15 @@ packages: '@napi-rs/wasm-runtime@1.1.1': resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + '@nest-lab/throttler-storage-redis@1.2.0': + resolution: {integrity: sha512-tMkUyo68NCKTR+zILk+EC35SMYBtDPZY2mCj7ZaCietWGVTnuP4zwq9ERYfvU6kJv6h8teNZrC6MJCmY6/dljw==} + peerDependencies: + '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/throttler': '>=6.0.0' + ioredis: '>=5.0.0' + reflect-metadata: ^0.2.1 + '@nestjs-labs/nestjs-ioredis@11.0.4': resolution: {integrity: sha512-4jPNOrxDiwNMIN5OLmsMWhA782kxv/ZBxkySX9l8n6sr55acHX/BciaFsOXVa/ILsm+Y7893y98/6WNhmEoiNQ==} engines: {node: '>=16'} @@ -3127,6 +3145,13 @@ packages: '@nestjs/platform-express': optional: true + '@nestjs/throttler@6.5.0': + resolution: {integrity: sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==} + peerDependencies: + '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + reflect-metadata: ^0.1.13 || ^0.2.0 + '@nestjs/websockets@11.1.17': resolution: {integrity: sha512-YbwQ0QfVj0lxkKQhdIIgk14ZSVWDqGk1J8nNSN6SLjf36sVv58Ma5ro+dtQua8wj3l2Ub7JJCVFixEhKtYc/rQ==} peerDependencies: @@ -7016,6 +7041,10 @@ packages: resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} hasBin: true + fastify-ip@2.0.0: + resolution: {integrity: sha512-7mQyAc7sapawpiriEFoJyQIs41nNIO42UCzgMKrjNGsIegnevj2VhOlXLLTa+q7cxXfJ5fDGmOAdQpaIgA9ObA==} + engines: {node: '>=20.x'} + fastify-plugin@5.0.1: resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==} @@ -13463,6 +13492,15 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2)': + dependencies: + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/throttler': 6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2) + ioredis: 5.10.1 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + '@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1)': dependencies: '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -13643,6 +13681,12 @@ snapshots: '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 + '@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)': + dependencies: + '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + reflect-metadata: 0.2.2 + '@nestjs/websockets@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -18072,6 +18116,10 @@ snapshots: path-expression-matcher: 1.2.0 strnum: 2.2.1 + fastify-ip@2.0.0: + dependencies: + fastify-plugin: 5.1.0 + fastify-plugin@5.0.1: {} fastify-plugin@5.1.0: {} From 879aa2c3d820dcb532b793c98476c9b4bef6d167 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:03:59 +0100 Subject: [PATCH 33/44] feat: page update notifications (#2074) * feat: watchers notification and email preferences * fix: email copy * digests * clean up * fix * clean up * move backlinks queue-up to history processor * fix * fix keys * feat: group notifications * filter * adjust email digest window --- .../public/locales/en-US/translation.json | 18 ++ .../components/mention/mention-list.tsx | 1 + .../components/slash-menu/command-list.tsx | 13 +- .../components/slash-menu/render-items.ts | 2 +- .../components/notification-item.tsx | 3 + .../components/notification-list.tsx | 10 +- .../components/notification-popover.tsx | 22 +- .../notification/notification.module.css | 1 + .../queries/notification-query.ts | 6 +- .../services/notification-service.ts | 1 + .../notification/types/notification.types.ts | 5 +- .../components/header/page-header-menu.tsx | 27 ++ .../features/page/queries/watcher-query.ts | 43 +++ .../features/page/services/watcher-service.ts | 16 ++ .../user/components/notification-pref.tsx | 117 ++++++++ .../src/features/user/types/user.types.ts | 12 + .../settings/account/account-preferences.tsx | 5 + .../extensions/persistence.extension.ts | 10 - .../processors/history.processor.ts | 53 +++- .../core/notification/dto/notification.dto.ts | 10 +- .../notification/notification.constants.ts | 38 +++ .../notification/notification.controller.ts | 7 +- .../core/notification/notification.module.ts | 2 + .../notification/notification.processor.ts | 16 ++ .../core/notification/notification.service.ts | 56 +++- .../services/comment.notification.ts | 6 + .../page-update-email-rate-limiter.ts | 43 +++ .../services/page.notification.ts | 256 +++++++++++++++++- .../src/core/user/dto/update-user.dto.ts | 20 ++ apps/server/src/core/user/user.service.ts | 19 ++ .../src/core/watcher/watcher.controller.ts | 30 +- .../server/src/core/watcher/watcher.module.ts | 7 +- .../repos/notification/notification.repo.ts | 38 ++- .../src/database/repos/user/user.repo.ts | 19 ++ .../queue/constants/queue.constants.ts | 1 + .../queue/constants/queue.interface.ts | 7 + .../emails/page-update-digest-email.tsx | 76 ++++++ .../emails/page-update-email.tsx | 36 +++ .../transactional/partials/partials.tsx | 4 + 39 files changed, 983 insertions(+), 73 deletions(-) create mode 100644 apps/client/src/features/page/queries/watcher-query.ts create mode 100644 apps/client/src/features/page/services/watcher-service.ts create mode 100644 apps/client/src/features/user/components/notification-pref.tsx create mode 100644 apps/server/src/core/notification/services/page-update-email-rate-limiter.ts create mode 100644 apps/server/src/integrations/transactional/emails/page-update-digest-email.tsx create mode 100644 apps/server/src/integrations/transactional/emails/page-update-email.tsx diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 19149612..b1c1ed6c 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -674,6 +674,24 @@ "<bold>{{name}}</bold> mentioned you on a page": "<bold>{{name}}</bold> mentioned you on a page.", "<bold>{{name}}</bold> gave you edit access to a page": "<bold>{{name}}</bold> gave you edit access to a page.", "<bold>{{name}}</bold> gave you view access to a page": "<bold>{{name}}</bold> gave you view access to a page.", + "<bold>{{name}}</bold> updated a page": "<bold>{{name}}</bold> updated a page.", + "Watch page": "Watch page", + "Stop watching": "Stop watching", + "Email notifications": "Email notifications", + "Page updates": "Page updates", + "Get notified when pages you watch are updated.": "Get notified when pages you watch are updated.", + "Page mentions": "Page mentions", + "Get notified when someone mentions you on a page.": "Get notified when someone mentions you on a page.", + "Comment mentions": "Comment mentions", + "Get notified when someone mentions you in a comment.": "Get notified when someone mentions you in a comment.", + "New comments": "New comments", + "Get notified about new comments on threads you participate in.": "Get notified about new comments on threads you participate in.", + "Resolved comments": "Resolved comments", + "Get notified when your comment is resolved.": "Get notified when your comment is resolved.", + "You are now watching this page": "You are now watching this page", + "You are no longer watching this page": "You are no longer watching this page", + "Direct": "Direct", + "Updates": "Updates", "Today": "Today", "Yesterday": "Yesterday", "This week": "This week", diff --git a/apps/client/src/features/editor/components/mention/mention-list.tsx b/apps/client/src/features/editor/components/mention/mention-list.tsx index f086df49..af6f8a1d 100644 --- a/apps/client/src/features/editor/components/mention/mention-list.tsx +++ b/apps/client/src/features/editor/components/mention/mention-list.tsx @@ -294,6 +294,7 @@ const MentionList = forwardRef<any, MentionListProps>((props, ref) => { w={popupWidth} scrollbars={"y"} scrollbarSize={6} + overscrollBehavior={"contain"} styles={{ content: { minWidth: 0 } }} > {renderItems?.map((item, index) => { diff --git a/apps/client/src/features/editor/components/slash-menu/command-list.tsx b/apps/client/src/features/editor/components/slash-menu/command-list.tsx index ab1dcafd..54d6cd17 100644 --- a/apps/client/src/features/editor/components/slash-menu/command-list.tsx +++ b/apps/client/src/features/editor/components/slash-menu/command-list.tsx @@ -87,7 +87,13 @@ const CommandList = ({ return flatItems.length > 0 ? ( <Paper id="slash-command" shadow="md" p="xs" withBorder> - <ScrollArea viewportRef={viewportRef} h={350} w={270} scrollbarSize={8}> + <ScrollArea + viewportRef={viewportRef} + h={350} + w={270} + scrollbarSize={8} + overscrollBehavior="contain" + > {Object.entries(items).map(([category, categoryItems]) => ( <div key={category}> <Text c="dimmed" mb={4} fw={500} tt="capitalize"> @@ -103,10 +109,7 @@ const CommandList = ({ })} > <Group> - <ActionIcon - variant="default" - component="div" - > + <ActionIcon variant="default" component="div"> <item.icon size={18} /> </ActionIcon> diff --git a/apps/client/src/features/editor/components/slash-menu/render-items.ts b/apps/client/src/features/editor/components/slash-menu/render-items.ts index 057e8214..041aa036 100644 --- a/apps/client/src/features/editor/components/slash-menu/render-items.ts +++ b/apps/client/src/features/editor/components/slash-menu/render-items.ts @@ -49,7 +49,7 @@ const renderItems = () => { getReferenceClientRect = props.clientRect; popup = document.createElement("div"); - popup.style.zIndex = "9999"; + popup.style.zIndex = "199"; popup.style.position = "absolute"; popup.style.top = "0"; popup.style.left = "0"; diff --git a/apps/client/src/features/notification/components/notification-item.tsx b/apps/client/src/features/notification/components/notification-item.tsx index 0ef81e44..0fd4f44b 100644 --- a/apps/client/src/features/notification/components/notification-item.tsx +++ b/apps/client/src/features/notification/components/notification-item.tsx @@ -49,6 +49,8 @@ export function NotificationItem({ return notification.data?.role === "writer" ? "<bold>{{name}}</bold> gave you edit access to a page" : "<bold>{{name}}</bold> gave you view access to a page"; + case "page.updated": + return "<bold>{{name}}</bold> updated a page"; default: return ""; } @@ -75,6 +77,7 @@ export function NotificationItem({ }; const handleMarkRead = (e: React.MouseEvent) => { + e.preventDefault(); e.stopPropagation(); markReadIfNeeded(); }; diff --git a/apps/client/src/features/notification/components/notification-list.tsx b/apps/client/src/features/notification/components/notification-list.tsx index 4c992c57..4cd30677 100644 --- a/apps/client/src/features/notification/components/notification-list.tsx +++ b/apps/client/src/features/notification/components/notification-list.tsx @@ -3,17 +3,23 @@ import { IconBellOff } from "@tabler/icons-react"; import { useTranslation } from "react-i18next"; import { useEffect, useRef } from "react"; import { NotificationItem } from "./notification-item"; -import { INotification, NotificationFilter } from "../types/notification.types"; +import { + INotification, + NotificationFilter, + NotificationTab, +} from "../types/notification.types"; import { groupNotificationsByTime } from "../notification.utils"; import { useNotificationsQuery } from "../queries/notification-query"; import classes from "../notification.module.css"; type NotificationListProps = { + tab: NotificationTab; filter: NotificationFilter; onNavigate: () => void; }; export function NotificationList({ + tab, filter, onNavigate, }: NotificationListProps) { @@ -24,7 +30,7 @@ export function NotificationList({ hasNextPage, fetchNextPage, isFetchingNextPage, - } = useNotificationsQuery(); + } = useNotificationsQuery(tab as string); const sentinelRef = useRef<HTMLDivElement>(null); diff --git a/apps/client/src/features/notification/components/notification-popover.tsx b/apps/client/src/features/notification/components/notification-popover.tsx index 8ebfedad..161ac1e6 100644 --- a/apps/client/src/features/notification/components/notification-popover.tsx +++ b/apps/client/src/features/notification/components/notification-popover.tsx @@ -6,6 +6,7 @@ import { Menu, Popover, ScrollArea, + Tabs, Text, Tooltip, } from "@mantine/core"; @@ -18,15 +19,20 @@ import { } from "@tabler/icons-react"; import { useTranslation } from "react-i18next"; import { NotificationList } from "./notification-list"; -import { NotificationFilter } from "../types/notification.types"; +import { + NotificationFilter, + NotificationTab, +} from "../types/notification.types"; import { useMarkAllReadMutation, useUnreadCountQuery, } from "../queries/notification-query"; +import classes from "../notification.module.css"; export function NotificationPopover() { const { t } = useTranslation(); const [opened, setOpened] = useState(false); + const [tab, setTab] = useState<NotificationTab>("direct"); const [filter, setFilter] = useState<NotificationFilter>("all"); const { data: unreadData } = useUnreadCountQuery(); @@ -125,13 +131,27 @@ export function NotificationPopover() { </Group> </Group> + <Tabs + value={tab} + onChange={(value) => setTab(value as NotificationTab)} + variant="default" + color="dark" + > + <Tabs.List px="md"> + <Tabs.Tab value="direct">{t("Direct")}</Tabs.Tab> + <Tabs.Tab value="updates">{t("Updates")}</Tabs.Tab> + </Tabs.List> + </Tabs> + <ScrollArea.Autosize mah={500} type="auto" offsetScrollbars scrollbarSize={6} + style={{ overscrollBehavior: "contain" }} > <NotificationList + tab={tab} filter={filter} onNavigate={() => setOpened(false)} /> diff --git a/apps/client/src/features/notification/notification.module.css b/apps/client/src/features/notification/notification.module.css index d56986ac..09802628 100644 --- a/apps/client/src/features/notification/notification.module.css +++ b/apps/client/src/features/notification/notification.module.css @@ -13,3 +13,4 @@ .divider { border-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); } + diff --git a/apps/client/src/features/notification/queries/notification-query.ts b/apps/client/src/features/notification/queries/notification-query.ts index 363482b1..92c46560 100644 --- a/apps/client/src/features/notification/queries/notification-query.ts +++ b/apps/client/src/features/notification/queries/notification-query.ts @@ -15,10 +15,10 @@ import { export const NOTIFICATION_KEY = ["notifications"]; export const UNREAD_COUNT_KEY = ["notifications", "unread-count"]; -export function useNotificationsQuery() { +export function useNotificationsQuery(type?: string) { return useInfiniteQuery({ - queryKey: NOTIFICATION_KEY, - queryFn: ({ pageParam }) => getNotifications({ cursor: pageParam }), + queryKey: [...NOTIFICATION_KEY, type], + queryFn: ({ pageParam }) => getNotifications({ cursor: pageParam, type }), initialPageParam: undefined as string | undefined, getNextPageParam: (lastPage) => lastPage.meta.hasNextPage ? lastPage.meta.nextCursor : undefined, diff --git a/apps/client/src/features/notification/services/notification-service.ts b/apps/client/src/features/notification/services/notification-service.ts index 8adf4909..7e4b8d2c 100644 --- a/apps/client/src/features/notification/services/notification-service.ts +++ b/apps/client/src/features/notification/services/notification-service.ts @@ -5,6 +5,7 @@ import { IPagination } from "@/lib/types"; export async function getNotifications(params: { limit?: number; cursor?: string; + type?: string; }): Promise<IPagination<INotification>> { const req = await api.post<IPagination<INotification>>( "/notifications", diff --git a/apps/client/src/features/notification/types/notification.types.ts b/apps/client/src/features/notification/types/notification.types.ts index 811805d0..f64e3648 100644 --- a/apps/client/src/features/notification/types/notification.types.ts +++ b/apps/client/src/features/notification/types/notification.types.ts @@ -3,7 +3,8 @@ export type NotificationType = | "comment.created" | "comment.resolved" | "page.user_mention" - | "page.permission_granted"; + | "page.permission_granted" + | "page.updated"; export type INotification = { id: string; @@ -38,3 +39,5 @@ export type INotification = { }; export type NotificationFilter = "all" | "unread"; + +export type NotificationTab = "direct" | "updates" | "all"; diff --git a/apps/client/src/features/page/components/header/page-header-menu.tsx b/apps/client/src/features/page/components/header/page-header-menu.tsx index 2660b2ba..5ba9d40e 100644 --- a/apps/client/src/features/page/components/header/page-header-menu.tsx +++ b/apps/client/src/features/page/components/header/page-header-menu.tsx @@ -3,6 +3,8 @@ import { IconArrowRight, IconArrowsHorizontal, IconDots, + IconEye, + IconEyeOff, IconFileExport, IconHistory, IconLink, @@ -40,6 +42,11 @@ import { PageStateSegmentedControl } from "@/features/user/components/page-state import MovePageModal from "@/features/page/components/move-page-modal.tsx"; import { useTimeAgo } from "@/hooks/use-time-ago.tsx"; import { PageShareModal } from "@/ee/page-permission"; +import { + useWatchStatusQuery, + useWatchPageMutation, + useUnwatchPageMutation, +} from "@/features/page/queries/watcher-query"; interface PageHeaderMenuProps { readOnly?: boolean; @@ -123,6 +130,9 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) { ] = useDisclosure(false); const [pageEditor] = useAtom(pageEditorAtom); const pageUpdatedAt = useTimeAgo(page?.updatedAt); + const { data: watchStatus } = useWatchStatusQuery(page?.id); + const watchPage = useWatchPageMutation(); + const unwatchPage = useUnwatchPageMutation(); const handleCopyLink = () => { const pageUrl = @@ -185,6 +195,23 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) { > {t("Copy as Markdown")} </Menu.Item> + + {watchStatus?.watching ? ( + <Menu.Item + leftSection={<IconEyeOff size={16} />} + onClick={() => unwatchPage.mutate(page.id)} + > + {t("Stop watching")} + </Menu.Item> + ) : ( + <Menu.Item + leftSection={<IconEye size={16} />} + onClick={() => watchPage.mutate(page.id)} + > + {t("Watch page")} + </Menu.Item> + )} + <Menu.Divider /> <Menu.Item leftSection={<IconArrowsHorizontal size={16} />}> diff --git a/apps/client/src/features/page/queries/watcher-query.ts b/apps/client/src/features/page/queries/watcher-query.ts new file mode 100644 index 00000000..0c9eba0f --- /dev/null +++ b/apps/client/src/features/page/queries/watcher-query.ts @@ -0,0 +1,43 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + watchPage, + unwatchPage, + getWatchStatus, +} from "@/features/page/services/watcher-service"; +import { notifications } from "@mantine/notifications"; +import { useTranslation } from "react-i18next"; + +const WATCHER_KEY = "watcher"; + +export function useWatchStatusQuery(pageId: string) { + return useQuery({ + queryKey: [WATCHER_KEY, pageId], + queryFn: () => getWatchStatus(pageId), + enabled: !!pageId, + staleTime: 60_000, + }); +} + +export function useWatchPageMutation() { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + return useMutation({ + mutationFn: (pageId: string) => watchPage(pageId), + onSuccess: (_data, pageId) => { + queryClient.setQueryData([WATCHER_KEY, pageId], { watching: true }); + notifications.show({ message: t("You are now watching this page") }); + }, + }); +} + +export function useUnwatchPageMutation() { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + return useMutation({ + mutationFn: (pageId: string) => unwatchPage(pageId), + onSuccess: (_data, pageId) => { + queryClient.setQueryData([WATCHER_KEY, pageId], { watching: false }); + notifications.show({ message: t("You are no longer watching this page") }); + }, + }); +} diff --git a/apps/client/src/features/page/services/watcher-service.ts b/apps/client/src/features/page/services/watcher-service.ts new file mode 100644 index 00000000..d0c1416b --- /dev/null +++ b/apps/client/src/features/page/services/watcher-service.ts @@ -0,0 +1,16 @@ +import api from "@/lib/api-client"; + +export async function watchPage(pageId: string): Promise<{ watching: boolean }> { + const req = await api.post<{ watching: boolean }>("/pages/watch", { pageId }); + return req.data; +} + +export async function unwatchPage(pageId: string): Promise<{ watching: boolean }> { + const req = await api.post<{ watching: boolean }>("/pages/unwatch", { pageId }); + return req.data; +} + +export async function getWatchStatus(pageId: string): Promise<{ watching: boolean }> { + const req = await api.post<{ watching: boolean }>("/pages/watch-status", { pageId }); + return req.data; +} diff --git a/apps/client/src/features/user/components/notification-pref.tsx b/apps/client/src/features/user/components/notification-pref.tsx new file mode 100644 index 00000000..e8a983ed --- /dev/null +++ b/apps/client/src/features/user/components/notification-pref.tsx @@ -0,0 +1,117 @@ +import { userAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { updateUser } from "@/features/user/services/user-service.ts"; +import { IUser, IUserSettings } from "@/features/user/types/user.types.ts"; +import { Switch, Text, Title, Stack } from "@mantine/core"; +import { useAtom } from "jotai"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + ResponsiveSettingsRow, + ResponsiveSettingsContent, + ResponsiveSettingsControl, +} from "@/components/ui/responsive-settings-row"; + +type NotificationKey = keyof NonNullable<IUserSettings["notifications"]>; + +const notificationItems: { + key: NotificationKey; + dtoField: keyof IUser; + label: string; + description: string; +}[] = [ + { + key: "page.updated", + dtoField: "notificationPageUpdates", + label: "Page updates", + description: "Get notified when pages you watch are updated.", + }, + { + key: "page.userMention", + dtoField: "notificationPageUserMention", + label: "Page mentions", + description: "Get notified when someone mentions you on a page.", + }, + { + key: "comment.userMention", + dtoField: "notificationCommentUserMention", + label: "Comment mentions", + description: "Get notified when someone mentions you in a comment.", + }, + { + key: "comment.created", + dtoField: "notificationCommentCreated", + label: "New comments", + description: + "Get notified about new comments on threads you participate in.", + }, + { + key: "comment.resolved", + dtoField: "notificationCommentResolved", + label: "Resolved comments", + description: "Get notified when your comment is resolved.", + }, +]; + +function NotificationToggle({ + settingKey, + dtoField, + label, + description, +}: { + settingKey: NotificationKey; + dtoField: keyof IUser; + label: string; + description: string; +}) { + const { t } = useTranslation(); + const [user, setUser] = useAtom(userAtom); + const [checked, setChecked] = useState( + user.settings?.notifications?.[settingKey] !== false, + ); + + const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => { + const value = event.currentTarget.checked; + setChecked(value); + try { + const updatedUser = await updateUser({ [dtoField]: value } as any); + setUser(updatedUser); + } catch { + setChecked(!value); + } + }; + + return ( + <ResponsiveSettingsRow> + <ResponsiveSettingsContent> + <Text size="md">{t(label)}</Text> + <Text size="sm" c="dimmed"> + {t(description)} + </Text> + </ResponsiveSettingsContent> + + <ResponsiveSettingsControl> + <Switch checked={checked} onChange={handleChange} /> + </ResponsiveSettingsControl> + </ResponsiveSettingsRow> + ); +} + +export default function NotificationPref() { + const { t } = useTranslation(); + + return ( + <Stack gap="xs"> + <Title order={5}>{t("Email notifications")} + + {notificationItems.map((item) => ( + + ))} + + ); +} diff --git a/apps/client/src/features/user/types/user.types.ts b/apps/client/src/features/user/types/user.types.ts index 80d86706..75d45bfd 100644 --- a/apps/client/src/features/user/types/user.types.ts +++ b/apps/client/src/features/user/types/user.types.ts @@ -20,6 +20,11 @@ export interface IUser { deletedAt: Date; fullPageWidth: boolean; // used for update pageEditMode: string; // used for update + notificationPageUpdates: boolean; // used for update + notificationPageUserMention: boolean; // used for update + notificationCommentUserMention: boolean; // used for update + notificationCommentCreated: boolean; // used for update + notificationCommentResolved: boolean; // used for update hasGeneratedPassword?: boolean; } @@ -33,6 +38,13 @@ export interface IUserSettings { fullPageWidth: boolean; pageEditMode: string; }; + notifications?: { + "page.updated"?: boolean; + "page.userMention"?: boolean; + "comment.userMention"?: boolean; + "comment.created"?: boolean; + "comment.resolved"?: boolean; + }; } export enum PageEditMode { diff --git a/apps/client/src/pages/settings/account/account-preferences.tsx b/apps/client/src/pages/settings/account/account-preferences.tsx index f082ea1b..caedc1b0 100644 --- a/apps/client/src/pages/settings/account/account-preferences.tsx +++ b/apps/client/src/pages/settings/account/account-preferences.tsx @@ -3,6 +3,7 @@ import AccountLanguage from "@/features/user/components/account-language.tsx"; import AccountTheme from "@/features/user/components/account-theme.tsx"; import PageWidthPref from "@/features/user/components/page-width-pref.tsx"; import PageEditPref from "@/features/user/components/page-state-pref"; +import NotificationPref from "@/features/user/components/notification-pref"; import { getAppName } from "@/lib/config.ts"; import { Divider } from "@mantine/core"; import { Helmet } from "react-helmet-async"; @@ -33,6 +34,10 @@ export default function AccountPreferences() { + + + + ); } diff --git a/apps/server/src/collaboration/extensions/persistence.extension.ts b/apps/server/src/collaboration/extensions/persistence.extension.ts index 642d0761..d32e4778 100644 --- a/apps/server/src/collaboration/extensions/persistence.extension.ts +++ b/apps/server/src/collaboration/extensions/persistence.extension.ts @@ -18,12 +18,10 @@ import { QueueJob, QueueName } from '../../integrations/queue/constants'; import { Queue } from 'bullmq'; import { extractMentions, - extractPageMentions, extractUserMentions, } from '../../common/helpers/prosemirror/utils'; import { isDeepStrictEqual } from 'node:util'; import { - IPageBacklinkJob, IPageHistoryJob, IPageMentionNotificationJob, } from '../../integrations/queue/constants/queue.interface'; @@ -43,7 +41,6 @@ export class PersistenceExtension implements Extension { constructor( private readonly pageRepo: PageRepo, @InjectKysely() private readonly db: KyselyDB, - @InjectQueue(QueueName.GENERAL_QUEUE) private generalQueue: Queue, @InjectQueue(QueueName.AI_QUEUE) private aiQueue: Queue, @InjectQueue(QueueName.HISTORY_QUEUE) private historyQueue: Queue, @InjectQueue(QueueName.NOTIFICATION_QUEUE) private notificationQueue: Queue, @@ -165,13 +162,6 @@ export class PersistenceExtension implements Extension { await this.collabHistory.addContributors(pageId, editingUserIds); const mentions = extractMentions(tiptapJson); - const pageMentions = extractPageMentions(mentions); - - await this.generalQueue.add(QueueJob.PAGE_BACKLINKS, { - pageId: pageId, - workspaceId: page.workspaceId, - mentions: pageMentions, - } as IPageBacklinkJob); const userMentions = extractUserMentions(mentions); const oldMentions = page.content ? extractMentions(page.content) : []; diff --git a/apps/server/src/collaboration/processors/history.processor.ts b/apps/server/src/collaboration/processors/history.processor.ts index 315dba0b..d7e27f60 100644 --- a/apps/server/src/collaboration/processors/history.processor.ts +++ b/apps/server/src/collaboration/processors/history.processor.ts @@ -1,8 +1,17 @@ import { Logger, OnModuleDestroy } from '@nestjs/common'; import { OnWorkerEvent, Processor, WorkerHost } from '@nestjs/bullmq'; -import { Job } from 'bullmq'; +import { InjectQueue } from '@nestjs/bullmq'; +import { Job, Queue } from 'bullmq'; import { QueueJob, QueueName } from '../../integrations/queue/constants'; -import { IPageHistoryJob } from '../../integrations/queue/constants/queue.interface'; +import { + IPageBacklinkJob, + IPageHistoryJob, + IPageUpdateNotificationJob, +} from '../../integrations/queue/constants/queue.interface'; +import { + extractMentions, + extractPageMentions, +} from '../../common/helpers/prosemirror/utils'; import { PageHistoryRepo } from '@docmost/db/repos/page/page-history.repo'; import { PageRepo } from '@docmost/db/repos/page/page.repo'; import { isDeepStrictEqual } from 'node:util'; @@ -18,6 +27,8 @@ export class HistoryProcessor extends WorkerHost implements OnModuleDestroy { private readonly pageRepo: PageRepo, private readonly collabHistory: CollabHistoryService, private readonly watcherService: WatcherService, + @InjectQueue(QueueName.NOTIFICATION_QUEUE) private notificationQueue: Queue, + @InjectQueue(QueueName.GENERAL_QUEUE) private generalQueue: Queue, ) { super(); } @@ -47,8 +58,7 @@ export class HistoryProcessor extends WorkerHost implements OnModuleDestroy { !lastHistory || !isDeepStrictEqual(lastHistory.content, page.content) ) { - const contributorIds = - await this.collabHistory.popContributors(pageId); + const contributorIds = await this.collabHistory.popContributors(pageId); try { await this.watcherService.addPageWatchers( @@ -61,12 +71,39 @@ export class HistoryProcessor extends WorkerHost implements OnModuleDestroy { await this.pageHistoryRepo.saveHistory(page, { contributorIds }); this.logger.debug(`History created for page: ${pageId}`); } catch (err) { - await this.collabHistory.addContributors( - pageId, - contributorIds, - ); + await this.collabHistory.addContributors(pageId, contributorIds); throw err; } + + const mentions = extractMentions(page.content); + const pageMentions = extractPageMentions(mentions); + + await this.generalQueue + .add(QueueJob.PAGE_BACKLINKS, { + pageId, + workspaceId: page.workspaceId, + mentions: pageMentions, + } as IPageBacklinkJob) + .catch((err) => { + this.logger.error( + `Failed to queue backlinks for ${pageId}: ${err.message}`, + ); + }); + + if (contributorIds.length > 0 && lastHistory?.content) { + await this.notificationQueue + .add(QueueJob.PAGE_UPDATED, { + pageId, + spaceId: page.spaceId, + workspaceId: page.workspaceId, + actorIds: contributorIds, + } as IPageUpdateNotificationJob) + .catch((err) => { + this.logger.error( + `Failed to queue page update notification for ${pageId}: ${err.message}`, + ); + }); + } } } catch (err) { throw err; diff --git a/apps/server/src/core/notification/dto/notification.dto.ts b/apps/server/src/core/notification/dto/notification.dto.ts index 0b0bde94..b583c746 100644 --- a/apps/server/src/core/notification/dto/notification.dto.ts +++ b/apps/server/src/core/notification/dto/notification.dto.ts @@ -1,4 +1,5 @@ -import { IsArray, IsOptional, IsUUID } from 'class-validator'; +import { IsArray, IsIn, IsOptional, IsString, IsUUID } from 'class-validator'; +import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; export class NotificationIdDto { @IsUUID() @@ -11,3 +12,10 @@ export class MarkNotificationsReadDto { @IsOptional() notificationIds?: string[]; } + +export class ListNotificationsDto extends PaginationOptions { + @IsOptional() + @IsString() + @IsIn(['direct', 'updates', 'all']) + type?: 'direct' | 'updates' | 'all' = 'all'; +} diff --git a/apps/server/src/core/notification/notification.constants.ts b/apps/server/src/core/notification/notification.constants.ts index 56d2ecad..8f7f5049 100644 --- a/apps/server/src/core/notification/notification.constants.ts +++ b/apps/server/src/core/notification/notification.constants.ts @@ -4,7 +4,45 @@ export const NotificationType = { COMMENT_RESOLVED: 'comment.resolved', PAGE_USER_MENTION: 'page.user_mention', PAGE_PERMISSION_GRANTED: 'page.permission_granted', + PAGE_UPDATED: 'page.updated', } as const; export type NotificationType = (typeof NotificationType)[keyof typeof NotificationType]; + +export type NotificationSettingKey = + | 'page.updated' + | 'page.userMention' + | 'comment.userMention' + | 'comment.created' + | 'comment.resolved'; + +export const NotificationTypeToSettingKey: Partial< + Record +> = { + [NotificationType.PAGE_UPDATED]: 'page.updated', + [NotificationType.PAGE_USER_MENTION]: 'page.userMention', + [NotificationType.COMMENT_USER_MENTION]: 'comment.userMention', + [NotificationType.COMMENT_CREATED]: 'comment.created', + [NotificationType.COMMENT_RESOLVED]: 'comment.resolved', +}; + +export type NotificationTab = 'direct' | 'updates' | 'all'; + +export const DIRECT_NOTIFICATION_TYPES: NotificationType[] = [ + NotificationType.COMMENT_USER_MENTION, + NotificationType.COMMENT_CREATED, + NotificationType.COMMENT_RESOLVED, + NotificationType.PAGE_USER_MENTION, + NotificationType.PAGE_PERMISSION_GRANTED, +]; + +export const UPDATES_NOTIFICATION_TYPES: NotificationType[] = [ + NotificationType.PAGE_UPDATED, +]; + +export function getTypesForTab(tab: NotificationTab): NotificationType[] | undefined { + if (tab === 'direct') return DIRECT_NOTIFICATION_TYPES; + if (tab === 'updates') return UPDATES_NOTIFICATION_TYPES; + return undefined; +} diff --git a/apps/server/src/core/notification/notification.controller.ts b/apps/server/src/core/notification/notification.controller.ts index d041414f..be5ee1d3 100644 --- a/apps/server/src/core/notification/notification.controller.ts +++ b/apps/server/src/core/notification/notification.controller.ts @@ -9,9 +9,8 @@ import { import { NotificationService } from './notification.service'; import { AuthUser } from '../../common/decorators/auth-user.decorator'; import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; -import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; import { User } from '@docmost/db/types/entity.types'; -import { MarkNotificationsReadDto } from './dto/notification.dto'; +import { ListNotificationsDto, MarkNotificationsReadDto } from './dto/notification.dto'; @UseGuards(JwtAuthGuard) @Controller('notifications') @@ -21,10 +20,10 @@ export class NotificationController { @HttpCode(HttpStatus.OK) @Post('/') async getNotifications( - @Body() pagination: PaginationOptions, + @Body() dto: ListNotificationsDto, @AuthUser() user: User, ) { - return this.notificationService.findByUserId(user.id, pagination); + return this.notificationService.findByUserId(user.id, dto, dto.type); } @HttpCode(HttpStatus.OK) diff --git a/apps/server/src/core/notification/notification.module.ts b/apps/server/src/core/notification/notification.module.ts index a142eaf8..83778294 100644 --- a/apps/server/src/core/notification/notification.module.ts +++ b/apps/server/src/core/notification/notification.module.ts @@ -4,6 +4,7 @@ import { NotificationController } from './notification.controller'; import { NotificationProcessor } from './notification.processor'; import { CommentNotificationService } from './services/comment.notification'; import { PageNotificationService } from './services/page.notification'; +import { PageUpdateEmailRateLimiter } from './services/page-update-email-rate-limiter'; @Module({ imports: [], @@ -13,6 +14,7 @@ import { PageNotificationService } from './services/page.notification'; NotificationProcessor, CommentNotificationService, PageNotificationService, + PageUpdateEmailRateLimiter, ], exports: [NotificationService], }) diff --git a/apps/server/src/core/notification/notification.processor.ts b/apps/server/src/core/notification/notification.processor.ts index f7c8b577..e3d3a883 100644 --- a/apps/server/src/core/notification/notification.processor.ts +++ b/apps/server/src/core/notification/notification.processor.ts @@ -8,6 +8,7 @@ import { ICommentNotificationJob, ICommentResolvedNotificationJob, IPageMentionNotificationJob, + IPageUpdateNotificationJob, IPermissionGrantedNotificationJob, } from '../../integrations/queue/constants/queue.interface'; import { CommentNotificationService } from './services/comment.notification'; @@ -35,6 +36,7 @@ export class NotificationProcessor | ICommentNotificationJob | ICommentResolvedNotificationJob | IPageMentionNotificationJob + | IPageUpdateNotificationJob | IPermissionGrantedNotificationJob, void >, @@ -76,6 +78,20 @@ export class NotificationProcessor break; } + case QueueJob.PAGE_UPDATED: { + await this.pageNotificationService.processPageUpdate( + job.data as IPageUpdateNotificationJob, + appUrl, + ); + break; + } + + case QueueJob.PAGE_UPDATE_DIGEST: { + const { userId } = job.data as unknown as { userId: string }; + await this.pageNotificationService.processDigest(userId, appUrl); + break; + } + default: this.logger.warn(`Unknown notification job: ${job.name}`); } diff --git a/apps/server/src/core/notification/notification.service.ts b/apps/server/src/core/notification/notification.service.ts index 493b673e..1f88bf59 100644 --- a/apps/server/src/core/notification/notification.service.ts +++ b/apps/server/src/core/notification/notification.service.ts @@ -6,6 +6,8 @@ import { InsertableNotification } from '@docmost/db/types/entity.types'; import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; import { WsGateway } from '../../ws/ws.gateway'; import { MailService } from '../../integrations/mail/mail.service'; +import { NotificationTab, NotificationType, NotificationTypeToSettingKey } from './notification.constants'; +import { PagePermissionRepo } from '@docmost/db/repos/page/page-permission.repo'; @Injectable() export class NotificationService { @@ -13,12 +15,23 @@ export class NotificationService { constructor( private readonly notificationRepo: NotificationRepo, + private readonly pagePermissionRepo: PagePermissionRepo, private readonly wsGateway: WsGateway, private readonly mailService: MailService, @InjectKysely() private readonly db: KyselyDB, ) {} async create(data: InsertableNotification) { + const user = await this.db + .selectFrom('users') + .select(['id']) + .where('id', '=', data.userId) + .where('deletedAt', 'is', null) + .where('deactivatedAt', 'is', null) + .executeTakeFirst(); + + if (!user) return null; + const notification = await this.notificationRepo.insert(data); this.wsGateway.server @@ -28,8 +41,35 @@ export class NotificationService { return notification; } - async findByUserId(userId: string, pagination: PaginationOptions) { - return this.notificationRepo.findByUserId(userId, pagination); + async findByUserId( + userId: string, + pagination: PaginationOptions, + type: NotificationTab = 'all', + ) { + const result = await this.notificationRepo.findByUserId( + userId, + pagination, + type, + ); + + const pageIds = result.items + .map((n: any) => n.pageId) + .filter(Boolean); + + if (pageIds.length > 0) { + const accessiblePageIds = + await this.pagePermissionRepo.filterAccessiblePageIds({ + pageIds, + userId, + }); + const accessibleSet = new Set(accessiblePageIds); + + result.items = result.items.filter( + (n: any) => !n.pageId || accessibleSet.has(n.pageId), + ); + } + + return result; } async getUnreadCount(userId: string) { @@ -53,17 +93,27 @@ export class NotificationService { notificationId: string, subject: string, template: any, + type?: NotificationType, ) { try { const user = await this.db .selectFrom('users') - .select(['email']) + .select(['email', 'settings']) .where('id', '=', userId) .where('deletedAt', 'is', null) + .where('deactivatedAt', 'is', null) .executeTakeFirst(); if (!user?.email) return; + if (type) { + const settingKey = NotificationTypeToSettingKey[type]; + if (settingKey) { + const settings = user.settings as any; + if (settings?.notifications?.[settingKey] === false) return; + } + } + await this.mailService.sendToQueue({ to: user.email, subject, diff --git a/apps/server/src/core/notification/services/comment.notification.ts b/apps/server/src/core/notification/services/comment.notification.ts index e75da302..c79c2895 100644 --- a/apps/server/src/core/notification/services/comment.notification.ts +++ b/apps/server/src/core/notification/services/comment.notification.ts @@ -86,12 +86,14 @@ export class CommentNotificationService { spaceId, commentId, }); + if (!notification) continue; await this.notificationService.queueEmail( userId, notification.id, `${actor.name} mentioned you in a comment`, CommentMentionEmail({ actorName: actor.name, pageTitle, pageUrl }), + NotificationType.COMMENT_USER_MENTION, ); notifiedUserIds.add(userId); @@ -110,12 +112,14 @@ export class CommentNotificationService { spaceId, commentId, }); + if (!notification) continue; await this.notificationService.queueEmail( recipientId, notification.id, `${actor.name} commented on ${pageTitle}`, CommentCreateEmail({ actorName: actor.name, pageTitle, pageUrl }), + NotificationType.COMMENT_CREATED, ); } } @@ -171,6 +175,7 @@ export class CommentNotificationService { spaceId, commentId, }); + if (!notification) return; const subject = `${actor.name} resolved a comment on ${pageTitle}`; @@ -179,6 +184,7 @@ export class CommentNotificationService { notification.id, subject, CommentResolvedEmail({ actorName: actor.name, pageTitle, pageUrl }), + NotificationType.COMMENT_RESOLVED, ); } diff --git a/apps/server/src/core/notification/services/page-update-email-rate-limiter.ts b/apps/server/src/core/notification/services/page-update-email-rate-limiter.ts new file mode 100644 index 00000000..59867f41 --- /dev/null +++ b/apps/server/src/core/notification/services/page-update-email-rate-limiter.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@nestjs/common'; +import { RedisService } from '@nestjs-labs/nestjs-ioredis'; +import type { Redis } from 'ioredis'; + +const KEY_PREFIX = 'page-update:emails:'; +const DIGEST_PREFIX = 'page-update:digest:'; +const TTL_SECONDS = 86400; // 24 hours +const MAX_IMMEDIATE_EMAILS = 4; + +@Injectable() +export class PageUpdateEmailRateLimiter { + private readonly redis: Redis; + + constructor(private readonly redisService: RedisService) { + this.redis = this.redisService.getOrThrow(); + } + + async canSendEmail(userId: string): Promise { + const key = KEY_PREFIX + userId; + const count = await this.redis.incr(key); + await this.redis.expire(key, TTL_SECONDS, 'NX'); + return count <= MAX_IMMEDIATE_EMAILS; + } + + async addToDigest(userId: string, notificationId: string): Promise { + const key = DIGEST_PREFIX + userId; + const len = await this.redis.rpush(key, notificationId); + await this.redis.expire(key, TTL_SECONDS); + return len === 1; + } + + async popDigest(userId: string): Promise { + const key = DIGEST_PREFIX + userId; + const [ids] = await this.redis + .multi() + .lrange(key, 0, -1) + .del(key) + .exec(); + + return (ids?.[1] as string[]) ?? []; + } + +} diff --git a/apps/server/src/core/notification/services/page.notification.ts b/apps/server/src/core/notification/services/page.notification.ts index a8d951dd..9e5c75dd 100644 --- a/apps/server/src/core/notification/services/page.notification.ts +++ b/apps/server/src/core/notification/services/page.notification.ts @@ -1,25 +1,43 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; import { InjectKysely } from 'nestjs-kysely'; +import { InjectQueue } from '@nestjs/bullmq'; +import { Queue } from 'bullmq'; import { KyselyDB } from '@docmost/db/types/kysely.types'; import { IPageMentionNotificationJob, + IPageUpdateNotificationJob, IPermissionGrantedNotificationJob, } from '../../../integrations/queue/constants/queue.interface'; import { NotificationService } from '../notification.service'; import { NotificationType } from '../notification.constants'; +import { NotificationRepo } from '@docmost/db/repos/notification/notification.repo'; import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo'; import { PagePermissionRepo } from '@docmost/db/repos/page/page-permission.repo'; +import { WatcherRepo } from '@docmost/db/repos/watcher/watcher.repo'; +import { PageUpdateEmailRateLimiter } from './page-update-email-rate-limiter'; import { PageMentionEmail } from '@docmost/transactional/emails/page-mention-email'; +import { PageUpdateEmail } from '@docmost/transactional/emails/page-update-email'; +import { PageUpdateDigestEmail } from '@docmost/transactional/emails/page-update-digest-email'; import { PermissionGrantedEmail } from '@docmost/transactional/emails/permission-granted-email'; import { getPageTitle } from '../../../common/helpers'; +import { QueueJob, QueueName } from '../../../integrations/queue/constants'; + +const PAGE_UPDATE_COOLDOWN_HOURS = 7; +const DIGEST_DELAY_MS = 12 * 60 * 60 * 1000; // 12 hours @Injectable() export class PageNotificationService { + private readonly logger = new Logger(PageNotificationService.name); + constructor( @InjectKysely() private readonly db: KyselyDB, private readonly notificationService: NotificationService, + private readonly notificationRepo: NotificationRepo, private readonly spaceMemberRepo: SpaceMemberRepo, private readonly pagePermissionRepo: PagePermissionRepo, + private readonly watcherRepo: WatcherRepo, + private readonly rateLimiter: PageUpdateEmailRateLimiter, + @InjectQueue(QueueName.NOTIFICATION_QUEUE) private notificationQueue: Queue, ) {} async processPageMention(data: IPageMentionNotificationJob, appUrl: string) { @@ -41,10 +59,9 @@ export class PageNotificationService { ); const usersWithPageAccess = - await this.pagePermissionRepo.getUserIdsWithPageAccess( - pageId, - [...usersWithSpaceAccess], - ); + await this.pagePermissionRepo.getUserIdsWithPageAccess(pageId, [ + ...usersWithSpaceAccess, + ]); const usersWithAccess = new Set(usersWithPageAccess); const accessibleMentions = newMentions.filter((m) => @@ -97,6 +114,7 @@ export class PageNotificationService { spaceId, data: { mentionId }, }); + if (!notification) continue; const pageUrl = `${basePageUrl}`; const subject = `${actor.name} mentioned you in ${pageTitle}`; @@ -106,6 +124,7 @@ export class PageNotificationService { notification.id, subject, PageMentionEmail({ actorName: actor.name, pageTitle, pageUrl }), + NotificationType.PAGE_USER_MENTION, ); } } @@ -139,6 +158,7 @@ export class PageNotificationService { spaceId, data: { role }, }); + if (!notification) continue; const subject = `${actor.name} gave you ${accessLabel} access to ${pageTitle}`; @@ -156,6 +176,232 @@ export class PageNotificationService { } } + async processPageUpdate(data: IPageUpdateNotificationJob, appUrl: string) { + const { pageId, spaceId, workspaceId, actorIds } = data; + + const watcherIds = await this.watcherRepo.getPageWatcherIds(pageId); + if (watcherIds.length === 0) return; + + const actorSet = new Set(actorIds); + const candidateIds = watcherIds.filter((id) => !actorSet.has(id)); + if (candidateIds.length === 0) return; + + const eligibleUsers = await this.getEligiblePageUpdateUsers(candidateIds); + if (eligibleUsers.size === 0) return; + + const afterPrefs = [...eligibleUsers.keys()]; + + const recentlyNotified = + await this.notificationRepo.getRecentlyNotifiedUserIds( + afterPrefs, + pageId, + NotificationType.PAGE_UPDATED, + PAGE_UPDATE_COOLDOWN_HOURS, + ); + const afterCooldown = afterPrefs.filter((id) => !recentlyNotified.has(id)); + if (afterCooldown.length === 0) return; + + const usersWithSpaceAccess = + await this.spaceMemberRepo.getUserIdsWithSpaceAccess( + afterCooldown, + spaceId, + ); + + const usersWithPageAccess = + await this.pagePermissionRepo.getUserIdsWithPageAccess(pageId, [ + ...usersWithSpaceAccess, + ]); + if (usersWithPageAccess.length === 0) return; + + const recipientIds = new Set(usersWithPageAccess); + const actorId = actorIds[0]; + + const context = await this.getPageContext(actorId, pageId, spaceId, appUrl); + if (!context) return; + + const { actor, pageTitle, basePageUrl } = context; + + for (const userId of recipientIds) { + const notification = await this.notificationService.create({ + userId, + workspaceId, + type: NotificationType.PAGE_UPDATED, + actorId, + pageId, + spaceId, + }); + if (!notification) continue; + + const canSend = await this.rateLimiter.canSendEmail(userId); + if (canSend) { + await this.notificationService.queueEmail( + userId, + notification.id, + `${actor.name} updated ${pageTitle}`, + PageUpdateEmail({ + userName: eligibleUsers.get(userId) ?? '', + actorName: actor.name, + pageTitle, + pageUrl: basePageUrl, + }), + NotificationType.PAGE_UPDATED, + ); + } else { + const isFirst = await this.rateLimiter.addToDigest( + userId, + notification.id, + ); + if (isFirst) { + await this.scheduleDigest(userId, workspaceId); + } + } + } + } + + private async getEligiblePageUpdateUsers( + userIds: string[], + ): Promise> { + if (userIds.length === 0) return new Map(); + + const users = await this.db + .selectFrom('users') + .select(['id', 'name', 'settings']) + .where('id', 'in', userIds) + .where('deletedAt', 'is', null) + .where('deactivatedAt', 'is', null) + .execute(); + + const eligible = new Map(); + for (const u of users) { + const settings = u.settings as any; + if (settings?.notifications?.['page.updated'] !== false) { + eligible.set(u.id, u.name); + } + } + return eligible; + } + + private async scheduleDigest( + userId: string, + workspaceId: string, + ): Promise { + await this.notificationQueue + .add( + QueueJob.PAGE_UPDATE_DIGEST, + { userId, workspaceId }, + { delay: DIGEST_DELAY_MS, removeOnComplete: true }, + ) + .catch((err) => { + this.logger.error( + `Failed to schedule digest for ${userId}: ${err.message}`, + ); + }); + } + + async processDigest(userId: string, appUrl: string): Promise { + const notificationIds = await this.rateLimiter.popDigest(userId); + if (notificationIds.length === 0) return; + + const [user, notifications] = await Promise.all([ + this.db + .selectFrom('users') + .select(['id', 'name']) + .where('id', '=', userId) + .executeTakeFirst(), + this.db + .selectFrom('notifications') + .select(['id', 'pageId', 'actorId']) + .where('id', 'in', notificationIds) + .execute(), + ]); + + if (!user || notifications.length === 0) return; + + const pageIds = [ + ...new Set(notifications.map((n) => n.pageId).filter(Boolean)), + ]; + const actorIds = [ + ...new Set(notifications.map((n) => n.actorId).filter(Boolean)), + ]; + + const allPages = await this.db + .selectFrom('pages') + .innerJoin('spaces', 'spaces.id', 'pages.spaceId') + .select([ + 'pages.id', + 'pages.title', + 'pages.slugId', + 'pages.spaceId', + 'spaces.slug as spaceSlug', + ]) + .where('pages.id', 'in', pageIds) + .execute(); + + if (allPages.length === 0) return; + + const spaceIds = [...new Set(allPages.map((p) => p.spaceId))]; + + const accessibleSpaceIds = new Set(); + for (const spaceId of spaceIds) { + const usersWithAccess = + await this.spaceMemberRepo.getUserIdsWithSpaceAccess([userId], spaceId); + if (usersWithAccess.has(userId)) accessibleSpaceIds.add(spaceId); + } + + const spaceFilteredPages = allPages.filter((p) => + accessibleSpaceIds.has(p.spaceId), + ); + if (spaceFilteredPages.length === 0) return; + + const accessiblePageIds = new Set(); + for (const p of spaceFilteredPages) { + const hasAccess = await this.pagePermissionRepo.getUserIdsWithPageAccess( + p.id, + [userId], + ); + if (hasAccess.includes(userId)) accessiblePageIds.add(p.id); + } + + const pages = spaceFilteredPages.filter((p) => accessiblePageIds.has(p.id)); + if (pages.length === 0) return; + + const actors = actorIds.length > 0 + ? await this.db + .selectFrom('users') + .select(['id', 'name']) + .where('id', 'in', actorIds) + .execute() + : []; + + const actorMap = new Map(actors.map((a) => [a.id, a.name])); + const pageActors = new Map>(); + for (const n of notifications) { + if (!n.pageId || !n.actorId) continue; + const names = pageActors.get(n.pageId) ?? new Set(); + const name = actorMap.get(n.actorId); + if (name) names.add(name); + pageActors.set(n.pageId, names); + } + + const pageUpdates = pages.map((p) => ({ + title: getPageTitle(p.title), + url: `${appUrl}/s/${p.spaceSlug}/p/${p.slugId}`, + updatedBy: [...(pageActors.get(p.id) ?? [])], + })); + + await this.notificationService.queueEmail( + userId, + notificationIds[0], + `Your digest: ${pageUpdates.length} page ${pageUpdates.length === 1 ? 'update' : 'updates'}`, + PageUpdateDigestEmail({ + userName: user.name, + pageUpdates, + totalUpdates: pageUpdates.length, + }), + NotificationType.PAGE_UPDATED, + ); + } + private async getPageContext( actorId: string, pageId: string, diff --git a/apps/server/src/core/user/dto/update-user.dto.ts b/apps/server/src/core/user/dto/update-user.dto.ts index 3f771339..f1c02c51 100644 --- a/apps/server/src/core/user/dto/update-user.dto.ts +++ b/apps/server/src/core/user/dto/update-user.dto.ts @@ -35,4 +35,24 @@ export class UpdateUserDto extends PartialType( @MaxLength(70) @IsString() confirmPassword: string; + + @IsOptional() + @IsBoolean() + notificationPageUpdates: boolean; + + @IsOptional() + @IsBoolean() + notificationPageUserMention: boolean; + + @IsOptional() + @IsBoolean() + notificationCommentUserMention: boolean; + + @IsOptional() + @IsBoolean() + notificationCommentCreated: boolean; + + @IsOptional() + @IsBoolean() + notificationCommentResolved: boolean; } diff --git a/apps/server/src/core/user/user.service.ts b/apps/server/src/core/user/user.service.ts index 59bc08ec..fa229827 100644 --- a/apps/server/src/core/user/user.service.ts +++ b/apps/server/src/core/user/user.service.ts @@ -7,6 +7,7 @@ import { UnauthorizedException, } from '@nestjs/common'; import { UpdateUserDto } from './dto/update-user.dto'; +import { NotificationSettingKey } from '../notification/notification.constants'; import { comparePasswordHash, diffAuditTrackedFields } from 'src/common/helpers/utils'; import { Workspace } from '@docmost/db/types/entity.types'; import { validateSsoEnforcement } from '../auth/auth.util'; @@ -60,6 +61,24 @@ export class UserService { ); } + const notificationSettings: Record = { + notificationPageUpdates: 'page.updated', + notificationPageUserMention: 'page.userMention', + notificationCommentUserMention: 'comment.userMention', + notificationCommentCreated: 'comment.created', + notificationCommentResolved: 'comment.resolved', + }; + + for (const [dtoField, settingKey] of Object.entries(notificationSettings)) { + if (typeof updateUserDto[dtoField] !== 'undefined') { + return this.userRepo.updateNotificationSetting( + userId, + settingKey, + updateUserDto[dtoField], + ); + } + } + const userBefore = { name: user.name, email: user.email, locale: user.locale }; if (updateUserDto.name) { diff --git a/apps/server/src/core/watcher/watcher.controller.ts b/apps/server/src/core/watcher/watcher.controller.ts index 8709719a..cd10fa37 100644 --- a/apps/server/src/core/watcher/watcher.controller.ts +++ b/apps/server/src/core/watcher/watcher.controller.ts @@ -1,8 +1,6 @@ -/*** - import { +import { Body, Controller, - ForbiddenException, HttpCode, HttpStatus, NotFoundException, @@ -16,12 +14,7 @@ import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; import { User, Workspace } from '@docmost/db/types/entity.types'; import { WatcherPageDto } from './dto/watcher.dto'; import { PageRepo } from '@docmost/db/repos/page/page.repo'; -import SpaceAbilityFactory from '../casl/abilities/space-ability.factory'; -import { - SpaceCaslAction, - SpaceCaslSubject, -} from '../casl/interfaces/space-ability.type'; - +import { PageAccessService } from '../page/page-access/page-access.service'; @UseGuards(JwtAuthGuard) @Controller('pages') @@ -29,7 +22,7 @@ export class WatcherController { constructor( private readonly watcherService: WatcherService, private readonly pageRepo: PageRepo, - private readonly spaceAbility: SpaceAbilityFactory, + private readonly pageAccessService: PageAccessService, ) {} @HttpCode(HttpStatus.OK) @@ -44,10 +37,7 @@ export class WatcherController { throw new NotFoundException('Page not found'); } - const ability = await this.spaceAbility.createForUser(user, page.spaceId); - if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) { - throw new ForbiddenException(); - } + await this.pageAccessService.validateCanView(page, user); await this.watcherService.watchPage( user.id, @@ -67,10 +57,7 @@ export class WatcherController { throw new NotFoundException('Page not found'); } - const ability = await this.spaceAbility.createForUser(user, page.spaceId); - if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) { - throw new ForbiddenException(); - } + await this.pageAccessService.validateCanView(page, user); await this.watcherService.unwatchPage(user.id, page.id); @@ -85,15 +72,10 @@ export class WatcherController { throw new NotFoundException('Page not found'); } - const ability = await this.spaceAbility.createForUser(user, page.spaceId); - if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) { - throw new ForbiddenException(); - } + await this.pageAccessService.validateCanView(page, user); const watching = await this.watcherService.isWatchingPage(user.id, page.id); return { watching }; } - } -***/ diff --git a/apps/server/src/core/watcher/watcher.module.ts b/apps/server/src/core/watcher/watcher.module.ts index 68ab5624..76267b5a 100644 --- a/apps/server/src/core/watcher/watcher.module.ts +++ b/apps/server/src/core/watcher/watcher.module.ts @@ -1,10 +1,11 @@ import { Module } from '@nestjs/common'; import { WatcherService } from './watcher.service'; -import { CaslModule } from '../casl/casl.module'; +import { WatcherController } from './watcher.controller'; +import { PageAccessModule } from '../page/page-access/page-access.module'; @Module({ - imports: [CaslModule], - controllers: [], + imports: [PageAccessModule], + controllers: [WatcherController], providers: [WatcherService], exports: [WatcherService], }) diff --git a/apps/server/src/database/repos/notification/notification.repo.ts b/apps/server/src/database/repos/notification/notification.repo.ts index 19add3c6..2914dbfc 100644 --- a/apps/server/src/database/repos/notification/notification.repo.ts +++ b/apps/server/src/database/repos/notification/notification.repo.ts @@ -11,6 +11,7 @@ import { ExpressionBuilder } from 'kysely'; import { DB } from '@docmost/db/types/db'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo'; +import { NotificationTab, NotificationType } from '../../../core/notification/notification.constants'; @Injectable() export class NotificationRepo { @@ -27,8 +28,12 @@ export class NotificationRepo { .executeTakeFirst(); } - async findByUserId(userId: string, pagination: PaginationOptions) { - const query = this.db + async findByUserId( + userId: string, + pagination: PaginationOptions, + type: NotificationTab = 'all', + ) { + let query = this.db .selectFrom('notifications') .selectAll('notifications') .select((eb) => this.withActor(eb)) @@ -42,6 +47,12 @@ export class NotificationRepo { ]), ); + if (type === 'direct') { + query = query.where('type', '!=', NotificationType.PAGE_UPDATED); + } else if (type === 'updates') { + query = query.where('type', '=', NotificationType.PAGE_UPDATED); + } + return executeWithCursorPagination(query, { perPage: pagination.limit, cursor: pagination.cursor, @@ -138,6 +149,29 @@ export class NotificationRepo { .execute(); } + async getRecentlyNotifiedUserIds( + userIds: string[], + pageId: string, + type: string, + withinHours: number, + ): Promise> { + if (userIds.length === 0) return new Set(); + + const cutoff = new Date(Date.now() - withinHours * 60 * 60 * 1000); + + const rows = await this.db + .selectFrom('notifications') + .select('userId') + .where('userId', 'in', userIds) + .where('pageId', '=', pageId) + .where('type', '=', type) + .where('createdAt', '>', cutoff) + .groupBy('userId') + .execute(); + + return new Set(rows.map((r) => r.userId)); + } + withActor(eb: ExpressionBuilder) { return jsonObjectFrom( eb diff --git a/apps/server/src/database/repos/user/user.repo.ts b/apps/server/src/database/repos/user/user.repo.ts index c3903357..eaaa318e 100644 --- a/apps/server/src/database/repos/user/user.repo.ts +++ b/apps/server/src/database/repos/user/user.repo.ts @@ -13,6 +13,7 @@ import { PaginationOptions } from '../../pagination/pagination-options'; import { executeWithCursorPagination } from '@docmost/db/pagination/cursor-pagination'; import { ExpressionBuilder, sql } from 'kysely'; import { jsonObjectFrom } from 'kysely/helpers/postgres'; +import { NotificationSettingKey } from '../../../core/notification/notification.constants'; @Injectable() export class UserRepo { @@ -191,6 +192,24 @@ export class UserRepo { .executeTakeFirst(); } + async updateNotificationSetting( + userId: string, + settingKey: NotificationSettingKey, + settingValue: boolean, + ) { + return await this.db + .updateTable('users') + .set({ + settings: sql`COALESCE(settings, '{}'::jsonb) + || jsonb_build_object('notifications', COALESCE(settings->'notifications', '{}'::jsonb) + || jsonb_build_object(${sql.lit(settingKey)}, ${sql.lit(settingValue)}))`, + updatedAt: new Date(), + }) + .where('id', '=', userId) + .returning(this.baseFields) + .executeTakeFirst(); + } + withUserMfa(eb: ExpressionBuilder) { return jsonObjectFrom( eb diff --git a/apps/server/src/integrations/queue/constants/queue.constants.ts b/apps/server/src/integrations/queue/constants/queue.constants.ts index 1c66a5f3..92d15426 100644 --- a/apps/server/src/integrations/queue/constants/queue.constants.ts +++ b/apps/server/src/integrations/queue/constants/queue.constants.ts @@ -69,6 +69,7 @@ export enum QueueJob { COMMENT_RESOLVED_NOTIFICATION = 'comment-resolved-notification', PAGE_MENTION_NOTIFICATION = 'page-mention-notification', PAGE_PERMISSION_GRANTED = 'page-permission-granted', + PAGE_UPDATE_DIGEST = 'page-update-digest', AUDIT_LOG = 'audit-log', AUDIT_CLEANUP = 'audit-cleanup', diff --git a/apps/server/src/integrations/queue/constants/queue.interface.ts b/apps/server/src/integrations/queue/constants/queue.interface.ts index 4254bbdc..f0683a4e 100644 --- a/apps/server/src/integrations/queue/constants/queue.interface.ts +++ b/apps/server/src/integrations/queue/constants/queue.interface.ts @@ -60,6 +60,13 @@ export interface IPageMentionNotificationJob { workspaceId: string; } +export interface IPageUpdateNotificationJob { + pageId: string; + spaceId: string; + workspaceId: string; + actorIds: string[]; +} + export interface IPermissionGrantedNotificationJob { userIds: string[]; pageId: string; diff --git a/apps/server/src/integrations/transactional/emails/page-update-digest-email.tsx b/apps/server/src/integrations/transactional/emails/page-update-digest-email.tsx new file mode 100644 index 00000000..f847291d --- /dev/null +++ b/apps/server/src/integrations/transactional/emails/page-update-digest-email.tsx @@ -0,0 +1,76 @@ +import { Link, Section, Text } from '@react-email/components'; +import * as React from 'react'; +import { content, link, paragraph } from '../css/styles'; +import { getGreetingName, MailBody } from '../partials/partials'; + +interface PageUpdate { + title: string; + url: string; + updatedBy: string[]; +} + +interface Props { + userName: string; + pageUpdates: PageUpdate[]; + totalUpdates: number; +} + +export const PageUpdateDigestEmail = ({ + userName, + pageUpdates, + totalUpdates, +}: Props) => { + return ( + +
+ + Hi {getGreetingName(userName)}, + + + There {totalUpdates === 1 ? 'has' : 'have'} been{' '} + + {totalUpdates} update{totalUpdates === 1 ? '' : 's'} + {' '} + since your last update. + + + {pageUpdates.map((page, i) => ( +
+ + + {page.title} + + + {page.updatedBy.length > 0 && ( + + Edited by {page.updatedBy.join(', ')} + + )} +
+ ))} +
+
+ ); +}; + +const pageCard = { + borderLeft: '3px solid #e8e5ef', + paddingLeft: '12px', + marginBottom: '12px', +}; + +const pageTitle = { + ...paragraph, + margin: '0 0 2px 0', + fontSize: 14, + fontWeight: 'bold' as const, +}; + +const updatedByText = { + ...paragraph, + margin: '0', + fontSize: 13, + color: '#666', +}; + +export default PageUpdateDigestEmail; diff --git a/apps/server/src/integrations/transactional/emails/page-update-email.tsx b/apps/server/src/integrations/transactional/emails/page-update-email.tsx new file mode 100644 index 00000000..188d8a34 --- /dev/null +++ b/apps/server/src/integrations/transactional/emails/page-update-email.tsx @@ -0,0 +1,36 @@ +import { Link, Section, Text } from '@react-email/components'; +import * as React from 'react'; +import { content, link, paragraph } from '../css/styles'; +import { EmailButton, getGreetingName, MailBody } from '../partials/partials'; + +interface Props { + userName: string; + actorName: string; + pageTitle: string; + pageUrl: string; +} + +export const PageUpdateEmail = ({ + userName, + actorName, + pageTitle, + pageUrl, +}: Props) => { + return ( + +
+ Hi {getGreetingName(userName)}, + + {actorName} updated{' '} + + {pageTitle} + + . + +
+ View page +
+ ); +}; + +export default PageUpdateEmail; diff --git a/apps/server/src/integrations/transactional/partials/partials.tsx b/apps/server/src/integrations/transactional/partials/partials.tsx index f97eb989..c5b30e6b 100644 --- a/apps/server/src/integrations/transactional/partials/partials.tsx +++ b/apps/server/src/integrations/transactional/partials/partials.tsx @@ -87,3 +87,7 @@ export function MailFooter() { ); } + +export function getGreetingName(name?: string): string { + return name?.split(' ')[0] || 'there'; +} From af8b0ddf3a2931ffd984e7611b231d97896d970f Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:05:09 +0100 Subject: [PATCH 34/44] sync --- apps/server/src/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/ee b/apps/server/src/ee index 350ef574..d6675bed 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 350ef574e398c318aa57ce5f79ab12e9d8329dcb +Subproject commit d6675bed05c20422c978e215866fb334c28dec09 From fd91b11c6c41c906e908e4ca33a0ab09a73d979b Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:06:44 +0100 Subject: [PATCH 35/44] pin version --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6599b07a..77878a19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -286,7 +286,7 @@ importers: specifier: ^1.1.0 version: 1.1.0 axios: - specifier: ^1.13.6 + specifier: 1.13.6 version: 1.13.6 blueimp-load-image: specifier: ^5.16.0 From 9aaa6c731c4a147033827c55e3b2e0579c52c398 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:39:49 +0100 Subject: [PATCH 36/44] feat: add AI_EMBEDDING_SUPPORTS_MRL env var to decouple pgvector dimensions from model API (#2079) Some embedding models don't accept a `dimensions` parameter. This adds an optional env var that controls whether the dimension is sent to the model API, while always using it for pgvector indexing. Preset models have this handled automatically; the env var allows explicit override for custom models. --- apps/client/package.json | 2 +- apps/server/src/ee | 2 +- .../src/integrations/environment/environment.service.ts | 6 ++++++ .../src/integrations/environment/environment.validation.ts | 6 ++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/client/package.json b/apps/client/package.json index 67b25406..3eddb402 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -25,7 +25,7 @@ "@tabler/icons-react": "^3.40.0", "@tanstack/react-query": "5.90.17", "alfaaz": "^1.1.0", - "axios": "^1.13.6", + "axios": "1.13.6", "blueimp-load-image": "^5.16.0", "clsx": "^2.1.1", "emoji-mart": "^5.6.0", diff --git a/apps/server/src/ee b/apps/server/src/ee index d6675bed..670b9458 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit d6675bed05c20422c978e215866fb334c28dec09 +Subproject commit 670b9458f3bbdc6dcfe349933cae4d95ff05b978 diff --git a/apps/server/src/integrations/environment/environment.service.ts b/apps/server/src/integrations/environment/environment.service.ts index 89e4bb81..b1b27d40 100644 --- a/apps/server/src/integrations/environment/environment.service.ts +++ b/apps/server/src/integrations/environment/environment.service.ts @@ -259,6 +259,12 @@ export class EnvironmentService { ); } + getAiEmbeddingSupportsMrl(): boolean | undefined { + const val = this.configService.get('AI_EMBEDDING_SUPPORTS_MRL'); + if (val === undefined || val === null || val === '') return undefined; + return val === 'true'; + } + getOpenAiApiKey(): string { return this.configService.get('OPENAI_API_KEY'); } diff --git a/apps/server/src/integrations/environment/environment.validation.ts b/apps/server/src/integrations/environment/environment.validation.ts index 5c307da2..3a59b08c 100644 --- a/apps/server/src/integrations/environment/environment.validation.ts +++ b/apps/server/src/integrations/environment/environment.validation.ts @@ -117,6 +117,12 @@ export class EnvironmentVariables { @IsString() AI_EMBEDDING_DIMENSION: string; + @IsOptional() + @ValidateIf((obj) => obj.AI_EMBEDDING_SUPPORTS_MRL) + @IsIn(['true', 'false']) + @IsString() + AI_EMBEDDING_SUPPORTS_MRL: string; + @ValidateIf((obj) => obj.AI_DRIVER) @IsString() @IsNotEmpty() From 992691e6e04dd2d6ff1d0069bd53f924102ce2f4 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:41:09 +0100 Subject: [PATCH 37/44] fix module import --- apps/server/src/collaboration/server/collab-app.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/server/src/collaboration/server/collab-app.module.ts b/apps/server/src/collaboration/server/collab-app.module.ts index 396ff0a9..f0e27d41 100644 --- a/apps/server/src/collaboration/server/collab-app.module.ts +++ b/apps/server/src/collaboration/server/collab-app.module.ts @@ -11,12 +11,14 @@ import { CollaborationController } from './collaboration.controller'; import { LoggerModule } from '../../common/logger/logger.module'; import { RedisModule } from '@nestjs-labs/nestjs-ioredis'; import { RedisConfigService } from '../../integrations/redis/redis-config.service'; +import { CaslModule } from '../../core/casl/casl.module'; @Module({ imports: [ LoggerModule, DatabaseModule, EnvironmentModule, + CaslModule, CollaborationModule, QueueModule, HealthModule, From 147d028036e3b976303d86ecf7a387b0d5b2a231 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:42:37 +0100 Subject: [PATCH 38/44] v0.71.0 --- apps/client/package.json | 2 +- apps/server/package.json | 2 +- apps/server/src/ee | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/client/package.json b/apps/client/package.json index 3eddb402..4b767f60 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -1,7 +1,7 @@ { "name": "client", "private": true, - "version": "0.70.3", + "version": "0.71.0", "scripts": { "dev": "vite", "build": "tsc && vite build", diff --git a/apps/server/package.json b/apps/server/package.json index a8869302..c6e0dc2c 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "0.70.3", + "version": "0.71.0", "description": "", "author": "", "private": true, diff --git a/apps/server/src/ee b/apps/server/src/ee index 670b9458..38158a5a 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 670b9458f3bbdc6dcfe349933cae4d95ff05b978 +Subproject commit 38158a5ab6a4c12c90a3124604b88f05d76efd7d diff --git a/package.json b/package.json index 346204c8..36db0934 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "docmost", "homepage": "https://docmost.com", - "version": "0.70.3", + "version": "0.71.0", "private": true, "scripts": { "build": "nx run-many -t build", From 642024ba9d1afcc7c14ad3c34da8495037faeaf1 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Tue, 31 Mar 2026 21:14:41 +0100 Subject: [PATCH 39/44] New Crowdin updates (#2078) --- .../public/locales/de-DE/translation.json | 18 +++++++++++++++++ .../public/locales/en-US/translation.json | 20 +++++++++---------- .../public/locales/es-ES/translation.json | 18 +++++++++++++++++ .../public/locales/fr-FR/translation.json | 18 +++++++++++++++++ .../public/locales/it-IT/translation.json | 18 +++++++++++++++++ .../public/locales/ja-JP/translation.json | 18 +++++++++++++++++ .../public/locales/ko-KR/translation.json | 18 +++++++++++++++++ .../public/locales/nl-NL/translation.json | 18 +++++++++++++++++ .../public/locales/pt-BR/translation.json | 18 +++++++++++++++++ .../public/locales/ru-RU/translation.json | 18 +++++++++++++++++ .../public/locales/uk-UA/translation.json | 18 +++++++++++++++++ .../public/locales/zh-CN/translation.json | 18 +++++++++++++++++ 12 files changed, 208 insertions(+), 10 deletions(-) diff --git a/apps/client/public/locales/de-DE/translation.json b/apps/client/public/locales/de-DE/translation.json index 437181d4..1f618fc5 100644 --- a/apps/client/public/locales/de-DE/translation.json +++ b/apps/client/public/locales/de-DE/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} hat Sie auf einer Seite erwähnt", "{{name}} gave you edit access to a page": "{{name}} hat Ihnen Bearbeitungszugriff auf eine Seite gegeben", "{{name}} gave you view access to a page": "{{name}} hat Ihnen Ansichtsrechte für eine Seite gegeben", + "{{name}} updated a page": "{{name}} hat eine Seite aktualisiert.", + "Watch page": "Seite beobachten", + "Stop watching": "Beobachtung beenden", + "Email notifications": "E-Mail-Benachrichtigungen", + "Page updates": "Seitenaktualisierungen", + "Get notified when pages you watch are updated.": "Erhalten Sie eine Benachrichtigung, wenn Seiten, die Sie beobachten, aktualisiert werden.", + "Page mentions": "Seiten-Erwähnungen", + "Get notified when someone mentions you on a page.": "Erhalten Sie eine Benachrichtigung, wenn Sie jemand auf einer Seite erwähnt.", + "Comment mentions": "Kommentar-Erwähnungen", + "Get notified when someone mentions you in a comment.": "Erhalten Sie eine Benachrichtigung, wenn Sie jemand in einem Kommentar erwähnt.", + "New comments": "Neue Kommentare", + "Get notified about new comments on threads you participate in.": "Erhalten Sie eine Benachrichtigung über neue Kommentare in Threads, an denen Sie teilnehmen.", + "Resolved comments": "Erledigte Kommentare", + "Get notified when your comment is resolved.": "Erhalten Sie eine Benachrichtigung, wenn Ihr Kommentar erledigt wurde.", + "You are now watching this page": "Sie beobachten diese Seite jetzt", + "You are no longer watching this page": "Sie beobachten diese Seite nicht mehr", + "Direct": "Direkt", + "Updates": "Aktualisierungen", "Today": "Heute", "Yesterday": "Gestern", "This week": "Diese Woche", diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index b1c1ed6c..171ba7b9 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -674,22 +674,22 @@ "{{name}} mentioned you on a page": "{{name}} mentioned you on a page.", "{{name}} gave you edit access to a page": "{{name}} gave you edit access to a page.", "{{name}} gave you view access to a page": "{{name}} gave you view access to a page.", - "{{name}} updated a page": "{{name}} updated a page.", - "Watch page": "Watch page", - "Stop watching": "Stop watching", + "{{name}} updated a page": "{{name}} has updated a page.", + "Watch page": "Watch this page", + "Stop watching": "Stop watching this page", "Email notifications": "Email notifications", "Page updates": "Page updates", - "Get notified when pages you watch are updated.": "Get notified when pages you watch are updated.", + "Get notified when pages you watch are updated.": "Receive notifications when the pages you watch are updated.", "Page mentions": "Page mentions", - "Get notified when someone mentions you on a page.": "Get notified when someone mentions you on a page.", + "Get notified when someone mentions you on a page.": "Receive notifications when someone mentions you on a page.", "Comment mentions": "Comment mentions", - "Get notified when someone mentions you in a comment.": "Get notified when someone mentions you in a comment.", + "Get notified when someone mentions you in a comment.": "Receive notifications when someone mentions you in a comment.", "New comments": "New comments", - "Get notified about new comments on threads you participate in.": "Get notified about new comments on threads you participate in.", + "Get notified about new comments on threads you participate in.": "Receive notifications about new comments in threads you are participating in.", "Resolved comments": "Resolved comments", - "Get notified when your comment is resolved.": "Get notified when your comment is resolved.", - "You are now watching this page": "You are now watching this page", - "You are no longer watching this page": "You are no longer watching this page", + "Get notified when your comment is resolved.": "Receive a notification when your comment is resolved.", + "You are now watching this page": "You’re now watching this page", + "You are no longer watching this page": "You’re no longer watching this page", "Direct": "Direct", "Updates": "Updates", "Today": "Today", diff --git a/apps/client/public/locales/es-ES/translation.json b/apps/client/public/locales/es-ES/translation.json index 2b6e88da..d1705bb1 100644 --- a/apps/client/public/locales/es-ES/translation.json +++ b/apps/client/public/locales/es-ES/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} te mencionó en una página", "{{name}} gave you edit access to a page": "{{name}} te dio acceso de edición a una página", "{{name}} gave you view access to a page": "{{name}} te dio acceso de visualización a una página", + "{{name}} updated a page": "{{name}} actualizó una página.", + "Watch page": "Seguir página", + "Stop watching": "Dejar de seguir", + "Email notifications": "Notificaciones por correo electrónico", + "Page updates": "Actualizaciones de página", + "Get notified when pages you watch are updated.": "Recibe una notificación cuando se actualicen las páginas que sigues.", + "Page mentions": "Menciones en la página", + "Get notified when someone mentions you on a page.": "Recibe una notificación cuando alguien te mencione en una página.", + "Comment mentions": "Menciones en comentarios", + "Get notified when someone mentions you in a comment.": "Recibe una notificación cuando alguien te mencione en un comentario.", + "New comments": "Nuevos comentarios", + "Get notified about new comments on threads you participate in.": "Recibe una notificación sobre nuevos comentarios en los hilos donde participas.", + "Resolved comments": "Comentarios resueltos", + "Get notified when your comment is resolved.": "Recibe una notificación cuando tu comentario sea resuelto.", + "You are now watching this page": "Ahora sigues esta página", + "You are no longer watching this page": "Ya no sigues esta página", + "Direct": "Directo", + "Updates": "Actualizaciones", "Today": "Hoy", "Yesterday": "Ayer", "This week": "Esta semana", diff --git a/apps/client/public/locales/fr-FR/translation.json b/apps/client/public/locales/fr-FR/translation.json index 288dfe84..f8971588 100644 --- a/apps/client/public/locales/fr-FR/translation.json +++ b/apps/client/public/locales/fr-FR/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} vous a mentionné sur une page", "{{name}} gave you edit access to a page": "{{name}} vous a donné l'accès en modification à une page", "{{name}} gave you view access to a page": "{{name}} vous a donné l'accès en lecture à une page", + "{{name}} updated a page": "{{name}} a mis à jour une page.", + "Watch page": "Surveiller la page", + "Stop watching": "Ne plus surveiller", + "Email notifications": "Notifications par e-mail", + "Page updates": "Mises à jour de la page", + "Get notified when pages you watch are updated.": "Recevez une notification lorsque les pages que vous surveillez sont mises à jour.", + "Page mentions": "Mentions sur la page", + "Get notified when someone mentions you on a page.": "Recevez une notification lorsqu'une personne vous mentionne sur une page.", + "Comment mentions": "Mentions dans les commentaires", + "Get notified when someone mentions you in a comment.": "Recevez une notification lorsqu'une personne vous mentionne dans un commentaire.", + "New comments": "Nouveaux commentaires", + "Get notified about new comments on threads you participate in.": "Recevez une notification concernant les nouveaux commentaires dans les fils auxquels vous participez.", + "Resolved comments": "Commentaires résolus", + "Get notified when your comment is resolved.": "Recevez une notification lorsque votre commentaire est résolu.", + "You are now watching this page": "Vous surveillez désormais cette page", + "You are no longer watching this page": "Vous ne surveillez plus cette page", + "Direct": "Direct", + "Updates": "Mises à jour", "Today": "Aujourd'hui", "Yesterday": "Hier", "This week": "Cette semaine", diff --git a/apps/client/public/locales/it-IT/translation.json b/apps/client/public/locales/it-IT/translation.json index f69650f0..1bc59c3e 100644 --- a/apps/client/public/locales/it-IT/translation.json +++ b/apps/client/public/locales/it-IT/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} ti ha menzionato su una pagina", "{{name}} gave you edit access to a page": "{{name}} ti ha dato l'accesso di modifica a una pagina", "{{name}} gave you view access to a page": "{{name}} ti ha dato l'accesso di visualizzazione a una pagina", + "{{name}} updated a page": "{{name}} ha aggiornato una pagina.", + "Watch page": "Segui pagina", + "Stop watching": "Smetti di seguire", + "Email notifications": "Notifiche email", + "Page updates": "Aggiornamenti pagina", + "Get notified when pages you watch are updated.": "Ricevi una notifica quando le pagine che segui vengono aggiornate.", + "Page mentions": "Menzioni nella pagina", + "Get notified when someone mentions you on a page.": "Ricevi una notifica quando qualcuno ti menziona su una pagina.", + "Comment mentions": "Menzioni nei commenti", + "Get notified when someone mentions you in a comment.": "Ricevi una notifica quando qualcuno ti menziona in un commento.", + "New comments": "Nuovi commenti", + "Get notified about new comments on threads you participate in.": "Ricevi una notifica sui nuovi commenti nelle discussioni a cui partecipi.", + "Resolved comments": "Commenti risolti", + "Get notified when your comment is resolved.": "Ricevi una notifica quando il tuo commento viene risolto.", + "You are now watching this page": "Ora stai seguendo questa pagina", + "You are no longer watching this page": "Non stai più seguendo questa pagina", + "Direct": "Diretto", + "Updates": "Aggiornamenti", "Today": "Oggi", "Yesterday": "Ieri", "This week": "Questa settimana", diff --git a/apps/client/public/locales/ja-JP/translation.json b/apps/client/public/locales/ja-JP/translation.json index 1d913114..4e01d2c4 100644 --- a/apps/client/public/locales/ja-JP/translation.json +++ b/apps/client/public/locales/ja-JP/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}}さんがページであなたに言及しました", "{{name}} gave you edit access to a page": "{{name}}さんがページの編集権限をあなたに付与しました", "{{name}} gave you view access to a page": "{{name}}さんがページの閲覧権限をあなたに付与しました", + "{{name}} updated a page": "{{name}}さんがページを更新しました。", + "Watch page": "ページをウォッチ", + "Stop watching": "ウォッチを解除", + "Email notifications": "メール通知", + "Page updates": "ページの更新", + "Get notified when pages you watch are updated.": "ウォッチしているページが更新されたときに通知を受け取ります。", + "Page mentions": "ページでの言及", + "Get notified when someone mentions you on a page.": "誰かがページであなたに言及したとき通知を受け取ります。", + "Comment mentions": "コメントでの言及", + "Get notified when someone mentions you in a comment.": "誰かがコメントであなたに言及したとき通知を受け取ります。", + "New comments": "新しいコメント", + "Get notified about new comments on threads you participate in.": "参加しているスレッドに新しいコメントがあると通知されます。", + "Resolved comments": "解決済みコメント", + "Get notified when your comment is resolved.": "あなたのコメントが解決されたとき通知を受け取ります。", + "You are now watching this page": "このページをウォッチしています", + "You are no longer watching this page": "このページのウォッチを解除しました", + "Direct": "直接", + "Updates": "アップデート", "Today": "今日", "Yesterday": "昨日", "This week": "今週", diff --git a/apps/client/public/locales/ko-KR/translation.json b/apps/client/public/locales/ko-KR/translation.json index 24186a6f..f5bcd0df 100644 --- a/apps/client/public/locales/ko-KR/translation.json +++ b/apps/client/public/locales/ko-KR/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}}님이 페이지에서 당신을 언급했습니다", "{{name}} gave you edit access to a page": "{{name}}님이 페이지 편집 권한을 부여했습니다", "{{name}} gave you view access to a page": "{{name}}님이 페이지 조회 권한을 부여했습니다", + "{{name}} updated a page": "{{name}}님이 페이지를 업데이트했습니다.", + "Watch page": "페이지 구독", + "Stop watching": "구독 취소", + "Email notifications": "이메일 알림", + "Page updates": "페이지 업데이트", + "Get notified when pages you watch are updated.": "구독한 페이지가 업데이트될 때 알림을 받으세요.", + "Page mentions": "페이지 언급", + "Get notified when someone mentions you on a page.": "누군가가 페이지에서 당신을 언급하면 알림을 받으세요.", + "Comment mentions": "댓글 언급", + "Get notified when someone mentions you in a comment.": "누군가가 댓글에서 당신을 언급하면 알림을 받으세요.", + "New comments": "새 댓글", + "Get notified about new comments on threads you participate in.": "참여 중인 스레드에 새 댓글이 달리면 알림을 받으세요.", + "Resolved comments": "해결된 댓글", + "Get notified when your comment is resolved.": "내 댓글이 해결되었을 때 알림을 받으세요.", + "You are now watching this page": "이제 이 페이지를 주시합니다.", + "You are no longer watching this page": "더 이상 이 페이지를 주시하지 않습니다.", + "Direct": "직접", + "Updates": "업데이트", "Today": "오늘", "Yesterday": "어제", "This week": "이번 주", diff --git a/apps/client/public/locales/nl-NL/translation.json b/apps/client/public/locales/nl-NL/translation.json index f1349d0f..55349725 100644 --- a/apps/client/public/locales/nl-NL/translation.json +++ b/apps/client/public/locales/nl-NL/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} noemde je op een pagina", "{{name}} gave you edit access to a page": "{{name}} heeft je toegang gegeven om een pagina te bewerken", "{{name}} gave you view access to a page": "{{name}} heeft je toegang gegeven om een pagina te bekijken", + "{{name}} updated a page": "{{name}} heeft een pagina bijgewerkt.", + "Watch page": "Pagina volgen", + "Stop watching": "Volgen stoppen", + "Email notifications": "E-mailmeldingen", + "Page updates": "Pagina-updates", + "Get notified when pages you watch are updated.": "Ontvang een melding wanneer pagina's die je volgt worden bijgewerkt.", + "Page mentions": "Pagina-vermeldingen", + "Get notified when someone mentions you on a page.": "Ontvang een melding wanneer iemand je noemt op een pagina.", + "Comment mentions": "Vermeldingen in opmerkingen", + "Get notified when someone mentions you in a comment.": "Ontvang een melding wanneer iemand je noemt in een opmerking.", + "New comments": "Nieuwe opmerkingen", + "Get notified about new comments on threads you participate in.": "Ontvang meldingen over nieuwe reacties in threads waaraan je deelneemt.", + "Resolved comments": "Opgeloste opmerkingen", + "Get notified when your comment is resolved.": "Ontvang een melding wanneer je reactie is opgelost.", + "You are now watching this page": "Je volgt nu deze pagina", + "You are no longer watching this page": "Je volgt deze pagina niet meer", + "Direct": "Direct", + "Updates": "Updates", "Today": "Vandaag", "Yesterday": "Gisteren", "This week": "Deze week", diff --git a/apps/client/public/locales/pt-BR/translation.json b/apps/client/public/locales/pt-BR/translation.json index 04ae0830..598f6d79 100644 --- a/apps/client/public/locales/pt-BR/translation.json +++ b/apps/client/public/locales/pt-BR/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} mencionou você em uma página", "{{name}} gave you edit access to a page": "{{name}} concedeu acesso de edição a uma página", "{{name}} gave you view access to a page": "{{name}} concedeu acesso de visualização a uma página", + "{{name}} updated a page": "{{name}} atualizou uma página.", + "Watch page": "Observar página", + "Stop watching": "Parar de observar", + "Email notifications": "Notificações por e-mail", + "Page updates": "Atualizações da página", + "Get notified when pages you watch are updated.": "Receba notificações quando as páginas que você observa forem atualizadas.", + "Page mentions": "Menções na página", + "Get notified when someone mentions you on a page.": "Receba notificações quando alguém mencionar você em uma página.", + "Comment mentions": "Menções em comentários", + "Get notified when someone mentions you in a comment.": "Receba notificações quando alguém mencionar você em um comentário.", + "New comments": "Novos comentários", + "Get notified about new comments on threads you participate in.": "Receba notificações sobre novos comentários nas discussões em que você participa.", + "Resolved comments": "Comentários resolvidos", + "Get notified when your comment is resolved.": "Receba notificações quando seu comentário for resolvido.", + "You are now watching this page": "Agora você está observando esta página", + "You are no longer watching this page": "Você não está mais observando esta página", + "Direct": "Direto", + "Updates": "Atualizações", "Today": "Hoje", "Yesterday": "Ontem", "This week": "Esta semana", diff --git a/apps/client/public/locales/ru-RU/translation.json b/apps/client/public/locales/ru-RU/translation.json index 02a4b861..c0d3f3d4 100644 --- a/apps/client/public/locales/ru-RU/translation.json +++ b/apps/client/public/locales/ru-RU/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} упомянул вас на странице", "{{name}} gave you edit access to a page": "{{name}} предоставил вам доступ для редактирования страницы", "{{name}} gave you view access to a page": "{{name}} предоставил вам доступ к просмотру страницы", + "{{name}} updated a page": "{{name}} обновил страницу.", + "Watch page": "Следить за страницей", + "Stop watching": "Прекратить отслеживание", + "Email notifications": "Уведомления на email", + "Page updates": "Обновления страницы", + "Get notified when pages you watch are updated.": "Получайте уведомления, когда отслеживаемые вами страницы обновляются.", + "Page mentions": "Упоминания на странице", + "Get notified when someone mentions you on a page.": "Получайте уведомления, когда кто-то упоминает вас на странице.", + "Comment mentions": "Упоминания в комментариях", + "Get notified when someone mentions you in a comment.": "Получайте уведомления, когда кто-то упоминает вас в комментарии.", + "New comments": "Новые комментарии", + "Get notified about new comments on threads you participate in.": "Получайте уведомления о новых комментариях в цепочках, в которых вы участвуете.", + "Resolved comments": "Разрешённые комментарии", + "Get notified when your comment is resolved.": "Получайте уведомление, когда ваш комментарий разрешён.", + "You are now watching this page": "Вы теперь следите за этой страницей", + "You are no longer watching this page": "Вы больше не следите за этой страницей", + "Direct": "Прямые", + "Updates": "Обновления", "Today": "Сегодня", "Yesterday": "Вчера", "This week": "На этой неделе", diff --git a/apps/client/public/locales/uk-UA/translation.json b/apps/client/public/locales/uk-UA/translation.json index 194cf161..02c4a4bd 100644 --- a/apps/client/public/locales/uk-UA/translation.json +++ b/apps/client/public/locales/uk-UA/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}} згадав вас на сторінці", "{{name}} gave you edit access to a page": "{{name}} надав вам доступ до редагування сторінки", "{{name}} gave you view access to a page": "{{name}} надав вам доступ до перегляду сторінки", + "{{name}} updated a page": "{{name}} оновив сторінку.", + "Watch page": "Стежити за сторінкою", + "Stop watching": "Припинити стежити", + "Email notifications": "Сповіщення електронною поштою", + "Page updates": "Оновлення сторінки", + "Get notified when pages you watch are updated.": "Отримуйте сповіщення, коли сторінки, за якими ви стежите, оновлюються.", + "Page mentions": "Згадки на сторінці", + "Get notified when someone mentions you on a page.": "Отримуйте сповіщення, коли хтось згадує вас на сторінці.", + "Comment mentions": "Згадки у коментарях", + "Get notified when someone mentions you in a comment.": "Отримуйте сповіщення, коли хтось згадує вас у коментарі.", + "New comments": "Нові коментарі", + "Get notified about new comments on threads you participate in.": "Отримуйте сповіщення про нові коментарі у темах, у яких ви берете участь.", + "Resolved comments": "Вирішені коментарі", + "Get notified when your comment is resolved.": "Отримайте сповіщення, коли ваш коментар вирішено.", + "You are now watching this page": "Ви зараз стежите за цією сторінкою", + "You are no longer watching this page": "Ви більше не стежите за цією сторінкою", + "Direct": "Прямі", + "Updates": "Оновлення", "Today": "Сьогодні", "Yesterday": "Вчора", "This week": "Цього тижня", diff --git a/apps/client/public/locales/zh-CN/translation.json b/apps/client/public/locales/zh-CN/translation.json index 13f4f334..a4ac6bd3 100644 --- a/apps/client/public/locales/zh-CN/translation.json +++ b/apps/client/public/locales/zh-CN/translation.json @@ -674,6 +674,24 @@ "{{name}} mentioned you on a page": "{{name}}在页面上提到你", "{{name}} gave you edit access to a page": "{{name}}授予你页面编辑权限", "{{name}} gave you view access to a page": "{{name}}授予你页面查看权限", + "{{name}} updated a page": "{{name}}更新了一个页面。", + "Watch page": "关注页面", + "Stop watching": "取消关注", + "Email notifications": "邮件通知", + "Page updates": "页面更新", + "Get notified when pages you watch are updated.": "当你关注的页面有更新时收到通知。", + "Page mentions": "页面提及", + "Get notified when someone mentions you on a page.": "当有人在页面上提到你时收到通知。", + "Comment mentions": "评论提及", + "Get notified when someone mentions you in a comment.": "当有人在评论中提到你时收到通知。", + "New comments": "新评论", + "Get notified about new comments on threads you participate in.": "当你参与的讨论有新评论时收到通知。", + "Resolved comments": "已解决的评论", + "Get notified when your comment is resolved.": "当你的评论被解决时收到通知。", + "You are now watching this page": "你现在正在关注此页面", + "You are no longer watching this page": "你已取消关注此页面", + "Direct": "直接", + "Updates": "更新", "Today": "今天", "Yesterday": "昨天", "This week": "本周", From 895c1817ae13a3b47bd0d25a09e226964f0aa828 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Sun, 5 Apr 2026 13:45:36 +0100 Subject: [PATCH 40/44] feat: bug fixes (#2084) * handle enter in inline code * fix: duplicate comment cache * track link nodes (backlinks) * fix en-US translation * fix internal a-links * overrides * 0.71.1 --- apps/client/package.json | 2 +- .../public/locales/en-US/translation.json | 518 +++++++++--------- .../features/comment/queries/comment-query.ts | 5 + .../features/editor/extensions/extensions.ts | 19 + apps/server/package.json | 2 +- .../processors/history.processor.ts | 3 + .../src/common/helpers/prosemirror/utils.ts | 25 + .../src/core/page/services/page.service.ts | 31 ++ .../queue/constants/queue.interface.ts | 1 + .../queue/tasks/backlinks.task.ts | 33 +- package.json | 9 +- pnpm-lock.yaml | 433 +++++++-------- 12 files changed, 593 insertions(+), 488 deletions(-) diff --git a/apps/client/package.json b/apps/client/package.json index 4b767f60..22192e56 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -1,7 +1,7 @@ { "name": "client", "private": true, - "version": "0.71.0", + "version": "0.71.1", "scripts": { "dev": "vite", "build": "tsc && vite build", diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 171ba7b9..3ecf103b 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -1,7 +1,7 @@ { - "Account": "Account ", + "Account": "Account", "Active": "Active", - "Add": "Add.", + "Add": "Add", "Add group members": "Add group members", "Add groups": "Add groups", "Add members": "Add members", @@ -44,24 +44,24 @@ "Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.": "Are you sure you want to delete this page? This will delete its children and page history. This action is irreversible.", "Description": "Description", "Details": "Details", - "e.g ACME": "e.g. ACME", - "e.g ACME Inc": "e.g. ACME Inc", - "e.g Developers": "e.g. Developers", - "e.g Group for developers": "e.g. Group for developers", - "e.g product": "e.g. product", - "e.g Product Team": "e.g. Product Team", - "e.g Sales": "e.g. Sales", - "e.g Space for product team": "e.g. Space for product team", - "e.g Space for sales team to collaborate": "e.g. Space for sales team to collaborate", + "e.g ACME": "e.g ACME", + "e.g ACME Inc": "e.g ACME Inc", + "e.g Developers": "e.g Developers", + "e.g Group for developers": "e.g Group for developers", + "e.g product": "e.g product", + "e.g Product Team": "e.g Product Team", + "e.g Sales": "e.g Sales", + "e.g Space for product team": "e.g Space for product team", + "e.g Space for sales team to collaborate": "e.g Space for sales team to collaborate", "Edit": "Edit", - "Read": "Read.", + "Read": "Read", "Edit group": "Edit group", "Email": "Email", "Enter a strong password": "Enter a strong password", "Enter valid email addresses separated by comma or space max_50": "Enter valid email addresses separated by comma or space [max: 50]", - "enter valid emails addresses": "Enter valid email addresses", + "enter valid emails addresses": "enter valid emails addresses", "Enter your current password": "Enter your current password", - "enter your full name": "Enter your full name", + "enter your full name": "enter your full name", "Enter your new password": "Enter your new password", "Enter your new preferred email": "Enter your new preferred email", "Enter your password": "Enter your password", @@ -87,7 +87,7 @@ "Import pages": "Import pages", "Import pages & space settings": "Import pages & space settings", "Importing pages": "Importing pages", - "invalid invitation link": "Invalid invitation link", + "invalid invitation link": "invalid invitation link", "Invitation signup": "Invitation signup", "Invite by email": "Invite by email", "Invite members": "Invite members", @@ -113,7 +113,7 @@ "New email": "New email", "New page": "New page", "New password": "New password", - "No group found": "No group found.", + "No group found": "No group found", "No page history saved yet.": "No page history saved yet.", "No pages yet": "No pages yet", "No shared pages": "No shared pages", @@ -149,56 +149,56 @@ "Search for users": "Search for users", "Search for users and groups": "Search for users and groups", "Search...": "Search...", - "Select language": "Select language.", - "Select role": "Select role.", - "Select role to assign to all invited members": "Select role to assign to all invited members.", - "Select theme": "Select theme.", - "Send invitation": "Send invitation.", - "Invitation sent": "Invitation sent.", - "Settings": "Settings.", - "Setup workspace": "Setup workspace.", - "Sign In": "Sign In.", - "Sign Up": "Sign Up.", - "Slug": "Slug.", - "Space": "Space.", - "Space description": "Space description.", - "Space menu": "Space menu.", - "Space name": "Space name.", - "Space settings": "Space settings.", - "Space slug": "Space slug.", - "Spaces": "Spaces.", - "Spaces you belong to": "Spaces you belong to.", - "No space found": "No space found.", - "Search for spaces": "Search for spaces.", + "Select language": "Select language", + "Select role": "Select role", + "Select role to assign to all invited members": "Select role to assign to all invited members", + "Select theme": "Select theme", + "Send invitation": "Send invitation", + "Invitation sent": "Invitation sent", + "Settings": "Settings", + "Setup workspace": "Setup workspace", + "Sign In": "Sign In", + "Sign Up": "Sign Up", + "Slug": "Slug", + "Space": "Space", + "Space description": "Space description", + "Space menu": "Space menu", + "Space name": "Space name", + "Space settings": "Space settings", + "Space slug": "Space slug", + "Spaces": "Spaces", + "Spaces you belong to": "Spaces you belong to", + "No space found": "No space found", + "Search for spaces": "Search for spaces", "Start typing to search...": "Start typing to search...", - "Status": "Status.", - "Successfully imported": "Successfully imported.", - "Successfully restored": "Successfully restored.", - "System settings": "System settings.", - "Theme": "Theme.", + "Status": "Status", + "Successfully imported": "Successfully imported", + "Successfully restored": "Successfully restored", + "System settings": "System settings", + "Theme": "Theme", "To change your email, you have to enter your password and new email.": "To change your email, you have to enter your password and new email.", - "Toggle full page width": "Toggle full page width.", + "Toggle full page width": "Toggle full page width", "Unable to import pages. Please try again.": "Unable to import pages. Please try again.", - "untitled": "untitled.", - "Untitled": "Untitled.", - "Updated successfully": "Updated successfully.", - "User": "User.", - "Workspace": "Workspace.", - "Workspace Name": "Workspace name.", - "Workspace settings": "Workspace settings.", + "untitled": "untitled", + "Untitled": "Untitled", + "Updated successfully": "Updated successfully", + "User": "User", + "Workspace": "Workspace", + "Workspace Name": "Workspace Name", + "Workspace settings": "Workspace settings", "You can change your password here.": "You can change your password here.", - "Your Email": "Your email.", + "Your Email": "Your Email", "Your import is complete.": "Your import is complete.", - "Your name": "Your name.", - "Your Name": "Your name.", - "Your password": "Your password.", + "Your name": "Your name", + "Your Name": "Your Name", + "Your password": "Your password", "Your password must be a minimum of 8 characters.": "Your password must be a minimum of 8 characters.", - "Sidebar toggle": "Sidebar toggle.", - "Comments": "Comments.", - "404 page not found": "404 page not found.", + "Sidebar toggle": "Sidebar toggle", + "Comments": "Comments", + "404 page not found": "404 page not found", "Sorry, we can't find the page you are looking for.": "Sorry, we can't find the page you are looking for.", - "Take me back to homepage": "Take me back to the homepage.", - "Forgot password": "Forgot password.", + "Take me back to homepage": "Take me back to homepage", + "Forgot password": "Forgot password", "Forgot your password?": "Forgot your password?", "A password reset link has been sent to your email. Please check your inbox.": "A password reset link has been sent to your email. Please check your inbox.", "Send reset link": "Send reset link", @@ -222,16 +222,16 @@ "Comment deleted successfully": "Comment deleted successfully", "Failed to delete comment": "Failed to delete comment", "Comment resolved successfully": "Comment resolved successfully", - "Comment re-opened successfully": "Comment re-opened successfully.", - "Comment unresolved successfully": "Comment marked as unresolved successfully.", + "Comment re-opened successfully": "Comment re-opened successfully", + "Comment unresolved successfully": "Comment unresolved successfully", "Failed to resolve comment": "Failed to resolve comment", - "Resolve comment": "Resolve comment.", - "Unresolve comment": "Mark comment as unresolved.", - "Resolve Comment Thread": "Resolve comment thread.", - "Unresolve Comment Thread": "Mark comment thread as unresolved.", + "Resolve comment": "Resolve comment", + "Unresolve comment": "Unresolve comment", + "Resolve Comment Thread": "Resolve Comment Thread", + "Unresolve Comment Thread": "Unresolve Comment Thread", "Are you sure you want to resolve this comment thread? This will mark it as completed.": "Are you sure you want to resolve this comment thread? This will mark it as completed.", "Are you sure you want to unresolve this comment thread?": "Are you sure you want to unresolve this comment thread?", - "Resolved": "Resolved.", + "Resolved": "Resolved", "No active comments.": "No active comments.", "Revoke invitation": "Revoke invitation", "Revoke": "Revoke", @@ -241,9 +241,9 @@ "Anyone with this link can join this workspace.": "Anyone with this link can join this workspace.", "Invite link": "Invite link", "Copy": "Copy", - "Copy to space": "Copy to space.", + "Copy to space": "Copy to space", "Copied": "Copied", - "Duplicate": "Duplicate.", + "Duplicate": "Duplicate", "Select a user": "Select a user", "Select a group": "Select a group", "Export all pages and attachments in this space.": "Export all pages and attachments in this space.", @@ -251,7 +251,7 @@ "Are you sure you want to delete this space?": "Are you sure you want to delete this space?", "Delete this space with all its pages and data.": "Delete this space with all its pages and data.", "All pages, comments, attachments and permissions in this space will be deleted irreversibly.": "All pages, comments, attachments and permissions in this space will be deleted irreversibly.", - "Confirm space name": "Confirm space name.", + "Confirm space name": "Confirm space name", "Type the space name {{spaceName}} to confirm your action.": "Type the space name {{spaceName}} to confirm your action.", "Format": "Format", "Include subpages": "Include subpages", @@ -267,7 +267,7 @@ "Align left": "Align left", "Align right": "Align right", "Align center": "Align center", - "Justify": "Justify.", + "Justify": "Justify", "Merge cells": "Merge cells", "Split cell": "Split cell", "Delete column": "Delete column", @@ -312,7 +312,7 @@ "Pink": "Pink", "Gray": "Gray", "Embed link": "Embed link", - "Invalid {{provider}} embed link": "Invalid {{provider}} embed link.", + "Invalid {{provider}} embed link": "Invalid {{provider}} embed link", "Embed {{provider}}": "Embed {{provider}}", "Enter {{provider}} link to embed": "Enter {{provider}} link to embed", "Bold": "Bold", @@ -345,41 +345,41 @@ "Upload any file from your device.": "Upload any file from your device.", "Uploading {{name}}": "Uploading {{name}}", "Uploading file": "Uploading file", - "Table": "Table.", + "Table": "Table", "Insert a table.": "Insert a table.", "Insert collapsible block.": "Insert collapsible block.", - "Video": "Video.", - "Divider": "Divider.", - "Quote": "Quote.", - "Image": "Image.", - "Audio": "Audio.", + "Video": "Video", + "Divider": "Divider", + "Quote": "Quote", + "Image": "Image", + "Audio": "Audio", "Embed PDF": "Embed PDF", "Upload and embed a PDF file.": "Upload and embed a PDF file.", "Embed as PDF": "Embed as PDF", "Failed to load PDF": "Failed to load PDF", "Convert to attachment": "Convert to attachment", - "File attachment": "File attachment.", - "Toggle block": "Toggle block.", - "Callout": "Callout.", + "File attachment": "File attachment", + "Toggle block": "Toggle block", + "Callout": "Callout", "Insert callout notice.": "Insert callout notice.", - "Math inline": "Math inline.", + "Math inline": "Math inline", "Insert inline math equation.": "Insert inline math equation.", - "Math block": "Math block.", - "Insert math equation": "Insert math equation.", - "Mermaid diagram": "Mermaid diagram.", - "Insert mermaid diagram": "Insert mermaid diagram.", - "Insert and design Drawio diagrams": "Insert and design Drawio diagrams.", - "Insert current date": "Insert current date.", - "Draw and sketch excalidraw diagrams": "Draw and sketch Excalidraw diagrams.", - "Multiple": "Multiple.", + "Math block": "Math block", + "Insert math equation": "Insert math equation", + "Mermaid diagram": "Mermaid diagram", + "Insert mermaid diagram": "Insert mermaid diagram", + "Insert and design Drawio diagrams": "Insert and design Drawio diagrams", + "Insert current date": "Insert current date", + "Draw and sketch excalidraw diagrams": "Draw and sketch excalidraw diagrams", + "Multiple": "Multiple", "Turn into": "Turn into", "Text align": "Text align", "This page may have been deleted, moved, or you may not have access.": "This page may have been deleted, moved, or you may not have access.", "Go to homepage": "Go to homepage", "Pages you create will show up here.": "Pages you create will show up here.", - "Heading {{level}}": "Heading {{level}}.", - "Toggle title": "Toggle title.", - "Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands.", + "Heading {{level}}": "Heading {{level}}", + "Toggle title": "Toggle title", + "Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands", "Write...": "Write...", "Column count": "Column count", "{{count}} Columns": "{{count}} Columns", @@ -389,27 +389,27 @@ "Wide center": "Wide center", "Left wide": "Left wide", "Right wide": "Right wide", - "Names do not match": "Names do not match.", - "Today, {{time}}": "Today, {{time}}.", - "Yesterday, {{time}}": "Yesterday, {{time}}.", - "Space created successfully": "Space created successfully.", - "Space updated successfully": "Space updated successfully.", - "Space deleted successfully": "Space deleted successfully.", - "Members added successfully": "Members added successfully.", - "Member removed successfully": "Member removed successfully.", - "Member role updated successfully": "Member role updated successfully.", - "Created by: {{creatorName}}": "Created by: {{creatorName}}.", - "Created at: {{time}}": "Created at: {{time}}.", - "Edited by {{name}} {{time}}": "Edited by {{name}} {{time}}.", - "Word count: {{wordCount}}": "Word count: {{wordCount}}.", - "Character count: {{characterCount}}": "Character count: {{characterCount}}.", - "New update": "New update.", - "{{latestVersion}} is available": "{{latestVersion}} is available.", - "Default page edit mode": "Default page edit mode.", + "Names do not match": "Names do not match", + "Today, {{time}}": "Today, {{time}}", + "Yesterday, {{time}}": "Yesterday, {{time}}", + "Space created successfully": "Space created successfully", + "Space updated successfully": "Space updated successfully", + "Space deleted successfully": "Space deleted successfully", + "Members added successfully": "Members added successfully", + "Member removed successfully": "Member removed successfully", + "Member role updated successfully": "Member role updated successfully", + "Created by: {{creatorName}}": "Created by: {{creatorName}}", + "Created at: {{time}}": "Created at: {{time}}", + "Edited by {{name}} {{time}}": "Edited by {{name}} {{time}}", + "Word count: {{wordCount}}": "Word count: {{wordCount}}", + "Character count: {{characterCount}}": "Character count: {{characterCount}}", + "New update": "New update", + "{{latestVersion}} is available": "{{latestVersion}} is available", + "Default page edit mode": "Default page edit mode", "Choose your preferred page edit mode. Avoid accidental edits.": "Choose your preferred page edit mode. Avoid accidental edits.", - "Reading": "Reading.", - "Delete member": "Delete member.", - "Member deleted successfully": "Member deleted successfully.", + "Reading": "Reading", + "Delete member": "Delete member", + "Member deleted successfully": "Member deleted successfully", "Are you sure you want to delete this workspace member? This action is irreversible.": "Are you sure you want to delete this workspace member? This action is irreversible.", "Deactivate member": "Deactivate member", "Activate member": "Activate member", @@ -418,33 +418,33 @@ "Deactivate": "Deactivate", "Activate": "Activate", "Deactivated": "Deactivated", - "Move": "Move.", - "Move page": "Move page.", + "Move": "Move", + "Move page": "Move page", "Move page to a different space.": "Move page to a different space.", "Real-time editor connection lost. Retrying...": "Real-time editor connection lost. Retrying...", - "Table of contents": "Table of contents.", + "Table of contents": "Table of contents", "Add headings (H1, H2, H3) to generate a table of contents.": "Add headings (H1, H2, H3) to generate a table of contents.", - "Share": "Share.", - "Public sharing": "Public sharing.", - "Shared by": "Shared by.", - "Shared at": "Shared at.", - "Inherits public sharing from": "Inherits public sharing from.", - "Share to web": "Share to web.", - "Shared to web": "Shared to web.", - "Anyone with the link can view this page": "Anyone with the link can view this page.", - "Make this page publicly accessible": "Make this page publicly accessible.", - "Include sub-pages": "Include sub-pages.", - "Make sub-pages public too": "Make sub-pages public too.", - "Allow search engines to index page": "Allow search engines to index page.", - "Open page": "Open page.", - "Page": "Page.", - "Delete public share link": "Delete public share link.", - "Delete share": "Delete share.", + "Share": "Share", + "Public sharing": "Public sharing", + "Shared by": "Shared by", + "Shared at": "Shared at", + "Inherits public sharing from": "Inherits public sharing from", + "Share to web": "Share to web", + "Shared to web": "Shared to web", + "Anyone with the link can view this page": "Anyone with the link can view this page", + "Make this page publicly accessible": "Make this page publicly accessible", + "Include sub-pages": "Include sub-pages", + "Make sub-pages public too": "Make sub-pages public too", + "Allow search engines to index page": "Allow search engines to index page", + "Open page": "Open page", + "Page": "Page", + "Delete public share link": "Delete public share link", + "Delete share": "Delete share", "Are you sure you want to delete this shared link?": "Are you sure you want to delete this shared link?", - "Publicly shared pages from spaces you are a member of will appear here": "Publicly shared pages from spaces you are a member of will appear here.", - "Share deleted successfully": "Share deleted successfully.", - "Share not found": "Share not found.", - "Failed to share page": "Failed to share page.", + "Publicly shared pages from spaces you are a member of will appear here": "Publicly shared pages from spaces you are a member of will appear here", + "Share deleted successfully": "Share deleted successfully", + "Share not found": "Share not found", + "Failed to share page": "Failed to share page", "Disable public sharing": "Disable public sharing", "Prevent members from sharing pages publicly.": "Prevent members from sharing pages publicly.", "Toggle public sharing": "Toggle public sharing", @@ -464,135 +464,135 @@ "Public sharing is disabled": "Public sharing is disabled", "Public sharing has been disabled at the workspace level.": "Public sharing has been disabled at the workspace level.", "Public sharing has been disabled for this space.": "Public sharing has been disabled for this space.", - "Copy page": "Copy page.", + "Copy page": "Copy page", "Copy page to a different space.": "Copy page to a different space.", - "Page copied successfully": "Page copied successfully.", - "Page duplicated successfully": "Page duplicated successfully.", - "Find": "Find.", - "Not found": "Not found.", - "Previous Match (Shift+Enter)": "Previous Match (Shift+Enter).", - "Next match (Enter)": "Next match (Enter).", - "Match case (Alt+C)": "Match case (Alt+C).", - "Replace": "Replace.", - "Close (Escape)": "Close (Escape).", - "Replace (Enter)": "Replace (Enter).", - "Replace all (Ctrl+Alt+Enter)": "Replace all (Ctrl+Alt+Enter).", - "Replace all": "Replace all.", - "View all spaces": "View all spaces.", - "Error": "Error.", - "Failed to disable MFA": "Failed to disable MFA.", - "Disable two-factor authentication": "Disable two-factor authentication.", + "Page copied successfully": "Page copied successfully", + "Page duplicated successfully": "Page duplicated successfully", + "Find": "Find", + "Not found": "Not found", + "Previous Match (Shift+Enter)": "Previous Match (Shift+Enter)", + "Next match (Enter)": "Next match (Enter)", + "Match case (Alt+C)": "Match case (Alt+C)", + "Replace": "Replace", + "Close (Escape)": "Close (Escape)", + "Replace (Enter)": "Replace (Enter)", + "Replace all (Ctrl+Alt+Enter)": "Replace all (Ctrl+Alt+Enter)", + "Replace all": "Replace all", + "View all spaces": "View all spaces", + "Error": "Error", + "Failed to disable MFA": "Failed to disable MFA", + "Disable two-factor authentication": "Disable two-factor authentication", "Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.": "Disabling two-factor authentication will make your account less secure. You'll only need your password to sign in.", "Please enter your password to disable two-factor authentication:": "Please enter your password to disable two-factor authentication:", - "Two-factor authentication has been enabled": "Two-factor authentication has been enabled.", - "Two-factor authentication has been disabled": "Two-factor authentication has been disabled.", - "2-step verification": "2-step verification.", + "Two-factor authentication has been enabled": "Two-factor authentication has been enabled", + "Two-factor authentication has been disabled": "Two-factor authentication has been disabled", + "2-step verification": "2-step verification", "Protect your account with an additional verification layer when signing in.": "Protect your account with an additional verification layer when signing in.", "Two-factor authentication is active on your account.": "Two-factor authentication is active on your account.", - "Add 2FA method": "Add 2FA method.", - "Backup codes": "Backup codes.", - "Disable": "Disable.", - "Invalid verification code": "Invalid verification code.", - "New backup codes have been generated": "New backup codes have been generated.", - "Failed to regenerate backup codes": "Failed to regenerate backup codes.", - "About backup codes": "About backup codes.", + "Add 2FA method": "Add 2FA method", + "Backup codes": "Backup codes", + "Disable": "Disable", + "Invalid verification code": "Invalid verification code", + "New backup codes have been generated": "New backup codes have been generated", + "Failed to regenerate backup codes": "Failed to regenerate backup codes", + "About backup codes": "About backup codes", "Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "Backup codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.", "You can regenerate new backup codes at any time. This will invalidate all existing codes.": "You can regenerate new backup codes at any time. This will invalidate all existing codes.", - "Confirm password": "Confirm password.", - "Generate new backup codes": "Generate new backup codes.", - "Save your new backup codes": "Save your new backup codes.", + "Confirm password": "Confirm password", + "Generate new backup codes": "Generate new backup codes", + "Save your new backup codes": "Save your new backup codes", "Make sure to save these codes in a secure place. Your old backup codes are no longer valid.": "Make sure to save these codes in a secure place. Your old backup codes are no longer valid.", - "Your new backup codes": "Your new backup codes.", - "I've saved my backup codes": "I've saved my backup codes.", - "Failed to setup MFA": "Failed to setup MFA.", - "Setup & Verify": "Setup & Verify.", - "Add to authenticator": "Add to authenticator.", - "1. Scan this QR code with your authenticator app": "1. Scan this QR code with your authenticator app.", + "Your new backup codes": "Your new backup codes", + "I've saved my backup codes": "I've saved my backup codes", + "Failed to setup MFA": "Failed to setup MFA", + "Setup & Verify": "Setup & Verify", + "Add to authenticator": "Add to authenticator", + "1. Scan this QR code with your authenticator app": "1. Scan this QR code with your authenticator app", "Can't scan the code?": "Can't scan the code?", "Enter this code manually in your authenticator app:": "Enter this code manually in your authenticator app:", - "2. Enter the 6-digit code from your authenticator": "2. Enter the 6-digit code from your authenticator.", - "Verify and enable": "Verify and enable.", + "2. Enter the 6-digit code from your authenticator": "2. Enter the 6-digit code from your authenticator", + "Verify and enable": "Verify and enable", "Failed to generate QR code. Please try again.": "Failed to generate QR code. Please try again.", - "Backup": "Backup.", - "Save codes": "Save codes.", - "Save your backup codes": "Save your backup codes.", + "Backup": "Backup", + "Save codes": "Save codes", + "Save your backup codes": "Save your backup codes", "These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.": "These codes can be used to access your account if you lose access to your authenticator app. Each code can only be used once.", - "Print": "Print.", + "Print": "Print", "Two-factor authentication has been set up. Please log in again.": "Two-factor authentication has been set up. Please log in again.", - "Two-Factor authentication required": "Two-factor authentication required.", - "Your workspace requires two-factor authentication for all users": "Your workspace requires two-factor authentication for all users.", + "Two-Factor authentication required": "Two-factor authentication required", + "Your workspace requires two-factor authentication for all users": "Your workspace requires two-factor authentication for all users", "To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.": "To continue accessing your workspace, you must set up two-factor authentication. This adds an extra layer of security to your account.", - "Set up two-factor authentication": "Set up two-factor authentication.", - "Cancel and logout": "Cancel and logout.", + "Set up two-factor authentication": "Set up two-factor authentication", + "Cancel and logout": "Cancel and logout", "Your workspace requires two-factor authentication. Please set it up to continue.": "Your workspace requires two-factor authentication. Please set it up to continue.", "This adds an extra layer of security to your account by requiring a verification code from your authenticator app.": "This adds an extra layer of security to your account by requiring a verification code from your authenticator app.", - "Password is required": "Password is required.", - "Password must be at least 8 characters": "Password must be at least 8 characters.", - "Please enter a 6-digit code": "Please enter a 6-digit code.", - "Code must be exactly 6 digits": "Code must be exactly 6 digits.", - "Enter the 6-digit code found in your authenticator app": "Enter the 6-digit code found in your authenticator app.", + "Password is required": "Password is required", + "Password must be at least 8 characters": "Password must be at least 8 characters", + "Please enter a 6-digit code": "Please enter a 6-digit code", + "Code must be exactly 6 digits": "Code must be exactly 6 digits", + "Enter the 6-digit code found in your authenticator app": "Enter the 6-digit code found in your authenticator app", "Need help authenticating?": "Need help authenticating?", - "MFA QR Code": "MFA QR Code.", + "MFA QR Code": "MFA QR Code", "Account created successfully. Please log in to set up two-factor authentication.": "Account created successfully. Please log in to set up two-factor authentication.", "Password reset successful. Please log in with your new password and complete two-factor authentication.": "Password reset successful. Please log in with your new password and complete two-factor authentication.", "Password reset successful. Please log in with your new password to set up two-factor authentication.": "Password reset successful. Please log in with your new password to set up two-factor authentication.", "Password reset was successful. Please log in with your new password.": "Password reset was successful. Please log in with your new password.", - "Two-factor authentication": "Two-factor authentication.", - "Use authenticator app instead": "Use authenticator app instead.", - "Verify backup code": "Verify backup code.", - "Use backup code": "Use backup code.", - "Enter one of your backup codes": "Enter one of your backup codes.", - "Backup code": "Backup code.", + "Two-factor authentication": "Two-factor authentication", + "Use authenticator app instead": "Use authenticator app instead", + "Verify backup code": "Verify backup code", + "Use backup code": "Use backup code", + "Enter one of your backup codes": "Enter one of your backup codes", + "Backup code": "Backup code", "Enter one of your backup codes. Each backup code can only be used once.": "Enter one of your backup codes. Each backup code can only be used once.", - "Verify": "Verify.", - "Trash": "Trash.", + "Verify": "Verify", + "Trash": "Trash", "Pages in trash will be permanently deleted after {{count}} days.": "Pages in trash will be permanently deleted after {{count}} days.", - "Deleted": "Deleted.", - "No pages in trash": "No pages in trash.", + "Deleted": "Deleted", + "No pages in trash": "No pages in trash", "Permanently delete page?": "Permanently delete page?", "Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.": "Are you sure you want to permanently delete '{{title}}'? This action cannot be undone.", "Restore '{{title}}' and its sub-pages?": "Restore '{{title}}' and its sub-pages?", - "Move to trash": "Move to trash.", + "Move to trash": "Move to trash", "Move this page to trash?": "Move this page to trash?", - "Restore page": "Restore page.", - "Page moved to trash": "Page moved to trash.", - "Page restored successfully": "Page restored successfully.", - "Deleted by": "Deleted by.", - "Deleted at": "Deleted at.", - "Preview": "Preview.", - "Subpages": "Subpages.", - "Failed to load subpages": "Failed to load subpages.", - "No subpages": "No subpages.", - "Subpages (Child pages)": "Subpages (Child pages).", - "List all subpages of the current page": "List all subpages of the current page.", - "Attachments": "Attachments.", - "All spaces": "All spaces.", - "Unknown": "Unknown.", - "Find a space": "Find a space.", - "Search in all your spaces": "Search in all your spaces.", - "Type": "Type.", - "Enterprise": "Enterprise.", - "Download attachment": "Download attachment.", - "Allowed email domains": "Allowed email domains.", - "Only users with email addresses from these domains can signup via SSO.": "Only users with email addresses from these domains can sign up via SSO.", - "Enter valid domain names separated by comma or space": "Enter valid domain names separated by comma or space.", - "Enforce two-factor authentication": "Enforce two-factor authentication.", + "Restore page": "Restore page", + "Page moved to trash": "Page moved to trash", + "Page restored successfully": "Page restored successfully", + "Deleted by": "Deleted by", + "Deleted at": "Deleted at", + "Preview": "Preview", + "Subpages": "Subpages", + "Failed to load subpages": "Failed to load subpages", + "No subpages": "No subpages", + "Subpages (Child pages)": "Subpages (Child pages)", + "List all subpages of the current page": "List all subpages of the current page", + "Attachments": "Attachments", + "All spaces": "All spaces", + "Unknown": "Unknown", + "Find a space": "Find a space", + "Search in all your spaces": "Search in all your spaces", + "Type": "Type", + "Enterprise": "Enterprise", + "Download attachment": "Download attachment", + "Allowed email domains": "Allowed email domains", + "Only users with email addresses from these domains can signup via SSO.": "Only users with email addresses from these domains can signup via SSO.", + "Enter valid domain names separated by comma or space": "Enter valid domain names separated by comma or space", + "Enforce two-factor authentication": "Enforce two-factor authentication", "Once enforced, all members must enable two-factor authentication to access the workspace.": "Once enforced, all members must enable two-factor authentication to access the workspace.", - "Toggle MFA enforcement": "Toggle MFA enforcement.", - "Display name": "Display name.", - "Allow signup": "Allow signup.", - "Enabled": "Enabled.", - "Advanced Settings": "Advanced Settings.", - "Enable TLS/SSL": "Enable TLS/SSL.", - "Use secure connection to LDAP server": "Use secure connection to LDAP server.", - "Group sync": "Group sync.", + "Toggle MFA enforcement": "Toggle MFA enforcement", + "Display name": "Display name", + "Allow signup": "Allow signup", + "Enabled": "Enabled", + "Advanced Settings": "Advanced Settings", + "Enable TLS/SSL": "Enable TLS/SSL", + "Use secure connection to LDAP server": "Use secure connection to LDAP server", + "Group sync": "Group sync", "No SSO providers found.": "No SSO providers found.", - "Delete SSO provider": "Delete SSO provider.", + "Delete SSO provider": "Delete SSO provider", "Are you sure you want to delete this SSO provider?": "Are you sure you want to delete this SSO provider?", - "Action": "Action.", - "{{ssoProviderType}} configuration": "{{ssoProviderType}} configuration.", - "Icon": "Icon.", - "Upload image": "Upload image.", + "Action": "Action", + "{{ssoProviderType}} configuration": "{{ssoProviderType}} configuration", + "Icon": "Icon", + "Upload image": "Upload image", "Remove image": "Remove image", "Failed to remove image": "Failed to remove image", "Image exceeds 10MB limit.": "Image exceeds 10MB limit.", @@ -670,13 +670,13 @@ "More options": "More options", "{{name}} mentioned you in a comment": "{{name}} mentioned you in a comment", "{{name}} commented on a page": "{{name}} commented on a page", - "{{name}} resolved a comment": "{{name}} resolved a comment.", - "{{name}} mentioned you on a page": "{{name}} mentioned you on a page.", - "{{name}} gave you edit access to a page": "{{name}} gave you edit access to a page.", - "{{name}} gave you view access to a page": "{{name}} gave you view access to a page.", - "{{name}} updated a page": "{{name}} has updated a page.", - "Watch page": "Watch this page", - "Stop watching": "Stop watching this page", + "{{name}} resolved a comment": "{{name}} resolved a comment", + "{{name}} mentioned you on a page": "{{name}} mentioned you on a page", + "{{name}} gave you edit access to a page": "{{name}} gave you edit access to a page", + "{{name}} gave you view access to a page": "{{name}} gave you view access to a page", + "{{name}} updated a page": "{{name}} updated a page", + "Watch page": "Watch page", + "Stop watching": "Stop watching", "Email notifications": "Email notifications", "Page updates": "Page updates", "Get notified when pages you watch are updated.": "Receive notifications when the pages you watch are updated.", @@ -726,30 +726,30 @@ "Removed page restriction": "Removed page restriction", "Added page permission": "Added page permission", "Removed page permission": "Removed page permission", - "Verifying your email": "Verifying your email.", + "Verifying your email": "Verifying your email", "Please wait...": "Please wait...", "Verification failed. The link may have expired.": "Verification failed. The link may have expired.", - "Check your email": "Check your email.", + "Check your email": "Check your email", "We sent a verification link to {{email}}.": "We sent a verification link to {{email}}.", "We sent a verification link to your email.": "We sent a verification link to your email.", "Click the link to verify your email and access your workspace.": "Click the link to verify your email and access your workspace.", - "Resend verification email": "Resend verification email.", + "Resend verification email": "Resend verification email", "Verification email sent. Please check your inbox.": "Verification email sent. Please check your inbox.", "Failed to resend verification email. Please try again.": "Failed to resend verification email. Please try again.", "We've sent you an email with your associated workspaces.": "We've sent you an email with your associated workspaces.", - "Load more": "Load more.", - "Log out of all devices": "Log out of all devices.", - "Log out of all sessions except this device": "Log out of all sessions except this device.", - "This Device": "This Device.", - "Unknown device": "Unknown device.", - "No active sessions": "No active sessions.", - "Session revoked": "Session revoked.", - "All other sessions revoked": "All other sessions revoked.", - "Last used": "Last used.", - "Created": "Created.", - "Rename": "Rename.", - "Publish": "Publish.", - "Security": "Security.", - "Enforce SSO": "Enforce SSO.", - "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to log in with email and password." + "Load more": "Load more", + "Log out of all devices": "Log out of all devices", + "Log out of all sessions except this device": "Log out of all sessions except this device", + "This Device": "This Device", + "Unknown device": "Unknown device", + "No active sessions": "No active sessions", + "Session revoked": "Session revoked", + "All other sessions revoked": "All other sessions revoked", + "Last used": "Last used", + "Created": "Created", + "Rename": "Rename", + "Publish": "Publish", + "Security": "Security", + "Enforce SSO": "Enforce SSO", + "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to login with email and password." } diff --git a/apps/client/src/features/comment/queries/comment-query.ts b/apps/client/src/features/comment/queries/comment-query.ts index 6c86c039..6b7432eb 100644 --- a/apps/client/src/features/comment/queries/comment-query.ts +++ b/apps/client/src/features/comment/queries/comment-query.ts @@ -65,6 +65,11 @@ export function useCreateCommentMutation() { ) as InfiniteData> | undefined; if (cache && cache.pages.length > 0) { + const alreadyExists = cache.pages.some((page) => + page.items.some((c) => c.id === newComment.id), + ); + if (alreadyExists) return; + const lastIdx = cache.pages.length - 1; queryClient.setQueryData(RQ_KEY(newComment.pageId), { ...cache, diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index 8f6e6cdb..a61b994b 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -142,6 +142,25 @@ export const mainExtensions = [ }), ]; }, + addKeyboardShortcuts() { + return { + Enter: ({ editor }) => { + const { from, to } = editor.state.selection; + if (from !== to) return false; + if (!editor.isActive("code")) return false; + + const $from = editor.state.doc.resolve(from); + const codeType = editor.state.schema.marks.code; + const nodeAfter = $from.nodeAfter; + + if (nodeAfter && codeType.isInSet(nodeAfter.marks)) { + return false; + } + + return editor.chain().unsetCode().splitBlock().run(); + }, + }; + }, }), SharedStorage, Heading, diff --git a/apps/server/package.json b/apps/server/package.json index c6e0dc2c..252d73ed 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "0.71.0", + "version": "0.71.1", "description": "", "author": "", "private": true, diff --git a/apps/server/src/collaboration/processors/history.processor.ts b/apps/server/src/collaboration/processors/history.processor.ts index d7e27f60..5374e745 100644 --- a/apps/server/src/collaboration/processors/history.processor.ts +++ b/apps/server/src/collaboration/processors/history.processor.ts @@ -11,6 +11,7 @@ import { import { extractMentions, extractPageMentions, + extractInternalLinkSlugIds, } from '../../common/helpers/prosemirror/utils'; import { PageHistoryRepo } from '@docmost/db/repos/page/page-history.repo'; import { PageRepo } from '@docmost/db/repos/page/page.repo'; @@ -77,12 +78,14 @@ export class HistoryProcessor extends WorkerHost implements OnModuleDestroy { const mentions = extractMentions(page.content); const pageMentions = extractPageMentions(mentions); + const internalLinkSlugIds = extractInternalLinkSlugIds(page.content); await this.generalQueue .add(QueueJob.PAGE_BACKLINKS, { pageId, workspaceId: page.workspaceId, mentions: pageMentions, + internalLinkSlugIds, } as IPageBacklinkJob) .catch((err) => { this.logger.error( diff --git a/apps/server/src/common/helpers/prosemirror/utils.ts b/apps/server/src/common/helpers/prosemirror/utils.ts index 7704306e..07b6828b 100644 --- a/apps/server/src/common/helpers/prosemirror/utils.ts +++ b/apps/server/src/common/helpers/prosemirror/utils.ts @@ -7,6 +7,10 @@ import { validate as isValidUUID } from 'uuid'; import { Transform } from '@tiptap/pm/transform'; import { TiptapTransformer } from '@hocuspocus/transformer'; import * as Y from 'yjs'; +import { + INTERNAL_LINK_REGEX, + extractPageSlugId, +} from '../../../integrations/export/utils'; export interface MentionNode { id: string; @@ -64,6 +68,27 @@ export function extractPageMentions(mentionList: MentionNode[]): MentionNode[] { return pageMentionList as MentionNode[]; } +export function extractInternalLinkSlugIds(prosemirrorJson: any): string[] { + const slugIds: string[] = []; + const doc = jsonToNode(prosemirrorJson); + + doc.descendants((node: Node) => { + for (const mark of node.marks) { + if (mark.type.name === 'link' && mark.attrs.internal && mark.attrs.href) { + const match = mark.attrs.href.match(INTERNAL_LINK_REGEX); + if (match) { + const slugId = extractPageSlugId(match[5]); + if (slugId && !slugIds.includes(slugId)) { + slugIds.push(slugId); + } + } + } + } + }); + + return slugIds; +} + export function extractUserMentionIdsFromJson(json: any): string[] { const userIds: string[] = []; diff --git a/apps/server/src/core/page/services/page.service.ts b/apps/server/src/core/page/services/page.service.ts index 8df9e4bd..c162782c 100644 --- a/apps/server/src/core/page/services/page.service.ts +++ b/apps/server/src/core/page/services/page.service.ts @@ -47,6 +47,10 @@ import { QueueJob, QueueName } from '../../../integrations/queue/constants'; import { EventName } from '../../../common/events/event.contants'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { CollaborationGateway } from '../../../collaboration/collaboration.gateway'; +import { + INTERNAL_LINK_REGEX, + extractPageSlugId, +} from '../../../integrations/export/utils'; import { markdownToHtml } from '@docmost/editor-ext'; import { WatcherService } from '../../watcher/watcher.service'; import { sql } from 'kysely'; @@ -510,6 +514,11 @@ export class PageService { }); }); + const slugIdMap = new Map(); + for (const [, entry] of pageMap) { + slugIdMap.set(entry.oldSlugId, entry); + } + const attachmentMap = new Map(); const insertablePages: InsertablePage[] = await Promise.all( @@ -576,6 +585,28 @@ export class PageService { node.attrs.slugId = mappedPage.newSlugId; } } + + // Update internal page links in link marks + for (const mark of node.marks) { + if ( + mark.type.name === 'link' && + mark.attrs.internal && + mark.attrs.href + ) { + const match = mark.attrs.href.match(INTERNAL_LINK_REGEX); + if (match) { + const slugId = extractPageSlugId(match[5]); + if (slugId && slugIdMap.has(slugId)) { + const mappedPage = slugIdMap.get(slugId); + //@ts-ignore + mark.attrs.href = mark.attrs.href.replace( + slugId, + mappedPage.newSlugId, + ); + } + } + } + } }); const prosemirrorJson = prosemirrorDoc.toJSON(); diff --git a/apps/server/src/integrations/queue/constants/queue.interface.ts b/apps/server/src/integrations/queue/constants/queue.interface.ts index f0683a4e..58937419 100644 --- a/apps/server/src/integrations/queue/constants/queue.interface.ts +++ b/apps/server/src/integrations/queue/constants/queue.interface.ts @@ -4,6 +4,7 @@ export interface IPageBacklinkJob { pageId: string; workspaceId: string; mentions: MentionNode[]; + internalLinkSlugIds?: string[]; } export interface IAddPageWatchersJob { diff --git a/apps/server/src/integrations/queue/tasks/backlinks.task.ts b/apps/server/src/integrations/queue/tasks/backlinks.task.ts index 268adaae..9d707272 100644 --- a/apps/server/src/integrations/queue/tasks/backlinks.task.ts +++ b/apps/server/src/integrations/queue/tasks/backlinks.task.ts @@ -11,7 +11,7 @@ export async function processBacklinks( backlinkRepo: BacklinkRepo, data: IPageBacklinkJob, ): Promise { - const { pageId, mentions, workspaceId } = data; + const { pageId, mentions, workspaceId, internalLinkSlugIds = [] } = data; await executeTx(db, async (trx) => { const existingBacklinks = await trx @@ -20,7 +20,28 @@ export async function processBacklinks( .where('sourcePageId', '=', pageId) .execute(); - if (existingBacklinks.length === 0 && mentions.length === 0) { + const mentionTargetPageIds = mentions + .filter((mention) => mention.entityId !== pageId) + .map((mention) => mention.entityId); + + let resolvedLinkPageIds: string[] = []; + if (internalLinkSlugIds.length > 0) { + const resolvedPages = await trx + .selectFrom('pages') + .select('id') + .where('slugId', 'in', internalLinkSlugIds) + .where('workspaceId', '=', workspaceId) + .execute(); + resolvedLinkPageIds = resolvedPages + .map((p) => p.id) + .filter((id) => id !== pageId); + } + + const allTargetPageIds = [ + ...new Set([...mentionTargetPageIds, ...resolvedLinkPageIds]), + ]; + + if (existingBacklinks.length === 0 && allTargetPageIds.length === 0) { return; } @@ -28,16 +49,12 @@ export async function processBacklinks( (backlink) => backlink.targetPageId, ); - const targetPageIds = mentions - .filter((mention) => mention.entityId !== pageId) - .map((mention) => mention.entityId); - let validTargetPages = []; - if (targetPageIds.length > 0) { + if (allTargetPageIds.length > 0) { validTargetPages = await trx .selectFrom('pages') .select('id') - .where('id', 'in', targetPageIds) + .where('id', 'in', allTargetPageIds) .where('workspaceId', '=', workspaceId) .execute(); } diff --git a/package.json b/package.json index 36db0934..2c49556b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "docmost", "homepage": "https://docmost.com", - "version": "0.71.0", + "version": "0.71.1", "private": true, "scripts": { "build": "nx run-many -t build", @@ -109,7 +109,8 @@ "nanoid@^3": "3.3.8", "socket.io-parser": "4.2.6", "serialize-javascript": "7.0.3", - "lodash-es": "4.17.23", + "lodash-es": "4.18.1", + "lodash": "4.18.1", "@hono/node-server": "1.19.10", "undici": "7.24.0", "ajv@^6": "6.14.0", @@ -126,7 +127,9 @@ "yaml@>=1.0.0 <1.10.3": "1.10.3", "yaml@>=2.0.0 <2.8.3": "2.8.3", "path-to-regexp@^8": "8.4.0", - "brace-expansion@^5": "5.0.5" + "brace-expansion@^5": "5.0.5", + "@xmldom/xmldom": "0.8.12", + "handlebars": "4.7.9" }, "neverBuiltDependencies": [] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77878a19..c00668da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,8 @@ overrides: nanoid@^3: 3.3.8 socket.io-parser: 4.2.6 serialize-javascript: 7.0.3 - lodash-es: 4.17.23 + lodash-es: 4.18.1 + lodash: 4.18.1 '@hono/node-server': 1.19.10 undici: 7.24.0 ajv@^6: 6.14.0 @@ -34,6 +35,8 @@ overrides: yaml@>=2.0.0 <2.8.3: 2.8.3 path-to-regexp@^8: 8.4.0 brace-expansion@^5: 5.0.5 + '@xmldom/xmldom': 0.8.12 + handlebars: 4.7.9 patchedDependencies: react-arborist@3.4.0: @@ -923,44 +926,44 @@ packages: resolution: {integrity: sha512-0XLrOT4Cm3NEhhiME7l/8LbTXS4KdsbR4dSrY207KNKTcHLLTZ9EXt4ZpgnTfLvWQF3pGP2us4Zi1fYLo0N+Ow==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.23': - resolution: {integrity: sha512-aoJncvD1XvloZ9JLnKqTRL9dBy+Szkryoag9VT+V1TqsuUgIxV9cnBVM/hrDi2vE8bDqLiDR8nirdRcCdtJu0w==} + '@aws-sdk/core@3.973.26': + resolution: {integrity: sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ==} engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.972.5': resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.21': - resolution: {integrity: sha512-BkAfKq8Bd4shCtec1usNz//urPJF/SZy14qJyxkSaRJQ/Vv1gVh0VZSTmS7aE6aLMELkFV5wHHrS9ZcdG8Kxsg==} + '@aws-sdk/credential-provider-env@3.972.24': + resolution: {integrity: sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.23': - resolution: {integrity: sha512-4XZ3+Gu5DY8/n8zQFHBgcKTF7hWQl42G6CY9xfXVo2d25FM/lYkpmuzhYopYoPL1ITWkJ2OSBQfYEu5JRfHOhA==} + '@aws-sdk/credential-provider-http@3.972.26': + resolution: {integrity: sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.23': - resolution: {integrity: sha512-PZLSmU0JFpNCDFReidBezsgL5ji9jOBry8CnZdw4Jj6d0K2z3Ftnp44NXgADqYx5BLMu/ZHujfeJReaDoV+IwQ==} + '@aws-sdk/credential-provider-ini@3.972.28': + resolution: {integrity: sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.23': - resolution: {integrity: sha512-OmE/pSkbMM3dCj1HdOnZ5kXnKK+R/Yz+kbBugraBecp0pGAs21eEURfQRz+1N2gzIHLVyGIP1MEjk/uSrFsngg==} + '@aws-sdk/credential-provider-login@3.972.28': + resolution: {integrity: sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.24': - resolution: {integrity: sha512-9Jwi7aps3AfUicJyF5udYadPypPpCwUZ6BSKr/QjRbVCpRVS1wc+1Q6AEZ/qz8J4JraeRd247pSzyMQSIHVebw==} + '@aws-sdk/credential-provider-node@3.972.29': + resolution: {integrity: sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.21': - resolution: {integrity: sha512-nRxbeOJ1E1gVA0lNQezuMVndx+ZcuyaW/RB05pUsznN5BxykSlH6KkZ/7Ca/ubJf3i5N3p0gwNO5zgPSCzj+ww==} + '@aws-sdk/credential-provider-process@3.972.24': + resolution: {integrity: sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.23': - resolution: {integrity: sha512-APUccADuYPLL0f2htpM8Z4czabSmHOdo4r41W6lKEZdy++cNJ42Radqy6x4TopENzr3hR6WYMyhiuiqtbf/nAA==} + '@aws-sdk/credential-provider-sso@3.972.28': + resolution: {integrity: sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.23': - resolution: {integrity: sha512-H5JNqtIwOu/feInmMMWcK0dL5r897ReEn7n2m16Dd0DPD9gA2Hg8Cq4UDzZ/9OzaLh/uqBM6seixz0U6Fi2Eag==} + '@aws-sdk/credential-provider-web-identity@3.972.28': + resolution: {integrity: sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ==} engines: {node: '>=20.0.0'} '@aws-sdk/lib-storage@3.1014.0': @@ -977,8 +980,8 @@ packages: resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.974.3': - resolution: {integrity: sha512-fB7FNLH1+VPUs0QL3PLrHW+DD4gKu6daFgWtyq3R0Y0Lx8DLZPvyGAxCZNFBxH+M2xt9KvBJX6USwjuqvitmCQ==} + '@aws-sdk/middleware-flexible-checksums@3.974.6': + resolution: {integrity: sha512-YckB8k1ejbyCg/g36gUMFLNzE4W5cERIa4MtsdO+wpTmJEP0+TB7okWIt7d8TDOvnb7SwvxJ21E4TGOBxFpSWQ==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.8': @@ -993,40 +996,40 @@ packages: resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.8': - resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==} + '@aws-sdk/middleware-recursion-detection@3.972.9': + resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.972.23': - resolution: {integrity: sha512-50QgHGPQAb2veqFOmTF1A3GsAklLHZXL47KbY35khIkfbXH5PLvqpEc/gOAEBPj/yFxrlgxz/8mqWcWTNxBkwQ==} + '@aws-sdk/middleware-sdk-s3@3.972.27': + resolution: {integrity: sha512-gomO6DZwx+1D/9mbCpcqO5tPBqYBK7DtdgjTIjZ4yvfh/S7ETwAPS0XbJgP2JD8Ycr5CwVrEkV1sFtu3ShXeOw==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.8': resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.24': - resolution: {integrity: sha512-dLTWy6IfAMhNiSEvMr07g/qZ54be6pLqlxVblbF6AzafmmGAzMMj8qMoY9B4+YgT+gY9IcuxZslNh03L6PyMCQ==} + '@aws-sdk/middleware-user-agent@3.972.28': + resolution: {integrity: sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.996.13': - resolution: {integrity: sha512-ptZ1HF4yYHNJX8cgFF+8NdYO69XJKZn7ft0/ynV3c0hCbN+89fAbrLS+fqniU2tW8o9Kfqhj8FUh+IPXb2Qsuw==} + '@aws-sdk/nested-clients@3.996.18': + resolution: {integrity: sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.9': - resolution: {integrity: sha512-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng==} + '@aws-sdk/region-config-resolver@3.972.10': + resolution: {integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==} engines: {node: '>=20.0.0'} '@aws-sdk/s3-request-presigner@3.1014.0': resolution: {integrity: sha512-XEcK50lToSoLPrQztKQhONYQW45613H8oEL00mBUd/+OZgk0+3zJ8kSNDsIJioZ3H7Be+yC3CL6a22dZFIKUXQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.996.11': - resolution: {integrity: sha512-SKgZY7x6AloLUXO20FJGnkKJ3a6CXzNDt6PYs2yqoPzgU0xKWcUoGGJGEBTsfM5eihKW42lbwp+sXzACLbSsaA==} + '@aws-sdk/signature-v4-multi-region@3.996.15': + resolution: {integrity: sha512-Ukw2RpqvaL96CjfH/FgfBmy/ZosHBqoHBCFsN61qGg99F33vpntIVii8aNeh65XuOja73arSduskoa4OJea9RQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1014.0': - resolution: {integrity: sha512-gHTHNUoaOGNrSWkl32A7wFsU78jlNTlqMccLu0byUk5CysYYXaxNMIonIVr4YcykC7vgtDS5ABuz83giy6fzJA==} + '@aws-sdk/token-providers@3.1021.0': + resolution: {integrity: sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.6': @@ -1052,8 +1055,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.972.8': resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.973.10': - resolution: {integrity: sha512-E99zeTscCc+pTMfsvnfi6foPpKmdD1cZfOC7/P8UUrjsoQdg9VEWPRD+xdFduKnfPXwcvby58AlO9jwwF6U96g==} + '@aws-sdk/util-user-agent-node@3.973.14': + resolution: {integrity: sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1061,8 +1064,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.15': - resolution: {integrity: sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA==} + '@aws-sdk/xml-builder@3.972.16': + resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.3': @@ -4373,8 +4376,8 @@ packages: resolution: {integrity: sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==} engines: {node: '>=18.0.0'} - '@smithy/core@3.23.12': - resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==} + '@smithy/core@3.23.13': + resolution: {integrity: sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q==} engines: {node: '>=18.0.0'} '@smithy/credential-provider-imds@4.2.12': @@ -4437,16 +4440,16 @@ packages: resolution: {integrity: sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.27': - resolution: {integrity: sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==} + '@smithy/middleware-endpoint@4.4.28': + resolution: {integrity: sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.44': - resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} + '@smithy/middleware-retry@4.4.46': + resolution: {integrity: sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.15': - resolution: {integrity: sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==} + '@smithy/middleware-serde@4.2.16': + resolution: {integrity: sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA==} engines: {node: '>=18.0.0'} '@smithy/middleware-stack@4.2.12': @@ -4457,8 +4460,8 @@ packages: resolution: {integrity: sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.5.0': - resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} + '@smithy/node-http-handler@4.5.1': + resolution: {integrity: sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw==} engines: {node: '>=18.0.0'} '@smithy/property-provider@4.2.12': @@ -4489,8 +4492,8 @@ packages: resolution: {integrity: sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.12.7': - resolution: {integrity: sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==} + '@smithy/smithy-client@4.12.8': + resolution: {integrity: sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA==} engines: {node: '>=18.0.0'} '@smithy/types@4.13.1': @@ -4525,12 +4528,12 @@ packages: resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.43': - resolution: {integrity: sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==} + '@smithy/util-defaults-mode-browser@4.3.44': + resolution: {integrity: sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.47': - resolution: {integrity: sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==} + '@smithy/util-defaults-mode-node@4.2.48': + resolution: {integrity: sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg==} engines: {node: '>=18.0.0'} '@smithy/util-endpoints@3.3.3': @@ -4545,12 +4548,12 @@ packages: resolution: {integrity: sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.2.12': - resolution: {integrity: sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==} + '@smithy/util-retry@4.2.13': + resolution: {integrity: sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.20': - resolution: {integrity: sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==} + '@smithy/util-stream@4.5.21': + resolution: {integrity: sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q==} engines: {node: '>=18.0.0'} '@smithy/util-uri-escape@4.2.2': @@ -4565,8 +4568,8 @@ packages: resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} engines: {node: '>=18.0.0'} - '@smithy/util-waiter@4.2.13': - resolution: {integrity: sha512-2zdZ9DTHngRtcYxJK1GUDxruNr53kv5W2Lupe0LMU+Imr6ohQg8M2T14MNkj1Y0wS3FFwpgpGQyvuaMF7CiTmQ==} + '@smithy/util-waiter@4.2.14': + resolution: {integrity: sha512-2zqq5o/oizvMaFUlNiTyZ7dbgYv1a893aGut2uaxtbzTx/VYYnRxWzDHuD/ftgcw94ffenua+ZNLrbqwUYE+Bg==} engines: {node: '>=18.0.0'} '@smithy/uuid@1.1.2': @@ -5545,8 +5548,8 @@ packages: resolution: {integrity: sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q==} engines: {node: '>= 16'} - '@xmldom/xmldom@0.8.10': - resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + '@xmldom/xmldom@0.8.12': + resolution: {integrity: sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==} engines: {node: '>=10.0.0'} '@xtuc/ieee754@1.2.0': @@ -7298,8 +7301,8 @@ packages: hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} hasBin: true @@ -8307,8 +8310,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.17.23: - resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -8349,8 +8352,8 @@ packages: lodash.throttle@4.1.1: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -11024,26 +11027,26 @@ snapshots: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.23 - '@aws-sdk/credential-provider-node': 3.972.24 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/credential-provider-node': 3.972.29 '@aws-sdk/middleware-bucket-endpoint': 3.972.8 '@aws-sdk/middleware-expect-continue': 3.972.8 - '@aws-sdk/middleware-flexible-checksums': 3.974.3 + '@aws-sdk/middleware-flexible-checksums': 3.974.6 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-location-constraint': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 - '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-sdk-s3': 3.972.23 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-sdk-s3': 3.972.27 '@aws-sdk/middleware-ssec': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.24 - '@aws-sdk/region-config-resolver': 3.972.9 - '@aws-sdk/signature-v4-multi-region': 3.996.11 + '@aws-sdk/middleware-user-agent': 3.972.28 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/signature-v4-multi-region': 3.996.15 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.10 + '@aws-sdk/util-user-agent-node': 3.973.14 '@smithy/config-resolver': 4.4.13 - '@smithy/core': 3.23.12 + '@smithy/core': 3.23.13 '@smithy/eventstream-serde-browser': 4.2.12 '@smithy/eventstream-serde-config-resolver': 4.3.12 '@smithy/eventstream-serde-node': 4.2.12 @@ -11054,41 +11057,41 @@ snapshots: '@smithy/invalid-dependency': 4.2.12 '@smithy/md5-js': 4.2.12 '@smithy/middleware-content-length': 4.2.12 - '@smithy/middleware-endpoint': 4.4.27 - '@smithy/middleware-retry': 4.4.44 - '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/middleware-retry': 4.4.46 + '@smithy/middleware-serde': 4.2.16 '@smithy/middleware-stack': 4.2.12 '@smithy/node-config-provider': 4.3.12 - '@smithy/node-http-handler': 4.5.0 + '@smithy/node-http-handler': 4.5.1 '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 '@smithy/url-parser': 4.2.12 '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.43 - '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-defaults-mode-browser': 4.3.44 + '@smithy/util-defaults-mode-node': 4.2.48 '@smithy/util-endpoints': 3.3.3 '@smithy/util-middleware': 4.2.12 - '@smithy/util-retry': 4.2.12 - '@smithy/util-stream': 4.5.20 + '@smithy/util-retry': 4.2.13 + '@smithy/util-stream': 4.5.21 '@smithy/util-utf8': 4.2.2 - '@smithy/util-waiter': 4.2.13 + '@smithy/util-waiter': 4.2.14 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.23': + '@aws-sdk/core@3.973.26': dependencies: '@aws-sdk/types': 3.973.6 - '@aws-sdk/xml-builder': 3.972.15 - '@smithy/core': 3.23.12 + '@aws-sdk/xml-builder': 3.972.16 + '@smithy/core': 3.23.13 '@smithy/node-config-provider': 4.3.12 '@smithy/property-provider': 4.2.12 '@smithy/protocol-http': 5.3.12 '@smithy/signature-v4': 5.3.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 '@smithy/util-base64': 4.3.2 '@smithy/util-middleware': 4.2.12 @@ -11100,37 +11103,37 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.21': + '@aws-sdk/credential-provider-env@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.26 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.23': + '@aws-sdk/credential-provider-http@3.972.26': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.26 '@aws-sdk/types': 3.973.6 '@smithy/fetch-http-handler': 5.3.15 - '@smithy/node-http-handler': 4.5.0 + '@smithy/node-http-handler': 4.5.1 '@smithy/property-provider': 4.2.12 '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 - '@smithy/util-stream': 4.5.20 + '@smithy/util-stream': 4.5.21 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.23': + '@aws-sdk/credential-provider-ini@3.972.28': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/credential-provider-env': 3.972.21 - '@aws-sdk/credential-provider-http': 3.972.23 - '@aws-sdk/credential-provider-login': 3.972.23 - '@aws-sdk/credential-provider-process': 3.972.21 - '@aws-sdk/credential-provider-sso': 3.972.23 - '@aws-sdk/credential-provider-web-identity': 3.972.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/credential-provider-env': 3.972.24 + '@aws-sdk/credential-provider-http': 3.972.26 + '@aws-sdk/credential-provider-login': 3.972.28 + '@aws-sdk/credential-provider-process': 3.972.24 + '@aws-sdk/credential-provider-sso': 3.972.28 + '@aws-sdk/credential-provider-web-identity': 3.972.28 + '@aws-sdk/nested-clients': 3.996.18 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -11140,10 +11143,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.23': + '@aws-sdk/credential-provider-login@3.972.28': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/protocol-http': 5.3.12 @@ -11153,14 +11156,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.24': + '@aws-sdk/credential-provider-node@3.972.29': dependencies: - '@aws-sdk/credential-provider-env': 3.972.21 - '@aws-sdk/credential-provider-http': 3.972.23 - '@aws-sdk/credential-provider-ini': 3.972.23 - '@aws-sdk/credential-provider-process': 3.972.21 - '@aws-sdk/credential-provider-sso': 3.972.23 - '@aws-sdk/credential-provider-web-identity': 3.972.23 + '@aws-sdk/credential-provider-env': 3.972.24 + '@aws-sdk/credential-provider-http': 3.972.26 + '@aws-sdk/credential-provider-ini': 3.972.28 + '@aws-sdk/credential-provider-process': 3.972.24 + '@aws-sdk/credential-provider-sso': 3.972.28 + '@aws-sdk/credential-provider-web-identity': 3.972.28 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -11170,20 +11173,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.21': + '@aws-sdk/credential-provider-process@3.972.24': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.26 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.23': + '@aws-sdk/credential-provider-sso@3.972.28': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 - '@aws-sdk/token-providers': 3.1014.0 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 + '@aws-sdk/token-providers': 3.1021.0 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -11192,10 +11195,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.23': + '@aws-sdk/credential-provider-web-identity@3.972.28': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -11208,8 +11211,8 @@ snapshots: dependencies: '@aws-sdk/client-s3': 3.1014.0 '@smithy/abort-controller': 4.2.12 - '@smithy/middleware-endpoint': 4.4.27 - '@smithy/smithy-client': 4.12.7 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/smithy-client': 4.12.8 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 @@ -11232,12 +11235,12 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.974.3': + '@aws-sdk/middleware-flexible-checksums@3.974.6': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.26 '@aws-sdk/crc64-nvme': 3.972.5 '@aws-sdk/types': 3.973.6 '@smithy/is-array-buffer': 4.2.2 @@ -11245,7 +11248,7 @@ snapshots: '@smithy/protocol-http': 5.3.12 '@smithy/types': 4.13.1 '@smithy/util-middleware': 4.2.12 - '@smithy/util-stream': 4.5.20 + '@smithy/util-stream': 4.5.21 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 @@ -11268,7 +11271,7 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.972.8': + '@aws-sdk/middleware-recursion-detection@3.972.9': dependencies: '@aws-sdk/types': 3.973.6 '@aws/lambda-invoke-store': 0.2.3 @@ -11276,20 +11279,20 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.23': + '@aws-sdk/middleware-sdk-s3@3.972.27': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.26 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-arn-parser': 3.972.3 - '@smithy/core': 3.23.12 + '@smithy/core': 3.23.13 '@smithy/node-config-provider': 4.3.12 '@smithy/protocol-http': 5.3.12 '@smithy/signature-v4': 5.3.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 '@smithy/util-config-provider': 4.2.2 '@smithy/util-middleware': 4.2.12 - '@smithy/util-stream': 4.5.20 + '@smithy/util-stream': 4.5.21 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 @@ -11299,61 +11302,61 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.24': + '@aws-sdk/middleware-user-agent@3.972.28': dependencies: - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.26 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 - '@smithy/core': 3.23.12 + '@smithy/core': 3.23.13 '@smithy/protocol-http': 5.3.12 '@smithy/types': 4.13.1 - '@smithy/util-retry': 4.2.12 + '@smithy/util-retry': 4.2.13 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.13': + '@aws-sdk/nested-clients@3.996.18': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.23 + '@aws-sdk/core': 3.973.26 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 - '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.24 - '@aws-sdk/region-config-resolver': 3.972.9 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.28 + '@aws-sdk/region-config-resolver': 3.972.10 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.10 + '@aws-sdk/util-user-agent-node': 3.973.14 '@smithy/config-resolver': 4.4.13 - '@smithy/core': 3.23.12 + '@smithy/core': 3.23.13 '@smithy/fetch-http-handler': 5.3.15 '@smithy/hash-node': 4.2.12 '@smithy/invalid-dependency': 4.2.12 '@smithy/middleware-content-length': 4.2.12 - '@smithy/middleware-endpoint': 4.4.27 - '@smithy/middleware-retry': 4.4.44 - '@smithy/middleware-serde': 4.2.15 + '@smithy/middleware-endpoint': 4.4.28 + '@smithy/middleware-retry': 4.4.46 + '@smithy/middleware-serde': 4.2.16 '@smithy/middleware-stack': 4.2.12 '@smithy/node-config-provider': 4.3.12 - '@smithy/node-http-handler': 4.5.0 + '@smithy/node-http-handler': 4.5.1 '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 '@smithy/url-parser': 4.2.12 '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.43 - '@smithy/util-defaults-mode-node': 4.2.47 + '@smithy/util-defaults-mode-browser': 4.3.44 + '@smithy/util-defaults-mode-node': 4.2.48 '@smithy/util-endpoints': 3.3.3 '@smithy/util-middleware': 4.2.12 - '@smithy/util-retry': 4.2.12 + '@smithy/util-retry': 4.2.13 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.972.9': + '@aws-sdk/region-config-resolver@3.972.10': dependencies: '@aws-sdk/types': 3.973.6 '@smithy/config-resolver': 4.4.13 @@ -11363,28 +11366,28 @@ snapshots: '@aws-sdk/s3-request-presigner@3.1014.0': dependencies: - '@aws-sdk/signature-v4-multi-region': 3.996.11 + '@aws-sdk/signature-v4-multi-region': 3.996.15 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-format-url': 3.972.8 - '@smithy/middleware-endpoint': 4.4.27 + '@smithy/middleware-endpoint': 4.4.28 '@smithy/protocol-http': 5.3.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.11': + '@aws-sdk/signature-v4-multi-region@3.996.15': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.23 + '@aws-sdk/middleware-sdk-s3': 3.972.27 '@aws-sdk/types': 3.973.6 '@smithy/protocol-http': 5.3.12 '@smithy/signature-v4': 5.3.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1014.0': + '@aws-sdk/token-providers@3.1021.0': dependencies: - '@aws-sdk/core': 3.973.23 - '@aws-sdk/nested-clients': 3.996.13 + '@aws-sdk/core': 3.973.26 + '@aws-sdk/nested-clients': 3.996.18 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -11428,16 +11431,16 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.10': + '@aws-sdk/util-user-agent-node@3.973.14': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.24 + '@aws-sdk/middleware-user-agent': 3.972.28 '@aws-sdk/types': 3.973.6 '@smithy/node-config-provider': 4.3.12 '@smithy/types': 4.13.1 '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.15': + '@aws-sdk/xml-builder@3.972.16': dependencies: '@smithy/types': 4.13.1 fast-xml-parser: 5.5.8 @@ -12255,23 +12258,23 @@ snapshots: dependencies: '@chevrotain/gast': 11.0.3 '@chevrotain/types': 11.0.3 - lodash-es: 4.17.23 + lodash-es: 4.18.1 '@chevrotain/cst-dts-gen@11.1.2': dependencies: '@chevrotain/gast': 11.1.2 '@chevrotain/types': 11.1.2 - lodash-es: 4.17.23 + lodash-es: 4.18.1 '@chevrotain/gast@11.0.3': dependencies: '@chevrotain/types': 11.0.3 - lodash-es: 4.17.23 + lodash-es: 4.18.1 '@chevrotain/gast@11.1.2': dependencies: '@chevrotain/types': 11.1.2 - lodash-es: 4.17.23 + lodash-es: 4.18.1 '@chevrotain/regexp-to-ast@11.0.3': {} @@ -13578,7 +13581,7 @@ snapshots: '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) dotenv: 17.2.3 dotenv-expand: 12.0.3 - lodash: 4.17.23 + lodash: 4.18.1 rxjs: 7.8.2 '@nestjs/core@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': @@ -13710,7 +13713,7 @@ snapshots: '@types/xml-encryption': 1.2.4 '@types/xml2js': 0.4.14 '@xmldom/is-dom-node': 1.0.1 - '@xmldom/xmldom': 0.8.10 + '@xmldom/xmldom': 0.8.12 debug: 4.4.0 xml-crypto: 6.1.2 xml-encryption: 3.1.0 @@ -14922,7 +14925,7 @@ snapshots: '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/core@3.23.12': + '@smithy/core@3.23.13': dependencies: '@smithy/protocol-http': 5.3.12 '@smithy/types': 4.13.1 @@ -14930,7 +14933,7 @@ snapshots: '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-middleware': 4.2.12 - '@smithy/util-stream': 4.5.20 + '@smithy/util-stream': 4.5.21 '@smithy/util-utf8': 4.2.2 '@smithy/uuid': 1.1.2 tslib: 2.8.1 @@ -15026,10 +15029,10 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.27': + '@smithy/middleware-endpoint@4.4.28': dependencies: - '@smithy/core': 3.23.12 - '@smithy/middleware-serde': 4.2.15 + '@smithy/core': 3.23.13 + '@smithy/middleware-serde': 4.2.16 '@smithy/node-config-provider': 4.3.12 '@smithy/shared-ini-file-loader': 4.4.7 '@smithy/types': 4.13.1 @@ -15037,21 +15040,21 @@ snapshots: '@smithy/util-middleware': 4.2.12 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.44': + '@smithy/middleware-retry@4.4.46': dependencies: '@smithy/node-config-provider': 4.3.12 '@smithy/protocol-http': 5.3.12 '@smithy/service-error-classification': 4.2.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 '@smithy/util-middleware': 4.2.12 - '@smithy/util-retry': 4.2.12 + '@smithy/util-retry': 4.2.13 '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.15': + '@smithy/middleware-serde@4.2.16': dependencies: - '@smithy/core': 3.23.12 + '@smithy/core': 3.23.13 '@smithy/protocol-http': 5.3.12 '@smithy/types': 4.13.1 tslib: 2.8.1 @@ -15068,9 +15071,8 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/node-http-handler@4.5.0': + '@smithy/node-http-handler@4.5.1': dependencies: - '@smithy/abort-controller': 4.2.12 '@smithy/protocol-http': 5.3.12 '@smithy/querystring-builder': 4.2.12 '@smithy/types': 4.13.1 @@ -15117,14 +15119,14 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/smithy-client@4.12.7': + '@smithy/smithy-client@4.12.8': dependencies: - '@smithy/core': 3.23.12 - '@smithy/middleware-endpoint': 4.4.27 + '@smithy/core': 3.23.13 + '@smithy/middleware-endpoint': 4.4.28 '@smithy/middleware-stack': 4.2.12 '@smithy/protocol-http': 5.3.12 '@smithy/types': 4.13.1 - '@smithy/util-stream': 4.5.20 + '@smithy/util-stream': 4.5.21 tslib: 2.8.1 '@smithy/types@4.13.1': @@ -15165,20 +15167,20 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.43': + '@smithy/util-defaults-mode-browser@4.3.44': dependencies: '@smithy/property-provider': 4.2.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.47': + '@smithy/util-defaults-mode-node@4.2.48': dependencies: '@smithy/config-resolver': 4.4.13 '@smithy/credential-provider-imds': 4.2.12 '@smithy/node-config-provider': 4.3.12 '@smithy/property-provider': 4.2.12 - '@smithy/smithy-client': 4.12.7 + '@smithy/smithy-client': 4.12.8 '@smithy/types': 4.13.1 tslib: 2.8.1 @@ -15197,16 +15199,16 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-retry@4.2.12': + '@smithy/util-retry@4.2.13': dependencies: '@smithy/service-error-classification': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-stream@4.5.20': + '@smithy/util-stream@4.5.21': dependencies: '@smithy/fetch-http-handler': 5.3.15 - '@smithy/node-http-handler': 4.5.0 + '@smithy/node-http-handler': 4.5.1 '@smithy/types': 4.13.1 '@smithy/util-base64': 4.3.2 '@smithy/util-buffer-from': 4.2.2 @@ -15228,9 +15230,8 @@ snapshots: '@smithy/util-buffer-from': 4.2.2 tslib: 2.8.1 - '@smithy/util-waiter@4.2.13': + '@smithy/util-waiter@4.2.14': dependencies: - '@smithy/abort-controller': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 @@ -16293,7 +16294,7 @@ snapshots: '@xmldom/is-dom-node@1.0.1': {} - '@xmldom/xmldom@0.8.10': {} + '@xmldom/xmldom@0.8.12': {} '@xtuc/ieee754@1.2.0': {} @@ -16849,12 +16850,12 @@ snapshots: chevrotain-allstar@0.3.1(chevrotain@11.0.3): dependencies: chevrotain: 11.0.3 - lodash-es: 4.17.23 + lodash-es: 4.18.1 chevrotain-allstar@0.3.1(chevrotain@11.1.2): dependencies: chevrotain: 11.1.2 - lodash-es: 4.17.23 + lodash-es: 4.18.1 chevrotain@11.0.3: dependencies: @@ -16863,7 +16864,7 @@ snapshots: '@chevrotain/regexp-to-ast': 11.0.3 '@chevrotain/types': 11.0.3 '@chevrotain/utils': 11.0.3 - lodash-es: 4.17.23 + lodash-es: 4.18.1 chevrotain@11.1.2: dependencies: @@ -16872,7 +16873,7 @@ snapshots: '@chevrotain/regexp-to-ast': 11.1.2 '@chevrotain/types': 11.1.2 '@chevrotain/utils': 11.1.2 - lodash-es: 4.17.23 + lodash-es: 4.18.1 chokidar@3.6.0: dependencies: @@ -17333,7 +17334,7 @@ snapshots: dagre-d3-es@7.0.14: dependencies: d3: 7.9.0 - lodash-es: 4.17.23 + lodash-es: 4.18.1 data-urls@5.0.0: dependencies: @@ -18396,7 +18397,7 @@ snapshots: hachure-fill@0.5.2: {} - handlebars@4.7.8: + handlebars@4.7.9: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -19591,7 +19592,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.17.23: {} + lodash-es@4.18.1: {} lodash.debounce@4.0.8: {} @@ -19619,7 +19620,7 @@ snapshots: lodash.throttle@4.1.1: {} - lodash@4.17.23: {} + lodash@4.18.1: {} log-symbols@4.1.0: dependencies: @@ -19688,7 +19689,7 @@ snapshots: mammoth@1.12.0: dependencies: - '@xmldom/xmldom': 0.8.10 + '@xmldom/xmldom': 0.8.12 argparse: 1.0.10 base64-js: 1.5.1 bluebird: 3.4.7 @@ -19752,7 +19753,7 @@ snapshots: dompurify: 3.3.3 katex: 0.16.40 khroma: 2.1.0 - lodash-es: 4.17.23 + lodash-es: 4.18.1 marked: 16.4.2 roughjs: 4.6.6 stylis: 4.3.6 @@ -19898,7 +19899,7 @@ snapshots: node-emoji@1.11.0: dependencies: - lodash: 4.17.23 + lodash: 4.18.1 node-fetch@2.7.0: dependencies: @@ -21728,7 +21729,7 @@ snapshots: dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.8 + handlebars: 4.7.9 jest: 30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 @@ -22292,12 +22293,12 @@ snapshots: xml-crypto@6.1.2: dependencies: '@xmldom/is-dom-node': 1.0.1 - '@xmldom/xmldom': 0.8.10 + '@xmldom/xmldom': 0.8.12 xpath: 0.0.33 xml-encryption@3.1.0: dependencies: - '@xmldom/xmldom': 0.8.10 + '@xmldom/xmldom': 0.8.12 escape-html: 1.0.3 xpath: 0.0.32 From e1bbceb9a63e2eeb927c945476c529607f752da0 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:10:41 +0100 Subject: [PATCH 41/44] fix: logs --- apps/server/src/common/helpers/utils.ts | 12 ++++++++++++ apps/server/src/common/logger/pino.config.ts | 3 ++- apps/server/src/ee | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/server/src/common/helpers/utils.ts b/apps/server/src/common/helpers/utils.ts index 36ff5b63..c37e9a47 100644 --- a/apps/server/src/common/helpers/utils.ts +++ b/apps/server/src/common/helpers/utils.ts @@ -142,6 +142,18 @@ export function isUserDisabled(user: { return !!(user.deactivatedAt || user.deletedAt); } +const SENSITIVE_URL_PREFIXES = ['/api/sso/']; + +export function redactSensitiveUrl(url: string): string { + if (url && SENSITIVE_URL_PREFIXES.some((prefix) => url.includes(prefix))) { + const qsIndex = url.indexOf('?'); + if (qsIndex !== -1) { + return url.substring(0, qsIndex); + } + } + return url; +} + export function createByteCountingStream(source: Readable) { let bytesRead = 0; const stream = new Transform({ diff --git a/apps/server/src/common/logger/pino.config.ts b/apps/server/src/common/logger/pino.config.ts index 0b8cd11a..4c3b1993 100644 --- a/apps/server/src/common/logger/pino.config.ts +++ b/apps/server/src/common/logger/pino.config.ts @@ -1,5 +1,6 @@ import { Params } from 'nestjs-pino'; import { stdTimeFunctions } from 'pino'; +import { redactSensitiveUrl } from '../helpers/utils'; const CONTEXTS_TO_IGNORE = [ 'InstanceLoader', @@ -52,7 +53,7 @@ export function createPinoConfig(): Params { serializers: { req: (req) => ({ method: req.method, - url: req.url, + url: redactSensitiveUrl(req.url), ip: req.ip || req.remoteAddress, userAgent: req.headers?.['user-agent'], }), diff --git a/apps/server/src/ee b/apps/server/src/ee index 38158a5a..dc7ae0e3 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 38158a5ab6a4c12c90a3124604b88f05d76efd7d +Subproject commit dc7ae0e3b066df842248b2295ccfad190a0c5a93 From 4966f9b152a9275ddbc12732e09864ca445e3ea7 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:24:46 +0100 Subject: [PATCH 42/44] fix(deps): package updates --- apps/client/package.json | 4 +- apps/server/package.json | 20 +- pnpm-lock.yaml | 852 +++++++++++++-------------------------- 3 files changed, 290 insertions(+), 586 deletions(-) diff --git a/apps/client/package.json b/apps/client/package.json index 22192e56..150a9389 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -67,7 +67,7 @@ "@types/node": "22.19.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "@vitejs/plugin-react": "^6.0.0", + "@vitejs/plugin-react": "^6.0.1", "eslint": "^9.28.0", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", @@ -80,6 +80,6 @@ "prettier": "^3.8.1", "typescript": "^5.9.3", "typescript-eslint": "^8.57.1", - "vite": "^8.0.1" + "vite": "8.0.5" } } diff --git a/apps/server/package.json b/apps/server/package.json index 252d73ed..9994ba43 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -48,19 +48,19 @@ "@nestjs-labs/nestjs-ioredis": "^11.0.4", "@nestjs/bullmq": "^11.0.4", "@nestjs/cache-manager": "^3.1.0", - "@nestjs/common": "^11.1.17", + "@nestjs/common": "^11.1.18", "@nestjs/config": "^4.0.3", - "@nestjs/core": "^11.1.17", + "@nestjs/core": "^11.1.18", "@nestjs/event-emitter": "^3.0.1", "@nestjs/jwt": "11.0.2", - "@nestjs/mapped-types": "^2.1.0", + "@nestjs/mapped-types": "^2.1.1", "@nestjs/passport": "^11.0.5", - "@nestjs/platform-fastify": "^11.1.17", - "@nestjs/platform-socket.io": "^11.1.17", + "@nestjs/platform-fastify": "^11.1.18", + "@nestjs/platform-socket.io": "^11.1.18", "@nestjs/schedule": "^6.1.1", "@nestjs/terminus": "^11.1.1", "@nestjs/throttler": "^6.5.0", - "@nestjs/websockets": "^11.1.17", + "@nestjs/websockets": "^11.1.18", "@node-saml/passport-saml": "^5.1.0", "@react-email/components": "1.0.10", "@react-email/render": "2.0.4", @@ -121,9 +121,9 @@ }, "devDependencies": { "@eslint/js": "^9.28.0", - "@nestjs/cli": "^11.0.16", - "@nestjs/schematics": "^11.0.9", - "@nestjs/testing": "^11.1.17", + "@nestjs/cli": "^11.0.18", + "@nestjs/schematics": "^11.0.10", + "@nestjs/testing": "^11.1.18", "@types/bcrypt": "^6.0.0", "@types/debounce": "^1.2.4", "@types/fs-extra": "^11.0.4", @@ -146,7 +146,7 @@ "source-map-support": "^0.5.21", "supertest": "^7.2.2", "ts-jest": "^29.4.6", - "ts-loader": "^9.5.4", + "ts-loader": "^9.5.7", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "typescript": "^5.9.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c00668da..ff37ec78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -410,8 +410,8 @@ importers: specifier: ^18.3.1 version: 18.3.1 '@vitejs/plugin-react': - specifier: ^6.0.0 - version: 6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.5(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3)) eslint: specifier: ^9.28.0 version: 9.39.4(jiti@2.4.2) @@ -449,8 +449,8 @@ importers: specifier: ^8.57.1 version: 8.57.1(eslint@9.39.4(jiti@2.4.2))(typescript@5.9.3) vite: - specifier: ^8.0.1 - version: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) + specifier: 8.0.5 + version: 8.0.5(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) apps/server: dependencies: @@ -498,55 +498,55 @@ importers: version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) '@nest-lab/throttler-storage-redis': specifier: ^1.2.0 - version: 1.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2) + version: 1.2.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2) '@nestjs-labs/nestjs-ioredis': specifier: ^11.0.4 - version: 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1) + version: 11.0.4(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(ioredis@5.10.1) '@nestjs/bullmq': specifier: ^11.0.4 - version: 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(bullmq@5.71.0) + version: 11.0.4(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(bullmq@5.71.0) '@nestjs/cache-manager': specifier: ^3.1.0 - version: 3.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2) + version: 3.1.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2) '@nestjs/common': - specifier: ^11.1.17 - version: 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: ^11.1.18 + version: 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/config': specifier: ^4.0.3 - version: 4.0.3(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) + version: 4.0.3(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) '@nestjs/core': - specifier: ^11.1.17 - version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: ^11.1.18 + version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/event-emitter': specifier: ^3.0.1 - version: 3.0.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) + version: 3.0.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) '@nestjs/jwt': specifier: 11.0.2 - version: 11.0.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) + version: 11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)) '@nestjs/mapped-types': - specifier: ^2.1.0 - version: 2.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) + specifier: ^2.1.1 + version: 2.1.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2) '@nestjs/passport': specifier: ^11.0.5 - version: 11.0.5(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) + version: 11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) '@nestjs/platform-fastify': - specifier: ^11.1.17 - version: 11.1.17(@fastify/static@9.0.0)(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) + specifier: ^11.1.18 + version: 11.1.18(@fastify/static@9.0.0)(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) '@nestjs/platform-socket.io': - specifier: ^11.1.17 - version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(rxjs@7.8.2) + specifier: ^11.1.18 + version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(rxjs@7.8.2) '@nestjs/schedule': specifier: ^6.1.1 - version: 6.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) + version: 6.1.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) '@nestjs/terminus': specifier: ^11.1.1 - version: 11.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/throttler': specifier: ^6.5.0 - version: 6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2) + version: 6.5.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2) '@nestjs/websockets': - specifier: ^11.1.17 - version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + specifier: ^11.1.18 + version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/platform-socket.io@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@node-saml/passport-saml': specifier: ^5.1.0 version: 5.1.0 @@ -633,13 +633,13 @@ importers: version: 5.1.7 nestjs-cls: specifier: ^6.2.0 - version: 6.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 6.2.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) nestjs-kysely: specifier: ^3.1.2 - version: 3.1.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(kysely@0.28.14)(reflect-metadata@0.2.2) + version: 3.1.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(kysely@0.28.14)(reflect-metadata@0.2.2) nestjs-pino: specifier: ^4.6.1 - version: 4.6.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) + version: 4.6.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2) nodemailer: specifier: ^8.0.4 version: 8.0.4 @@ -723,14 +723,14 @@ importers: specifier: ^9.28.0 version: 9.39.4 '@nestjs/cli': - specifier: ^11.0.16 - version: 11.0.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0) + specifier: ^11.0.18 + version: 11.0.18(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0) '@nestjs/schematics': - specifier: ^11.0.9 - version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3) + specifier: ^11.0.10 + version: 11.0.10(chokidar@4.0.3)(typescript@5.9.3) '@nestjs/testing': - specifier: ^11.1.17 - version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) + specifier: ^11.1.18 + version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) '@types/bcrypt': specifier: ^6.0.0 version: 6.0.0 @@ -798,8 +798,8 @@ importers: specifier: ^29.4.6 version: 29.4.6(@babel/core@7.28.5)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.28.5))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: - specifier: ^9.5.4 - version: 9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))) + specifier: ^9.5.7 + version: 9.5.7(typescript@5.9.3)(webpack@5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5))) ts-node: specifier: ^10.9.2 version: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3) @@ -862,8 +862,8 @@ packages: resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==} engines: {node: '>=18'} - '@angular-devkit/core@19.2.17': - resolution: {integrity: sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==} + '@angular-devkit/core@19.2.23': + resolution: {integrity: sha512-RazHPQkUEsNU/OZ75w9UeHxGFMthRiuAW2B/uA7eXExBj/1meHrrBfoCA56ujW2GUxVjRtSrMjylKh4R4meiYA==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^4.0.0 @@ -871,26 +871,13 @@ packages: chokidar: optional: true - '@angular-devkit/core@19.2.19': - resolution: {integrity: sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - peerDependencies: - chokidar: ^4.0.0 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/schematics-cli@19.2.19': - resolution: {integrity: sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==} + '@angular-devkit/schematics-cli@19.2.23': + resolution: {integrity: sha512-M8g7Gu3Lc5bbzijd2QLcQhfdpfMVE32YXQ6FIkA8x91Kmd2gb8aVvGYPLYUN5619P+ABWhN5Dn2PKuk01zz3vg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/schematics@19.2.17': - resolution: {integrity: sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - - '@angular-devkit/schematics@19.2.19': - resolution: {integrity: sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==} + '@angular-devkit/schematics@19.2.23': + resolution: {integrity: sha512-Jzs7YM4X6azmHU7Mw5tQSPMuvaqYS8SLnZOJbtiXCy1JyuW9bm/WBBecNHMiuZ8LHXKhvQ6AVX1tKrzF6uiDmw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@antfu/install-pkg@1.1.0': @@ -2337,15 +2324,6 @@ packages: resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} - '@inquirer/checkbox@4.1.2': - resolution: {integrity: sha512-PL9ixC5YsPXzXhAZFUPmkXGxfgjkdfZdPEPPmt4kFwQ4LBMDG9n/nHXYRGGZSKZJs+d1sGKWgS2GiPzVRKUdtQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/checkbox@4.3.2': resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} engines: {node: '>=18'} @@ -2364,24 +2342,6 @@ packages: '@types/node': optional: true - '@inquirer/confirm@5.1.6': - resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/core@10.1.7': - resolution: {integrity: sha512-AA9CQhlrt6ZgiSy6qoAigiA1izOa751ugX6ioSjqgJ+/Gd+tEN/TORk5sUYNjXuHWfW0r1n/a6ak4u/NqHHrtA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/core@10.3.2': resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} engines: {node: '>=18'} @@ -2400,15 +2360,6 @@ packages: '@types/node': optional: true - '@inquirer/editor@4.2.7': - resolution: {integrity: sha512-gktCSQtnSZHaBytkJKMKEuswSk2cDBuXX5rxGFv306mwHfBPjg5UAldw9zWGoEyvA9KpRDkeM4jfrx0rXn0GyA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/expand@4.0.23': resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} engines: {node: '>=18'} @@ -2418,15 +2369,6 @@ packages: '@types/node': optional: true - '@inquirer/expand@4.0.9': - resolution: {integrity: sha512-Xxt6nhomWTAmuSX61kVgglLjMEFGa+7+F6UUtdEUeg7fg4r9vaFttUUKrtkViYYrQBA5Ia1tkOJj2koP9BuLig==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -2436,23 +2378,10 @@ packages: '@types/node': optional: true - '@inquirer/figures@1.0.10': - resolution: {integrity: sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==} - engines: {node: '>=18'} - '@inquirer/figures@1.0.15': resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} - '@inquirer/input@4.1.6': - resolution: {integrity: sha512-1f5AIsZuVjPT4ecA8AwaxDFNHny/tSershP/cTvTDxLdiIGTeILNcKozB0LaYt6mojJLUbOYhpIxicaYf7UKIQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/input@4.3.1': resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} engines: {node: '>=18'} @@ -2471,15 +2400,6 @@ packages: '@types/node': optional: true - '@inquirer/number@3.0.9': - resolution: {integrity: sha512-iN2xZvH3tyIYXLXBvlVh0npk1q/aVuKXZo5hj+K3W3D4ngAEq/DkLpofRzx6oebTUhBvOgryZ+rMV0yImKnG3w==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/password@4.0.23': resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} engines: {node: '>=18'} @@ -2489,15 +2409,6 @@ packages: '@types/node': optional: true - '@inquirer/password@4.0.9': - resolution: {integrity: sha512-xBEoOw1XKb0rIN208YU7wM7oJEHhIYkfG7LpTJAEW913GZeaoQerzf5U/LSHI45EVvjAdgNXmXgH51cUXKZcJQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/prompts@7.10.1': resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} engines: {node: '>=18'} @@ -2516,15 +2427,6 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@4.0.9': - resolution: {integrity: sha512-+5t6ebehKqgoxV8fXwE49HkSF2Rc9ijNiVGEQZwvbMI61/Q5RcD+jWD6Gs1tKdz5lkI8GRBL31iO0HjGK1bv+A==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/rawlist@4.1.11': resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} engines: {node: '>=18'} @@ -2534,15 +2436,6 @@ packages: '@types/node': optional: true - '@inquirer/search@3.0.9': - resolution: {integrity: sha512-DWmKztkYo9CvldGBaRMr0ETUHgR86zE6sPDVOHsqz4ISe9o1LuiWfgJk+2r75acFclA93J/lqzhT0dTjCzHuoA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/search@3.2.2': resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} engines: {node: '>=18'} @@ -2552,15 +2445,6 @@ packages: '@types/node': optional: true - '@inquirer/select@4.0.9': - resolution: {integrity: sha512-BpJyJe7Dkhv2kz7yG7bPSbJLQuu/rqyNlF1CfiiFeFwouegfH+zh13KDyt6+d9DwucKo7hqM3wKLLyJxZMO+Xg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@inquirer/select@4.4.2': resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} engines: {node: '>=18'} @@ -2579,15 +2463,6 @@ packages: '@types/node': optional: true - '@inquirer/type@3.0.4': - resolution: {integrity: sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@ioredis/commands@1.5.0': resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} @@ -2976,12 +2851,12 @@ packages: keyv: '>=5' rxjs: ^7.8.1 - '@nestjs/cli@11.0.16': - resolution: {integrity: sha512-P0H+Vcjki6P5160E5QnMt3Q0X5FTg4PZkP99Ig4lm/4JWqfw32j3EXv3YBTJ2DmxLwOQ/IS9F7dzKpMAgzKTGg==} + '@nestjs/cli@11.0.18': + resolution: {integrity: sha512-z72OS+sFrDgIkNu/e/vUhbnjHZwAYQS8fBJKXLiFyz8059IVuY2FKebV2YMxyhY+920d4LX1hBIAGL5qQNdR7g==} engines: {node: '>= 20.11'} hasBin: true peerDependencies: - '@swc/cli': ^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 + '@swc/cli': ^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 '@swc/core': ^1.3.62 peerDependenciesMeta: '@swc/cli': @@ -2989,8 +2864,8 @@ packages: '@swc/core': optional: true - '@nestjs/common@11.1.17': - resolution: {integrity: sha512-hLODw5Abp8OQgA+mUO4tHou4krKgDtUcM9j5Ihxncst9XeyxYBTt2bwZm4e4EQr5E352S4Fyy6V3iFx9ggxKAg==} + '@nestjs/common@11.1.18': + resolution: {integrity: sha512-0sLq8Z+TIjLnz1Tqp0C/x9BpLbqpt1qEu0VcH4/fkE0y3F5JxhfK1AdKQ/SPbKhKgwqVDoY4gS8GQr2G6ujaWg==} peerDependencies: class-transformer: '>=0.4.1' class-validator: '>=0.13.2' @@ -3008,8 +2883,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 rxjs: ^7.1.0 - '@nestjs/core@11.1.17': - resolution: {integrity: sha512-lD5mAYekTTurF3vDaa8C2OKPnjiz4tsfxIc5XlcSUzOhkwWf6Ay3HKvt6FmvuWQam6uIIHX52Clg+e6tAvf/cg==} + '@nestjs/core@11.1.18': + resolution: {integrity: sha512-wR3DtGyk/LUAiPtbXDuWJJwVkWElKBY0sqnTzf9d4uM3+X18FRZhK7WFc47czsIGOdWuRsMeLYV+1Z9dO4zDEQ==} engines: {node: '>= 20'} peerDependencies: '@nestjs/common': ^11.0.0 @@ -3037,12 +2912,12 @@ packages: peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - '@nestjs/mapped-types@2.1.0': - resolution: {integrity: sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==} + '@nestjs/mapped-types@2.1.1': + resolution: {integrity: sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==} peerDependencies: '@nestjs/common': ^10.0.0 || ^11.0.0 class-transformer: ^0.4.0 || ^0.5.0 - class-validator: ^0.13.0 || ^0.14.0 + class-validator: ^0.13.0 || ^0.14.0 || ^0.15.0 reflect-metadata: ^0.1.12 || ^0.2.0 peerDependenciesMeta: class-transformer: @@ -3056,8 +2931,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 passport: ^0.5.0 || ^0.6.0 || ^0.7.0 - '@nestjs/platform-fastify@11.1.17': - resolution: {integrity: sha512-7nndTu6I2SvIEQbQhv+GVLDpoN05OFZ7x4JhdKhJ+m+1DSNXdgAcPaM1P4FX4xBMKo4m+chbwdcTaGp0ECEnwA==} + '@nestjs/platform-fastify@11.1.18': + resolution: {integrity: sha512-iJtbqQz51k7Z1vOTUEHO1mU8PsDO1WdgPSJ/6CuXBnazkrkePXoszhefFaPwJreBVn35GE3WTd/6ou7bFwnhmA==} peerDependencies: '@fastify/static': ^8.0.0 || ^9.0.0 '@fastify/view': ^10.0.0 || ^11.0.0 @@ -3069,8 +2944,8 @@ packages: '@fastify/view': optional: true - '@nestjs/platform-socket.io@11.1.17': - resolution: {integrity: sha512-BSOAsENdmTtsnDL0hb4takbWzPy9WoPybjlM57ab3/rQgm0biMFYUupH2uzmCjmmIXJL/EFbAWznVl8xw2Sa6Q==} + '@nestjs/platform-socket.io@11.1.18': + resolution: {integrity: sha512-DiFRpMIdFaHqZQFwqLqGHMdNurrKVkRkMHxIrecjooPHJNNIMgrbpYZ+oJW8hpwifUyZUL4r4uXXRy4+yFEMjA==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/websockets': ^11.0.0 @@ -3082,8 +2957,8 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/schematics@11.0.9': - resolution: {integrity: sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==} + '@nestjs/schematics@11.0.10': + resolution: {integrity: sha512-q9lr0wGwgBHLarD4uno3XiW4JX60WPlg2VTgbqPHl/6bT4u1IEEzj+q9Tad3bVnqL5mlDF3vrZ2tj+x13CJpmw==} peerDependencies: typescript: '>=4.8.2' @@ -3135,8 +3010,8 @@ packages: typeorm: optional: true - '@nestjs/testing@11.1.17': - resolution: {integrity: sha512-lNffw+z+2USewmw4W0tsK+Rq94A2N4PiHbcqoRUu5y8fnqxQeIWGHhjo5BFCqj7eivqJBhT7WdRydxVq4rAHzg==} + '@nestjs/testing@11.1.18': + resolution: {integrity: sha512-frzwNlpBgtAzI3hp/qo57DZoRO4RMTH1wST3QUYEhRTHyfPkLpzkWz3jV/mhApXjD0yT56Ptlzn6zuYPLh87Lw==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -3155,8 +3030,8 @@ packages: '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 reflect-metadata: ^0.1.13 || ^0.2.0 - '@nestjs/websockets@11.1.17': - resolution: {integrity: sha512-YbwQ0QfVj0lxkKQhdIIgk14ZSVWDqGk1J8nNSN6SLjf36sVv58Ma5ro+dtQua8wj3l2Ub7JJCVFixEhKtYc/rQ==} + '@nestjs/websockets@11.1.18': + resolution: {integrity: sha512-HLO/QGlJoJaMMDphgjbcIwW7/8EA8nnFQjf+qPvA+wWLLfPKoiFPUezc5m9YgN2A7jmzwo6YmEAXeHyqO9tvTw==} peerDependencies: '@nestjs/common': ^11.0.0 '@nestjs/core': ^11.0.0 @@ -3326,8 +3201,8 @@ packages: resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} engines: {node: '>=14'} - '@oxc-project/types@0.120.0': - resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + '@oxc-project/types@0.122.0': + resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==} '@paralleldrive/cuid2@2.3.1': resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} @@ -4245,97 +4120,97 @@ packages: '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} - '@rolldown/binding-android-arm64@1.0.0-rc.10': - resolution: {integrity: sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==} + '@rolldown/binding-android-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.10': - resolution: {integrity: sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==} + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.10': - resolution: {integrity: sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==} + '@rolldown/binding-darwin-x64@1.0.0-rc.12': + resolution: {integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.10': - resolution: {integrity: sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==} + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': + resolution: {integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': - resolution: {integrity: sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': + resolution: {integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': - resolution: {integrity: sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': - resolution: {integrity: sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==} + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': + resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': - resolution: {integrity: sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==} + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': - resolution: {integrity: sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==} + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': - resolution: {integrity: sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': - resolution: {integrity: sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==} + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': + resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': - resolution: {integrity: sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==} + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': - resolution: {integrity: sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==} + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': + resolution: {integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': - resolution: {integrity: sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': + resolution: {integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': - resolution: {integrity: sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': + resolution: {integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.10': - resolution: {integrity: sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==} + '@rolldown/pluginutils@1.0.0-rc.12': + resolution: {integrity: sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==} '@rolldown/pluginutils@1.0.0-rc.7': resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} @@ -5998,9 +5873,6 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} @@ -6153,8 +6025,8 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - comment-json@4.4.1: - resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} + comment-json@4.6.2: + resolution: {integrity: sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==} engines: {node: '>= 6'} component-emitter@1.3.1: @@ -6755,14 +6627,14 @@ packages: resolution: {integrity: sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==} engines: {node: '>=10.2.0'} - enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} - engines: {node: '>=10.13.0'} - enhanced-resolve@5.19.0: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + engines: {node: '>=10.13.0'} + enquirer@2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} engines: {node: '>=8.6'} @@ -7002,10 +6874,6 @@ packages: exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - fast-copy@4.0.2: resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} @@ -7086,8 +6954,8 @@ packages: file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} - file-type@21.3.2: - resolution: {integrity: sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==} + file-type@21.3.4: + resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} engines: {node: '>=20'} filelist@1.0.4: @@ -7424,10 +7292,6 @@ packages: typescript: optional: true - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -9617,10 +9481,6 @@ packages: resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} engines: {node: '>=10'} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -9634,8 +9494,8 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rolldown@1.0.0-rc.10: - resolution: {integrity: sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==} + rolldown@1.0.0-rc.12: + resolution: {integrity: sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -9753,10 +9613,6 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} - serialize-javascript@7.0.3: - resolution: {integrity: sha512-h+cZ/XXarqDgCjo+YSyQU/ulDEESGGf8AMK9pPNmhNSl/FzPl6L8pMp1leca5z6NuG6tvV/auC8/43tmovowww==} - engines: {node: '>=20.0.0'} - serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -10050,10 +9906,6 @@ packages: tailwindcss@4.1.18: resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} @@ -10062,8 +9914,8 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} - terser-webpack-plugin@5.3.16: - resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} + terser-webpack-plugin@5.4.0: + resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -10195,8 +10047,8 @@ packages: jest-util: optional: true - ts-loader@9.5.4: - resolution: {integrity: sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==} + ts-loader@9.5.7: + resolution: {integrity: sha512-/ZNrKgA3K3PtpMYOC71EeMWIloGw3IYEa5/t1cyz2r5/PyUwTXGzYJvcD3kfUvmhlfpz1rhV8B2O6IVTQ0avsg==} engines: {node: '>=12.0.0'} peerDependencies: typescript: '*' @@ -10484,14 +10336,14 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@8.0.1: - resolution: {integrity: sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==} + vite@8.0.5: + resolution: {integrity: sha512-nmu43Qvq9UopTRfMx2jOYW5l16pb3iDC1JH6yMuPkpVbzK0k+L7dfsEDH4jRgYFmsg0sTAqkojoZgzLMlwHsCQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 '@vitejs/devtools': ^0.1.0 - esbuild: ^0.27.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 sass: ^1.70.0 @@ -10585,12 +10437,12 @@ packages: resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} engines: {node: '>=6'} - webpack-sources@3.3.3: - resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} engines: {node: '>=10.13.0'} - webpack@5.104.1: - resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} + webpack@5.105.4: + resolution: {integrity: sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -10825,10 +10677,6 @@ packages: resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} engines: {node: '>=12.20'} - yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - yoctocolors-cjs@2.1.3: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} @@ -10908,7 +10756,7 @@ snapshots: dependencies: json-schema: 0.4.0 - '@angular-devkit/core@19.2.17(chokidar@4.0.3)': + '@angular-devkit/core@19.2.23(chokidar@4.0.3)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -10919,21 +10767,10 @@ snapshots: optionalDependencies: chokidar: 4.0.3 - '@angular-devkit/core@19.2.19(chokidar@4.0.3)': + '@angular-devkit/schematics-cli@19.2.23(@types/node@25.5.0)(chokidar@4.0.3)': dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - jsonc-parser: 3.3.1 - picomatch: 4.0.4 - rxjs: 7.8.1 - source-map: 0.7.4 - optionalDependencies: - chokidar: 4.0.3 - - '@angular-devkit/schematics-cli@19.2.19(@types/node@25.5.0)(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) + '@angular-devkit/core': 19.2.23(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.23(chokidar@4.0.3) '@inquirer/prompts': 7.3.2(@types/node@25.5.0) ansi-colors: 4.1.3 symbol-observable: 4.0.0 @@ -10942,19 +10779,9 @@ snapshots: - '@types/node' - chokidar - '@angular-devkit/schematics@19.2.17(chokidar@4.0.3)': + '@angular-devkit/schematics@19.2.23(chokidar@4.0.3)': dependencies: - '@angular-devkit/core': 19.2.17(chokidar@4.0.3) - jsonc-parser: 3.3.1 - magic-string: 0.30.17 - ora: 5.4.1 - rxjs: 7.8.1 - transitivePeerDependencies: - - chokidar - - '@angular-devkit/schematics@19.2.19(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) + '@angular-devkit/core': 19.2.23(chokidar@4.0.3) jsonc-parser: 3.3.1 magic-string: 0.30.17 ora: 5.4.1 @@ -12333,11 +12160,11 @@ snapshots: '@emnapi/runtime@1.5.0': dependencies: tslib: 2.8.1 + optional: true '@emnapi/runtime@1.9.1': dependencies: tslib: 2.8.1 - optional: true '@emnapi/wasi-threads@1.1.0': dependencies: @@ -12785,16 +12612,6 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.1.2(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@25.5.0) - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/checkbox@4.3.2(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 @@ -12812,26 +12629,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/confirm@5.1.6(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/type': 3.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/core@10.1.7(@types/node@25.5.0)': - dependencies: - '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@25.5.0) - ansi-escapes: 4.3.2 - cli-width: 4.1.0 - mute-stream: 2.0.0 - signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.2 - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/core@10.3.2(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 @@ -12853,14 +12650,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/editor@4.2.7(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/type': 3.0.4(@types/node@25.5.0) - external-editor: 3.1.0 - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/expand@4.0.23(@types/node@25.5.0)': dependencies: '@inquirer/core': 10.3.2(@types/node@25.5.0) @@ -12869,14 +12658,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/expand@4.0.9(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/type': 3.0.4(@types/node@25.5.0) - yoctocolors-cjs: 2.1.2 - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/external-editor@1.0.3(@types/node@25.5.0)': dependencies: chardet: 2.1.1 @@ -12884,17 +12665,8 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/figures@1.0.10': {} - '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.1.6(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/type': 3.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/input@4.3.1(@types/node@25.5.0)': dependencies: '@inquirer/core': 10.3.2(@types/node@25.5.0) @@ -12909,13 +12681,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/number@3.0.9(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/type': 3.0.4(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/password@4.0.23(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 @@ -12924,14 +12689,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/password@4.0.9(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/type': 3.0.4(@types/node@25.5.0) - ansi-escapes: 4.3.2 - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/prompts@7.10.1(@types/node@25.5.0)': dependencies: '@inquirer/checkbox': 4.3.2(@types/node@25.5.0) @@ -12949,24 +12706,16 @@ snapshots: '@inquirer/prompts@7.3.2(@types/node@25.5.0)': dependencies: - '@inquirer/checkbox': 4.1.2(@types/node@25.5.0) - '@inquirer/confirm': 5.1.6(@types/node@25.5.0) - '@inquirer/editor': 4.2.7(@types/node@25.5.0) - '@inquirer/expand': 4.0.9(@types/node@25.5.0) - '@inquirer/input': 4.1.6(@types/node@25.5.0) - '@inquirer/number': 3.0.9(@types/node@25.5.0) - '@inquirer/password': 4.0.9(@types/node@25.5.0) - '@inquirer/rawlist': 4.0.9(@types/node@25.5.0) - '@inquirer/search': 3.0.9(@types/node@25.5.0) - '@inquirer/select': 4.0.9(@types/node@25.5.0) - optionalDependencies: - '@types/node': 25.5.0 - - '@inquirer/rawlist@4.0.9(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/type': 3.0.4(@types/node@25.5.0) - yoctocolors-cjs: 2.1.2 + '@inquirer/checkbox': 4.3.2(@types/node@25.5.0) + '@inquirer/confirm': 5.1.21(@types/node@25.5.0) + '@inquirer/editor': 4.2.23(@types/node@25.5.0) + '@inquirer/expand': 4.0.23(@types/node@25.5.0) + '@inquirer/input': 4.3.1(@types/node@25.5.0) + '@inquirer/number': 3.0.23(@types/node@25.5.0) + '@inquirer/password': 4.0.23(@types/node@25.5.0) + '@inquirer/rawlist': 4.1.11(@types/node@25.5.0) + '@inquirer/search': 3.2.2(@types/node@25.5.0) + '@inquirer/select': 4.4.2(@types/node@25.5.0) optionalDependencies: '@types/node': 25.5.0 @@ -12978,15 +12727,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/search@3.0.9(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@25.5.0) - yoctocolors-cjs: 2.1.2 - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/search@3.2.2(@types/node@25.5.0)': dependencies: '@inquirer/core': 10.3.2(@types/node@25.5.0) @@ -12996,16 +12736,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/select@4.0.9(@types/node@25.5.0)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@25.5.0) - '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@25.5.0) - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 - optionalDependencies: - '@types/node': 25.5.0 - '@inquirer/select@4.4.2(@types/node@25.5.0)': dependencies: '@inquirer/ansi': 1.0.2 @@ -13020,10 +12750,6 @@ snapshots: optionalDependencies: '@types/node': 25.5.0 - '@inquirer/type@3.0.4(@types/node@25.5.0)': - optionalDependencies: - '@types/node': 25.5.0 - '@ioredis/commands@1.5.0': {} '@ioredis/commands@1.5.1': {} @@ -13485,7 +13211,7 @@ snapshots: '@napi-rs/wasm-runtime@0.2.4': dependencies: '@emnapi/core': 1.8.1 - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.9.1 '@tybys/wasm-util': 0.9.0 '@napi-rs/wasm-runtime@1.1.1': @@ -13495,63 +13221,63 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2)': + '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/throttler@6.5.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2))(ioredis@5.10.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/throttler': 6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/throttler': 6.5.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2) ioredis: 5.10.1 reflect-metadata: 0.2.2 tslib: 2.8.1 - '@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(ioredis@5.10.1)': + '@nestjs-labs/nestjs-ioredis@11.0.4(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(ioredis@5.10.1)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) ioredis: 5.10.1 tslib: 2.8.1 - '@nestjs/bull-shared@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': + '@nestjs/bull-shared@11.0.4(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/bullmq@11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(bullmq@5.71.0)': + '@nestjs/bullmq@11.0.4(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(bullmq@5.71.0)': dependencies: - '@nestjs/bull-shared': 11.0.4(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/bull-shared': 11.0.4(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) bullmq: 5.71.0 tslib: 2.8.1 - '@nestjs/cache-manager@3.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2)': + '@nestjs/cache-manager@3.1.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(cache-manager@7.2.8)(keyv@5.6.0)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) cache-manager: 7.2.8 keyv: 5.6.0 rxjs: 7.8.2 - '@nestjs/cli@11.0.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)': + '@nestjs/cli@11.0.18(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)': dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics-cli': 19.2.19(@types/node@25.5.0)(chokidar@4.0.3) + '@angular-devkit/core': 19.2.23(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.23(chokidar@4.0.3) + '@angular-devkit/schematics-cli': 19.2.23(@types/node@25.5.0)(chokidar@4.0.3) '@inquirer/prompts': 7.10.1(@types/node@25.5.0) - '@nestjs/schematics': 11.0.9(chokidar@4.0.3)(typescript@5.9.3) + '@nestjs/schematics': 11.0.10(chokidar@4.0.3)(typescript@5.9.3) ansis: 4.2.0 chokidar: 4.0.3 cli-table3: 0.6.5 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))) + fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5))) glob: 13.0.6 node-emoji: 1.11.0 ora: 5.4.1 tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 typescript: 5.9.3 - webpack: 5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)) + webpack: 5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5)) webpack-node-externals: 3.0.0 optionalDependencies: '@swc/core': 1.5.25(@swc/helpers@0.5.5) @@ -13561,9 +13287,9 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - file-type: 21.3.2 + file-type: 21.3.4 iterare: 1.2.1 load-esm: 1.0.3 reflect-metadata: 0.2.2 @@ -13576,17 +13302,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/config@4.0.3(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': + '@nestjs/config@4.0.3(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) dotenv: 17.2.3 dotenv-expand: 12.0.3 lodash: 4.18.1 rxjs: 7.8.2 - '@nestjs/core@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -13596,39 +13322,39 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/websockets': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/websockets': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/platform-socket.io@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/event-emitter@3.0.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': + '@nestjs/event-emitter@3.0.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) eventemitter2: 6.4.9 - '@nestjs/jwt@11.0.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))': + '@nestjs/jwt@11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/jsonwebtoken': 9.0.10 jsonwebtoken: 9.0.3 - '@nestjs/mapped-types@2.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': + '@nestjs/mapped-types@2.1.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.15.1 - '@nestjs/passport@11.0.5(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': + '@nestjs/passport@11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) passport: 0.7.0 - '@nestjs/platform-fastify@11.1.17(@fastify/static@9.0.0)(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': + '@nestjs/platform-fastify@11.1.18(@fastify/static@9.0.0)(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)': dependencies: '@fastify/cors': 11.2.0 '@fastify/formbody': 8.0.2 - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) fast-querystring: 1.1.2 fastify: 5.8.3 fastify-plugin: 5.1.0 @@ -13640,10 +13366,10 @@ snapshots: optionalDependencies: '@fastify/static': 9.0.0 - '@nestjs/platform-socket.io@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(rxjs@7.8.2)': + '@nestjs/platform-socket.io@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/websockets': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/websockets': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/platform-socket.io@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) rxjs: 7.8.2 socket.io: 4.8.3 tslib: 2.8.1 @@ -13652,55 +13378,55 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@6.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': + '@nestjs/schedule@6.1.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) cron: 4.4.0 - '@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.9.3)': + '@nestjs/schematics@11.0.10(chokidar@4.0.3)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 19.2.17(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) - comment-json: 4.4.1 + '@angular-devkit/core': 19.2.23(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.23(chokidar@4.0.3) + comment-json: 4.6.2 jsonc-parser: 3.3.1 pluralize: 8.0.0 typescript: 5.9.3 transitivePeerDependencies: - chokidar - '@nestjs/terminus@11.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/terminus@11.1.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) boxen: 5.1.2 check-disk-space: 3.4.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 - '@nestjs/testing@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': + '@nestjs/testing@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)': + '@nestjs/throttler@6.5.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 - '@nestjs/websockets@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/websockets@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(@nestjs/platform-socket.io@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) iterare: 1.2.1 object-hash: 3.0.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@nestjs/platform-socket.io': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(rxjs@7.8.2) + '@nestjs/platform-socket.io': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(rxjs@7.8.2) '@noble/hashes@1.8.0': {} @@ -13757,7 +13483,7 @@ snapshots: '@babel/plugin-transform-runtime': 7.23.7(@babel/core@7.28.5) '@babel/preset-env': 7.23.8(@babel/core@7.28.5) '@babel/preset-typescript': 7.23.3(@babel/core@7.28.5) - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 '@nx/devkit': 22.6.1(nx@22.6.1) '@nx/workspace': 22.6.1 '@zkochan/js-yaml': 0.0.7 @@ -13907,7 +13633,7 @@ snapshots: '@opentelemetry/semantic-conventions@1.39.0': {} - '@oxc-project/types@0.120.0': {} + '@oxc-project/types@0.122.0': {} '@paralleldrive/cuid2@2.3.1': dependencies: @@ -14829,54 +14555,54 @@ snapshots: '@remirror/core-constants@3.0.0': {} - '@rolldown/binding-android-arm64@1.0.0-rc.10': + '@rolldown/binding-android-arm64@1.0.0-rc.12': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.10': + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.10': + '@rolldown/binding-darwin-x64@1.0.0-rc.12': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.10': + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': dependencies: '@napi-rs/wasm-runtime': 1.1.1 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': optional: true - '@rolldown/pluginutils@1.0.0-rc.10': {} + '@rolldown/pluginutils@1.0.0-rc.12': {} '@rolldown/pluginutils@1.0.0-rc.7': {} @@ -16211,10 +15937,10 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vitejs/plugin-react@6.0.1(vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3))': + '@vitejs/plugin-react@6.0.1(vite@8.0.5(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.5(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3) '@webassemblyjs/ast@1.14.1': dependencies: @@ -16323,9 +16049,9 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 - acorn-import-phases@1.0.4(acorn@8.15.0): + acorn-import-phases@1.0.4(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn-jsx@5.3.2(acorn@8.16.0): dependencies: @@ -16584,7 +16310,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.26.10 + '@babel/runtime': 7.29.2 cosmiconfig: 7.1.0 resolve: 1.22.8 @@ -16818,8 +16544,6 @@ snapshots: char-regex@1.0.2: {} - chardet@0.7.0: {} - chardet@2.1.1: {} check-disk-space@3.4.0: {} @@ -16984,10 +16708,9 @@ snapshots: commander@8.3.0: {} - comment-json@4.4.1: + comment-json@4.6.2: dependencies: array-timsort: 1.0.3 - core-util-is: 1.0.3 esprima: 4.0.1 component-emitter@1.3.1: {} @@ -17608,12 +17331,12 @@ snapshots: - supports-color - utf-8-validate - enhanced-resolve@5.17.1: + enhanced-resolve@5.19.0: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 - enhanced-resolve@5.19.0: + enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -18072,12 +17795,6 @@ snapshots: exsolve@1.0.7: {} - external-editor@3.1.0: - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.2.5 - fast-copy@4.0.2: {} fast-decode-uri-component@1.0.1: {} @@ -18145,7 +17862,7 @@ snapshots: fastq@1.17.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 fb-watchman@2.0.2: dependencies: @@ -18167,7 +17884,7 @@ snapshots: file-saver@2.0.5: {} - file-type@21.3.2: + file-type@21.3.4: dependencies: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.4 @@ -18230,7 +17947,7 @@ snapshots: dependencies: is-callable: 1.2.7 - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))): + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5))): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -18243,9 +17960,9 @@ snapshots: node-abort-controller: 3.1.1 schema-utils: 3.3.0 semver: 7.7.4 - tapable: 2.2.1 + tapable: 2.3.0 typescript: 5.9.3 - webpack: 5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)) + webpack: 5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5)) form-data@4.0.5: dependencies: @@ -18544,10 +18261,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -19872,23 +19585,23 @@ snapshots: neo-async@2.6.2: {} - nestjs-cls@6.2.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2): + nestjs-cls@6.2.0(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2): dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 rxjs: 7.8.2 - nestjs-kysely@3.1.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(kysely@0.28.14)(reflect-metadata@0.2.2): + nestjs-kysely@3.1.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)(kysely@0.28.14)(reflect-metadata@0.2.2): dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.18)(reflect-metadata@0.2.2)(rxjs@7.8.2) kysely: 0.28.14 reflect-metadata: 0.2.2 - nestjs-pino@4.6.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2): + nestjs-pino@4.6.1(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(pino-http@11.0.0)(pino@10.1.0)(rxjs@7.8.2): dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) pino: 10.1.0 pino-http: 11.0.0 rxjs: 7.8.2 @@ -19952,7 +19665,7 @@ snapshots: flat: 5.0.2 front-matter: 4.0.2 ignore: 7.0.5 - jest-diff: 30.2.0 + jest-diff: 30.3.0 jsonc-parser: 3.2.0 lines-and-columns: 2.0.3 minimatch: 10.2.4 @@ -21090,8 +20803,6 @@ snapshots: ret@0.5.0: {} - reusify@1.0.4: {} - reusify@1.1.0: {} rfc6902@5.2.0: {} @@ -21100,26 +20811,26 @@ snapshots: robust-predicates@3.0.2: {} - rolldown@1.0.0-rc.10: + rolldown@1.0.0-rc.12: dependencies: - '@oxc-project/types': 0.120.0 - '@rolldown/pluginutils': 1.0.0-rc.10 + '@oxc-project/types': 0.122.0 + '@rolldown/pluginutils': 1.0.0-rc.12 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.10 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.10 - '@rolldown/binding-darwin-x64': 1.0.0-rc.10 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.10 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.10 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.10 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.10 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.10 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.10 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.10 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.10 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.10 + '@rolldown/binding-android-arm64': 1.0.0-rc.12 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.12 + '@rolldown/binding-darwin-x64': 1.0.0-rc.12 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.12 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.12 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.12 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.12 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.12 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.12 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.12 rope-sequence@1.3.4: {} @@ -21268,8 +20979,6 @@ snapshots: transitivePeerDependencies: - supports-color - serialize-javascript@7.0.3: {} - serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -21622,8 +21331,6 @@ snapshots: tailwindcss@4.1.18: {} - tapable@2.2.1: {} - tapable@2.3.0: {} tar-stream@2.2.0: @@ -21634,21 +21341,20 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - terser-webpack-plugin@5.3.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))): + terser-webpack-plugin@5.4.0(@swc/core@1.5.25(@swc/helpers@0.5.5))(webpack@5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5))): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - serialize-javascript: 7.0.3 terser: 5.39.0 - webpack: 5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)) + webpack: 5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5)) optionalDependencies: '@swc/core': 1.5.25(@swc/helpers@0.5.5) terser@5.39.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.15.0 + acorn: 8.16.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -21745,15 +21451,15 @@ snapshots: babel-jest: 30.3.0(@babel/core@7.28.5) jest-util: 30.3.0 - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))): + ts-loader@9.5.7(typescript@5.9.3)(webpack@5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5))): dependencies: chalk: 4.1.2 - enhanced-resolve: 5.19.0 + enhanced-resolve: 5.20.1 micromatch: 4.0.8 semver: 7.7.4 source-map: 0.7.4 typescript: 5.9.3 - webpack: 5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)) + webpack: 5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5)) ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@25.5.0)(typescript@5.9.3): dependencies: @@ -21778,8 +21484,8 @@ snapshots: tsconfig-paths-webpack-plugin@4.2.0: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.17.1 - tapable: 2.2.1 + enhanced-resolve: 5.19.0 + tapable: 2.3.0 tsconfig-paths: 4.2.0 tsconfig-paths@4.2.0: @@ -22060,12 +21766,12 @@ snapshots: vary@1.1.2: {} - vite@8.0.1(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3): + vite@8.0.5(@types/node@22.19.1)(esbuild@0.27.4)(jiti@2.4.2)(less@4.2.0)(sugarss@5.0.1(postcss@8.5.8))(terser@5.39.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.8 - rolldown: 1.0.0-rc.10 + rolldown: 1.0.0-rc.12 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.1 @@ -22126,9 +21832,9 @@ snapshots: webpack-node-externals@3.0.0: {} - webpack-sources@3.3.3: {} + webpack-sources@3.3.4: {} - webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5)): + webpack@5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5)): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -22136,11 +21842,11 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) browserslist: 4.28.1 chrome-trace-event: 1.0.3 - enhanced-resolve: 5.19.0 + enhanced-resolve: 5.20.1 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -22152,9 +21858,9 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(@swc/core@1.5.25(@swc/helpers@0.5.5))(webpack@5.104.1(@swc/core@1.5.25(@swc/helpers@0.5.5))) + terser-webpack-plugin: 5.4.0(@swc/core@1.5.25(@swc/helpers@0.5.5))(webpack@5.105.4(@swc/core@1.5.25(@swc/helpers@0.5.5))) watchpack: 2.5.1 - webpack-sources: 3.3.3 + webpack-sources: 3.3.4 transitivePeerDependencies: - '@swc/core' - esbuild @@ -22403,8 +22109,6 @@ snapshots: yocto-queue@1.2.1: {} - yoctocolors-cjs@2.1.2: {} - yoctocolors-cjs@2.1.3: {} yoctocolors@2.1.2: {} From da9b43681e0e087eb10fe38e05dd7a94e8203377 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Thu, 9 Apr 2026 00:37:51 +0100 Subject: [PATCH 43/44] feat: watch space (#2096) --- .../public/locales/en-US/translation.json | 4 + .../components/sidebar/space-sidebar.tsx | 143 ++++++++++++------ .../space/queries/space-watcher-query.ts | 49 ++++++ .../space/services/space-watcher-service.ts | 28 ++++ .../services/page.notification.ts | 18 ++- .../src/core/watcher/dto/space-watcher.dto.ts | 7 + .../core/watcher/space-watcher.controller.ts | 95 ++++++++++++ .../src/core/watcher/watcher.controller.ts | 7 +- .../server/src/core/watcher/watcher.module.ts | 3 +- .../src/core/watcher/watcher.service.ts | 34 ++++- .../database/repos/watcher/watcher.repo.ts | 130 +++++++++++++--- .../emails/page-update-email.tsx | 6 +- 12 files changed, 449 insertions(+), 75 deletions(-) create mode 100644 apps/client/src/features/space/queries/space-watcher-query.ts create mode 100644 apps/client/src/features/space/services/space-watcher-service.ts create mode 100644 apps/server/src/core/watcher/dto/space-watcher.dto.ts create mode 100644 apps/server/src/core/watcher/space-watcher.controller.ts diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 3ecf103b..5db91598 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -677,6 +677,8 @@ "{{name}} updated a page": "{{name}} updated a page", "Watch page": "Watch page", "Stop watching": "Stop watching", + "Watch space": "Watch space", + "Stop watching space": "Stop watching space", "Email notifications": "Email notifications", "Page updates": "Page updates", "Get notified when pages you watch are updated.": "Receive notifications when the pages you watch are updated.", @@ -690,6 +692,8 @@ "Get notified when your comment is resolved.": "Receive a notification when your comment is resolved.", "You are now watching this page": "You’re now watching this page", "You are no longer watching this page": "You’re no longer watching this page", + "You are now watching this space": "You’re now watching this space", + "You are no longer watching this space": "You’re no longer watching this space", "Direct": "Direct", "Updates": "Updates", "Today": "Today", diff --git a/apps/client/src/features/space/components/sidebar/space-sidebar.tsx b/apps/client/src/features/space/components/sidebar/space-sidebar.tsx index ced237be..d8032212 100644 --- a/apps/client/src/features/space/components/sidebar/space-sidebar.tsx +++ b/apps/client/src/features/space/components/sidebar/space-sidebar.tsx @@ -9,6 +9,8 @@ import { import { IconArrowDown, IconDots, + IconEye, + IconEyeOff, IconFileExport, IconHome, IconPlus, @@ -16,6 +18,11 @@ import { IconSettings, IconTrash, } from "@tabler/icons-react"; +import { + useSpaceWatchStatusQuery, + useWatchSpaceMutation, + useUnwatchSpaceMutation, +} from "@/features/space/queries/space-watcher-query.ts"; import classes from "./space-sidebar.module.css"; import React from "react"; import { useAtom } from "jotai"; @@ -160,13 +167,20 @@ export function SpaceSidebar() { {t("Pages")} - {spaceAbility.can( - SpaceCaslAction.Manage, - SpaceCaslSubject.Page, - ) && ( - - + + + {spaceAbility.can( + SpaceCaslAction.Manage, + SpaceCaslSubject.Page, + ) && ( - - )} + )} +
@@ -204,9 +218,14 @@ export function SpaceSidebar() { interface SpaceMenuProps { spaceId: string; + canManagePages: boolean; onSpaceSettings: () => void; } -function SpaceMenu({ spaceId, onSpaceSettings }: SpaceMenuProps) { +function SpaceMenu({ + spaceId, + canManagePages, + onSpaceSettings, +}: SpaceMenuProps) { const { t } = useTranslation(); const { spaceSlug } = useParams(); const [importOpened, { open: openImportModal, close: closeImportModal }] = @@ -214,15 +233,24 @@ function SpaceMenu({ spaceId, onSpaceSettings }: SpaceMenuProps) { const [exportOpened, { open: openExportModal, close: closeExportModal }] = useDisclosure(false); + const { data: watchStatus } = useSpaceWatchStatusQuery(spaceId); + const watchMutation = useWatchSpaceMutation(); + const unwatchMutation = useUnwatchSpaceMutation(); + const isWatching = watchStatus?.watching ?? false; + + const handleToggleWatch = () => { + if (isWatching) { + unwatchMutation.mutate(spaceId); + } else { + watchMutation.mutate(spaceId); + } + }; + return ( <> - + } + onClick={handleToggleWatch} + leftSection={ + isWatching ? : + } > - {t("Import pages")} + {isWatching ? t("Stop watching space") : t("Watch space")} - } - > - {t("Export space")} - + {canManagePages && ( + <> + - + } + > + {t("Import pages")} + - } - > - {t("Space settings")} - + } + > + {t("Export space")} + - } - > - {t("Trash")} - + + + } + > + {t("Space settings")} + + + } + > + {t("Trash")} + + + )} - + {canManagePages && ( + <> + - + + + )} ); } diff --git a/apps/client/src/features/space/queries/space-watcher-query.ts b/apps/client/src/features/space/queries/space-watcher-query.ts new file mode 100644 index 00000000..ae4d5696 --- /dev/null +++ b/apps/client/src/features/space/queries/space-watcher-query.ts @@ -0,0 +1,49 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + watchSpace, + unwatchSpace, + getSpaceWatchStatus, +} from "@/features/space/services/space-watcher-service"; +import { notifications } from "@mantine/notifications"; +import { useTranslation } from "react-i18next"; + +const SPACE_WATCHER_KEY = "space-watcher"; + +export function useSpaceWatchStatusQuery(spaceId: string) { + return useQuery({ + queryKey: [SPACE_WATCHER_KEY, spaceId], + queryFn: () => getSpaceWatchStatus(spaceId), + enabled: !!spaceId, + staleTime: 60_000, + }); +} + +export function useWatchSpaceMutation() { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + return useMutation({ + mutationFn: (spaceId: string) => watchSpace(spaceId), + onSuccess: (_data, spaceId) => { + queryClient.setQueryData([SPACE_WATCHER_KEY, spaceId], { + watching: true, + }); + notifications.show({ message: t("You are now watching this space") }); + }, + }); +} + +export function useUnwatchSpaceMutation() { + const queryClient = useQueryClient(); + const { t } = useTranslation(); + return useMutation({ + mutationFn: (spaceId: string) => unwatchSpace(spaceId), + onSuccess: (_data, spaceId) => { + queryClient.setQueryData([SPACE_WATCHER_KEY, spaceId], { + watching: false, + }); + notifications.show({ + message: t("You are no longer watching this space"), + }); + }, + }); +} diff --git a/apps/client/src/features/space/services/space-watcher-service.ts b/apps/client/src/features/space/services/space-watcher-service.ts new file mode 100644 index 00000000..bcbeccc9 --- /dev/null +++ b/apps/client/src/features/space/services/space-watcher-service.ts @@ -0,0 +1,28 @@ +import api from "@/lib/api-client"; + +export async function watchSpace( + spaceId: string, +): Promise<{ watching: boolean }> { + const req = await api.post<{ watching: boolean }>("/spaces/watch", { + spaceId, + }); + return req.data; +} + +export async function unwatchSpace( + spaceId: string, +): Promise<{ watching: boolean }> { + const req = await api.post<{ watching: boolean }>("/spaces/unwatch", { + spaceId, + }); + return req.data; +} + +export async function getSpaceWatchStatus( + spaceId: string, +): Promise<{ watching: boolean }> { + const req = await api.post<{ watching: boolean }>("/spaces/watch-status", { + spaceId, + }); + return req.data; +} diff --git a/apps/server/src/core/notification/services/page.notification.ts b/apps/server/src/core/notification/services/page.notification.ts index 9e5c75dd..77ab967a 100644 --- a/apps/server/src/core/notification/services/page.notification.ts +++ b/apps/server/src/core/notification/services/page.notification.ts @@ -179,7 +179,11 @@ export class PageNotificationService { async processPageUpdate(data: IPageUpdateNotificationJob, appUrl: string) { const { pageId, spaceId, workspaceId, actorIds } = data; - const watcherIds = await this.watcherRepo.getPageWatcherIds(pageId); + const watcherIds = await this.watcherRepo.getPageUpdateRecipientIds( + pageId, + spaceId, + ); + if (watcherIds.length === 0) return; const actorSet = new Set(actorIds); @@ -219,7 +223,7 @@ export class PageNotificationService { const context = await this.getPageContext(actorId, pageId, spaceId, appUrl); if (!context) return; - const { actor, pageTitle, basePageUrl } = context; + const { actor, pageTitle, basePageUrl, spaceName } = context; for (const userId of recipientIds) { const notification = await this.notificationService.create({ @@ -243,6 +247,7 @@ export class PageNotificationService { actorName: actor.name, pageTitle, pageUrl: basePageUrl, + spaceName, }), NotificationType.PAGE_UPDATED, ); @@ -421,7 +426,7 @@ export class PageNotificationService { .executeTakeFirst(), this.db .selectFrom('spaces') - .select(['id', 'slug']) + .select(['id', 'slug', 'name']) .where('id', '=', spaceId) .executeTakeFirst(), ]); @@ -432,6 +437,11 @@ export class PageNotificationService { const basePageUrl = `${appUrl}/s/${space.slug}/p/${page.slugId}`; - return { actor, pageTitle: getPageTitle(page.title), basePageUrl }; + return { + actor, + pageTitle: getPageTitle(page.title), + basePageUrl, + spaceName: space.name, + }; } } diff --git a/apps/server/src/core/watcher/dto/space-watcher.dto.ts b/apps/server/src/core/watcher/dto/space-watcher.dto.ts new file mode 100644 index 00000000..1df06010 --- /dev/null +++ b/apps/server/src/core/watcher/dto/space-watcher.dto.ts @@ -0,0 +1,7 @@ +import { IsString, IsNotEmpty } from 'class-validator'; + +export class SpaceWatcherDto { + @IsString() + @IsNotEmpty() + spaceId: string; +} diff --git a/apps/server/src/core/watcher/space-watcher.controller.ts b/apps/server/src/core/watcher/space-watcher.controller.ts new file mode 100644 index 00000000..455c7d0d --- /dev/null +++ b/apps/server/src/core/watcher/space-watcher.controller.ts @@ -0,0 +1,95 @@ +import { + Body, + Controller, + ForbiddenException, + HttpCode, + HttpStatus, + NotFoundException, + Post, + UseGuards, +} from '@nestjs/common'; +import { WatcherService } from './watcher.service'; +import { AuthUser } from '../../common/decorators/auth-user.decorator'; +import { AuthWorkspace } from '../../common/decorators/auth-workspace.decorator'; +import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; +import { User, Workspace } from '@docmost/db/types/entity.types'; +import { SpaceWatcherDto } from './dto/space-watcher.dto'; +import { SpaceRepo } from '@docmost/db/repos/space/space.repo'; +import SpaceAbilityFactory from '../casl/abilities/space-ability.factory'; +import { + SpaceCaslAction, + SpaceCaslSubject, +} from '../casl/interfaces/space-ability.type'; + +@UseGuards(JwtAuthGuard) +@Controller('spaces') +export class SpaceWatcherController { + constructor( + private readonly watcherService: WatcherService, + private readonly spaceRepo: SpaceRepo, + private readonly spaceAbility: SpaceAbilityFactory, + ) {} + + private async loadSpaceAndAuthorize( + spaceId: string, + user: User, + workspace: Workspace, + ) { + const space = await this.spaceRepo.findById(spaceId, workspace.id); + if (!space) { + throw new NotFoundException('Space not found'); + } + + const ability = await this.spaceAbility.createForUser(user, space.id); + if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Settings)) { + throw new ForbiddenException(); + } + + return space; + } + + @HttpCode(HttpStatus.OK) + @Post('watch') + async watchSpace( + @Body() dto: SpaceWatcherDto, + @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, + ) { + const space = await this.loadSpaceAndAuthorize(dto.spaceId, user, workspace); + + await this.watcherService.watchSpace(user.id, space.id, workspace.id); + + return { watching: true }; + } + + @HttpCode(HttpStatus.OK) + @Post('unwatch') + async unwatchSpace( + @Body() dto: SpaceWatcherDto, + @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, + ) { + const space = await this.loadSpaceAndAuthorize(dto.spaceId, user, workspace); + + await this.watcherService.unwatchSpace(user.id, space.id); + + return { watching: false }; + } + + @HttpCode(HttpStatus.OK) + @Post('watch-status') + async getWatchStatus( + @Body() dto: SpaceWatcherDto, + @AuthUser() user: User, + @AuthWorkspace() workspace: Workspace, + ) { + const space = await this.loadSpaceAndAuthorize(dto.spaceId, user, workspace); + + const watching = await this.watcherService.isWatchingSpace( + user.id, + space.id, + ); + + return { watching }; + } +} diff --git a/apps/server/src/core/watcher/watcher.controller.ts b/apps/server/src/core/watcher/watcher.controller.ts index cd10fa37..24c317f6 100644 --- a/apps/server/src/core/watcher/watcher.controller.ts +++ b/apps/server/src/core/watcher/watcher.controller.ts @@ -59,7 +59,12 @@ export class WatcherController { await this.pageAccessService.validateCanView(page, user); - await this.watcherService.unwatchPage(user.id, page.id); + await this.watcherService.unwatchPage( + user.id, + page.id, + page.spaceId, + page.workspaceId, + ); return { watching: false }; } diff --git a/apps/server/src/core/watcher/watcher.module.ts b/apps/server/src/core/watcher/watcher.module.ts index 76267b5a..357b8352 100644 --- a/apps/server/src/core/watcher/watcher.module.ts +++ b/apps/server/src/core/watcher/watcher.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { WatcherService } from './watcher.service'; import { WatcherController } from './watcher.controller'; +import { SpaceWatcherController } from './space-watcher.controller'; import { PageAccessModule } from '../page/page-access/page-access.module'; @Module({ imports: [PageAccessModule], - controllers: [WatcherController], + controllers: [WatcherController, SpaceWatcherController], providers: [WatcherService], exports: [WatcherService], }) diff --git a/apps/server/src/core/watcher/watcher.service.ts b/apps/server/src/core/watcher/watcher.service.ts index 384a0787..3c5fe621 100644 --- a/apps/server/src/core/watcher/watcher.service.ts +++ b/apps/server/src/core/watcher/watcher.service.ts @@ -50,14 +50,44 @@ export class WatcherService { return this.watcherRepo.insertMany(watchers, trx); } - async unwatchPage(userId: string, pageId: string) { - return this.watcherRepo.mute(userId, pageId); + async unwatchPage( + userId: string, + pageId: string, + spaceId: string, + workspaceId: string, + ) { + return this.watcherRepo.mute(userId, pageId, spaceId, workspaceId); } async isWatchingPage(userId: string, pageId: string): Promise { return this.watcherRepo.isWatching(userId, pageId); } + async watchSpace( + userId: string, + spaceId: string, + workspaceId: string, + trx?: KyselyTransaction, + ) { + const watcher: InsertableWatcher = { + userId, + pageId: null, + spaceId, + workspaceId, + type: WatcherType.SPACE, + addedById: userId, + }; + return this.watcherRepo.upsertSpace(watcher, trx); + } + + async unwatchSpace(userId: string, spaceId: string) { + return this.watcherRepo.deleteSpaceWatch(userId, spaceId); + } + + async isWatchingSpace(userId: string, spaceId: string): Promise { + return this.watcherRepo.isWatchingSpace(userId, spaceId); + } + async getPageWatchers(pageId: string, pagination: PaginationOptions) { return this.watcherRepo.findPageWatchers(pageId, pagination); } diff --git a/apps/server/src/database/repos/watcher/watcher.repo.ts b/apps/server/src/database/repos/watcher/watcher.repo.ts index 9739b4de..f1506ff9 100644 --- a/apps/server/src/database/repos/watcher/watcher.repo.ts +++ b/apps/server/src/database/repos/watcher/watcher.repo.ts @@ -20,18 +20,6 @@ export type WatcherType = (typeof WatcherType)[keyof typeof WatcherType]; export class WatcherRepo { constructor(@InjectKysely() private readonly db: KyselyDB) {} - async findByUserAndPage( - userId: string, - pageId: string, - ): Promise { - return this.db - .selectFrom('watchers') - .selectAll() - .where('userId', '=', userId) - .where('pageId', '=', pageId) - .executeTakeFirst(); - } - async findPageWatchers(pageId: string, pagination: PaginationOptions) { const query = this.db .selectFrom('watchers') @@ -66,6 +54,53 @@ export class WatcherRepo { return watchers.map((w) => w.userId); } + /** + * Recipients for a `page.updated` notification, combining: + * - Active page watchers on this page, AND + * - Active space watchers on this space, EXCLUDING any user who has a + * muted page watcher row for this page (per-page mute always wins). + * + * Deduplicated at the SQL level — a user watching both the page and the + * containing space appears once. + */ + async getPageUpdateRecipientIds( + pageId: string, + spaceId: string, + trx?: KyselyTransaction, + ): Promise { + const db = dbOrTx(this.db, trx); + + const pageWatchers = db + .selectFrom('watchers') + .select('userId') + .where('pageId', '=', pageId) + .where('type', '=', WatcherType.PAGE) + .where('mutedAt', 'is', null); + + const spaceWatchers = db + .selectFrom('watchers as sw') + .select('sw.userId') + .where('sw.spaceId', '=', spaceId) + .where('sw.pageId', 'is', null) + .where('sw.type', '=', WatcherType.SPACE) + .where((eb) => + eb.not( + eb.exists( + eb + .selectFrom('watchers as pw') + .select('pw.id') + .whereRef('pw.userId', '=', 'sw.userId') + .where('pw.pageId', '=', pageId) + .where('pw.type', '=', WatcherType.PAGE) + .where('pw.mutedAt', 'is not', null), + ), + ), + ); + + const rows = await pageWatchers.union(spaceWatchers).execute(); + return [...new Set(rows.map((r) => r.userId))]; + } + async insert( watcher: InsertableWatcher, trx?: KyselyTransaction, @@ -110,20 +145,81 @@ export class WatcherRepo { .executeTakeFirst(); } + async upsertSpace( + watcher: InsertableWatcher, + trx?: KyselyTransaction, + ): Promise { + const db = dbOrTx(this.db, trx); + return db + .insertInto('watchers') + .values(watcher) + .onConflict((oc) => + oc + .columns(['userId', 'spaceId']) + .where('pageId', 'is', null) + .doNothing(), + ) + .returningAll() + .executeTakeFirst(); + } + async mute( userId: string, pageId: string, + spaceId: string, + workspaceId: string, + trx?: KyselyTransaction, + ): Promise { + const db = dbOrTx(this.db, trx); + const mutedAt = new Date(); + await db + .insertInto('watchers') + .values({ + userId, + pageId, + spaceId, + workspaceId, + type: WatcherType.PAGE, + addedById: userId, + mutedAt, + }) + .onConflict((oc) => + oc + .columns(['userId', 'pageId']) + .where('pageId', 'is not', null) + .doUpdateSet({ mutedAt }), + ) + .execute(); + } + + async deleteSpaceWatch( + userId: string, + spaceId: string, trx?: KyselyTransaction, ): Promise { const db = dbOrTx(this.db, trx); await db - .updateTable('watchers') - .set({ mutedAt: new Date() }) + .deleteFrom('watchers') .where('userId', '=', userId) - .where('pageId', '=', pageId) + .where('spaceId', '=', spaceId) + .where('pageId', 'is', null) + .where('type', '=', WatcherType.SPACE) .execute(); } + async isWatchingSpace(userId: string, spaceId: string): Promise { + const watcher = await this.db + .selectFrom('watchers') + .select('id') + .where('userId', '=', userId) + .where('spaceId', '=', spaceId) + .where('pageId', 'is', null) + .where('type', '=', WatcherType.SPACE) + .executeTakeFirst(); + + return !!watcher; + } + async isWatching(userId: string, pageId: string): Promise { const watcher = await this.db .selectFrom('watchers') @@ -164,14 +260,14 @@ export class WatcherRepo { .where('spaceId', '=', spaceId) .where('userId', 'is not', null) .union( - this.db + db .selectFrom('spaceMembers') .innerJoin('groupUsers', 'groupUsers.groupId', 'spaceMembers.groupId') .select('groupUsers.userId') .where('spaceMembers.spaceId', '=', spaceId), ); - await this.db + await db .deleteFrom('watchers') .where('userId', 'in', userIds) .where('spaceId', '=', spaceId) diff --git a/apps/server/src/integrations/transactional/emails/page-update-email.tsx b/apps/server/src/integrations/transactional/emails/page-update-email.tsx index 188d8a34..c4c85769 100644 --- a/apps/server/src/integrations/transactional/emails/page-update-email.tsx +++ b/apps/server/src/integrations/transactional/emails/page-update-email.tsx @@ -8,6 +8,7 @@ interface Props { actorName: string; pageTitle: string; pageUrl: string; + spaceName: string; } export const PageUpdateEmail = ({ @@ -15,6 +16,7 @@ export const PageUpdateEmail = ({ actorName, pageTitle, pageUrl, + spaceName, }: Props) => { return ( @@ -24,8 +26,8 @@ export const PageUpdateEmail = ({ {actorName} updated{' '} {pageTitle} - - . + {' '} + in {spaceName}. View page From 57efb91bd3ad626bcbc360f344be796018c10572 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Fri, 10 Apr 2026 19:23:47 +0100 Subject: [PATCH 44/44] feat(ee): ai chat (#2098) * feat: ai chat * feat: ai chat * sync * cleanup * view space button --- .../public/locales/en-US/translation.json | 30 +- apps/client/src/App.tsx | 3 + apps/client/src/components/common/copy.tsx | 7 +- .../layouts/global/app-header.module.css | 16 + .../components/layouts/global/app-header.tsx | 94 +++- .../src/components/layouts/global/aside.tsx | 17 +- .../layouts/global/global-app-shell.tsx | 5 +- .../components/ui/card-carousel.module.css | 68 +++ .../src/components/ui/card-carousel.tsx | 77 ++++ .../ee/ai-chat/components/ai-chat-layout.tsx | 106 +++++ .../components/ai-chat-sidebar-item.tsx | 166 +++++++ .../ee/ai-chat/components/ai-chat-sidebar.tsx | 224 ++++++++++ .../ai-chat/components/aside-chat-history.tsx | 67 +++ .../ai-chat/components/aside-chat-panel.tsx | 258 +++++++++++ .../ai-chat/components/chat-empty-state.tsx | 91 ++++ .../src/ee/ai-chat/components/chat-input.tsx | 409 ++++++++++++++++++ .../ai-chat/components/chat-message-list.tsx | 174 ++++++++ .../ee/ai-chat/components/chat-message.tsx | 139 ++++++ .../ee/ai-chat/components/chat-tool-group.tsx | 56 +++ .../ai-chat/components/chat-tool-result.tsx | 49 +++ .../ee/ai-chat/components/enable-ai-chat.tsx | 67 +++ .../src/ee/ai-chat/hooks/use-chat-stream.ts | 227 ++++++++++ apps/client/src/ee/ai-chat/pages/ai-chat.tsx | 39 ++ .../src/ee/ai-chat/queries/ai-chat-query.ts | 61 +++ .../ee/ai-chat/services/ai-chat-service.ts | 144 ++++++ .../src/ee/ai-chat/styles/ai-chat.module.css | 169 ++++++++ .../styles/aside-chat-panel.module.css | 139 ++++++ .../ee/ai-chat/styles/chat-input.module.css | 242 +++++++++++ .../ee/ai-chat/styles/chat-message.module.css | 286 ++++++++++++ .../ee/ai-chat/styles/chat-sidebar.module.css | 138 ++++++ .../src/ee/ai-chat/types/ai-chat.types.ts | 49 +++ apps/client/src/ee/ai/pages/ai-settings.tsx | 2 + .../components/mention/mention-list.tsx | 2 +- .../components/mention/mention-suggestion.ts | 4 +- .../home/components/home-ai-prompt.module.css | 28 ++ .../home/components/home-ai-prompt.tsx | 60 +++ .../components/search-control.module.css | 2 + .../components/space-carousel.module.css | 22 + .../space/components/space-carousel.tsx | 77 ++++ .../workspace/types/workspace.types.ts | 1 + apps/client/src/pages/dashboard/home.tsx | 9 +- apps/server/package.json | 2 + .../core/attachment/attachment.constants.ts | 1 + .../core/attachment/attachment.controller.ts | 30 +- .../src/core/attachment/attachment.utils.ts | 2 + .../processors/attachment.processor.ts | 5 + .../attachment/services/attachment.service.ts | 25 ++ apps/server/src/core/auth/auth.controller.ts | 11 +- .../workspace/dto/update-workspace.dto.ts | 4 + .../workspace/services/workspace.service.ts | 17 +- .../migrations/20260409T132415-ai-chat.ts | 118 +++++ .../repos/attachment/attachment.repo.ts | 51 +++ apps/server/src/database/types/db.d.ts | 28 ++ .../server/src/database/types/entity.types.ts | 17 + apps/server/src/ee | 2 +- .../environment/environment.service.ts | 7 + .../src/integrations/export/export.service.ts | 2 +- .../queue/constants/queue.constants.ts | 1 + .../integrations/throttle/throttle.module.ts | 6 +- .../integrations/throttle/throttler-names.ts | 2 + .../throttle/user-throttler.guard.ts | 13 + .../src/lib/markdown/utils/marked.utils.ts | 7 +- pnpm-lock.yaml | 22 + 63 files changed, 4149 insertions(+), 48 deletions(-) create mode 100644 apps/client/src/components/ui/card-carousel.module.css create mode 100644 apps/client/src/components/ui/card-carousel.tsx create mode 100644 apps/client/src/ee/ai-chat/components/ai-chat-layout.tsx create mode 100644 apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx create mode 100644 apps/client/src/ee/ai-chat/components/ai-chat-sidebar.tsx create mode 100644 apps/client/src/ee/ai-chat/components/aside-chat-history.tsx create mode 100644 apps/client/src/ee/ai-chat/components/aside-chat-panel.tsx create mode 100644 apps/client/src/ee/ai-chat/components/chat-empty-state.tsx create mode 100644 apps/client/src/ee/ai-chat/components/chat-input.tsx create mode 100644 apps/client/src/ee/ai-chat/components/chat-message-list.tsx create mode 100644 apps/client/src/ee/ai-chat/components/chat-message.tsx create mode 100644 apps/client/src/ee/ai-chat/components/chat-tool-group.tsx create mode 100644 apps/client/src/ee/ai-chat/components/chat-tool-result.tsx create mode 100644 apps/client/src/ee/ai-chat/components/enable-ai-chat.tsx create mode 100644 apps/client/src/ee/ai-chat/hooks/use-chat-stream.ts create mode 100644 apps/client/src/ee/ai-chat/pages/ai-chat.tsx create mode 100644 apps/client/src/ee/ai-chat/queries/ai-chat-query.ts create mode 100644 apps/client/src/ee/ai-chat/services/ai-chat-service.ts create mode 100644 apps/client/src/ee/ai-chat/styles/ai-chat.module.css create mode 100644 apps/client/src/ee/ai-chat/styles/aside-chat-panel.module.css create mode 100644 apps/client/src/ee/ai-chat/styles/chat-input.module.css create mode 100644 apps/client/src/ee/ai-chat/styles/chat-message.module.css create mode 100644 apps/client/src/ee/ai-chat/styles/chat-sidebar.module.css create mode 100644 apps/client/src/ee/ai-chat/types/ai-chat.types.ts create mode 100644 apps/client/src/features/home/components/home-ai-prompt.module.css create mode 100644 apps/client/src/features/home/components/home-ai-prompt.tsx create mode 100644 apps/client/src/features/space/components/space-carousel.module.css create mode 100644 apps/client/src/features/space/components/space-carousel.tsx create mode 100644 apps/server/src/database/migrations/20260409T132415-ai-chat.ts create mode 100644 apps/server/src/integrations/throttle/throttler-names.ts create mode 100644 apps/server/src/integrations/throttle/user-throttler.guard.ts diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 5db91598..b83b3548 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -627,6 +627,7 @@ "AI Answer": "AI Answer", "Ask AI": "Ask AI", "AI is thinking...": "AI is thinking...", + "Thinking": "Thinking", "Ask a question...": "Ask a question...", "AI Answers": "AI Answers", "AI-powered search (AI Answers)": "AI-powered search (AI Answers)", @@ -755,5 +756,32 @@ "Publish": "Publish", "Security": "Security", "Enforce SSO": "Enforce SSO", - "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to login with email and password." + "Once enforced, members will not be able to login with email and password.": "Once enforced, members will not be able to login with email and password.", + "AI-generated content may not be accurate.": "AI-generated content may not be accurate.", + "AI Chat": "AI Chat", + "Analyze for insights": "Analyze for insights", + "Ask anything...": "Ask anything...", + "Chat history": "Chat history", + "Chat name": "Chat name", + "Close": "Close", + "Docmost AI": "Docmost AI", + "Failed to load chat. An error occurred.": "Failed to load chat. An error occurred.", + "Failed to render this message.": "Failed to render this message.", + "How can I help you today?": "How can I help you today?", + "New chat": "New chat", + "No chat history": "No chat history", + "No chats found": "No chats found", + "No conversations yet": "No conversations yet", + "Open full page": "Open full page", + "Previous 7 days": "Previous 7 days", + "Previous 30 days": "Previous 30 days", + "Search chats...": "Search chats...", + "Start a new chat to see it here.": "Start a new chat to see it here.", + "Summarize this page": "Summarize this page", + "Toggle AI Chat": "Toggle AI Chat", + "Translate this page": "Translate this page", + "Try a different search term.": "Try a different search term.", + "Try again": "Try again", + "Untitled chat": "Untitled chat", + "What can I help you with?": "What can I help you with?" } diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx index b99e63b3..382660c7 100644 --- a/apps/client/src/App.tsx +++ b/apps/client/src/App.tsx @@ -38,6 +38,7 @@ import UserApiKeys from "@/ee/api-key/pages/user-api-keys"; import WorkspaceApiKeys from "@/ee/api-key/pages/workspace-api-keys"; import AiSettings from "@/ee/ai/pages/ai-settings.tsx"; import AuditLogs from "@/ee/audit/pages/audit-logs.tsx"; +import AiChat from "@/ee/ai-chat/pages/ai-chat.tsx"; import VerifyEmail from "@/ee/pages/verify-email.tsx"; export default function App() { @@ -81,6 +82,8 @@ export default function App() { }> } /> + } /> + } /> } /> } /> } /> diff --git a/apps/client/src/components/common/copy.tsx b/apps/client/src/components/common/copy.tsx index 81a70771..745fc4ba 100644 --- a/apps/client/src/components/common/copy.tsx +++ b/apps/client/src/components/common/copy.tsx @@ -1,4 +1,4 @@ -import { ActionIcon, Tooltip } from "@mantine/core"; +import { ActionIcon, MantineColor, MantineSize, Tooltip } from "@mantine/core"; import { CopyButton } from "@/components/common/copy-button"; import { IconCheck, IconCopy } from "@tabler/icons-react"; import React from "react"; @@ -6,8 +6,10 @@ import { useTranslation } from "react-i18next"; interface CopyProps { text: string; + size?: MantineSize; + color?: MantineColor; } -export default function CopyTextButton({ text }: CopyProps) { +export default function CopyTextButton({ text, size }: CopyProps) { const { t } = useTranslation(); return ( @@ -22,6 +24,7 @@ export default function CopyTextButton({ text }: CopyProps) { color={copied ? "teal" : "gray"} variant="subtle" onClick={copy} + size={size} > {copied ? : } diff --git a/apps/client/src/components/layouts/global/app-header.module.css b/apps/client/src/components/layouts/global/app-header.module.css index b2298f13..a70b57e7 100644 --- a/apps/client/src/components/layouts/global/app-header.module.css +++ b/apps/client/src/components/layouts/global/app-header.module.css @@ -7,6 +7,19 @@ padding-right: var(--mantine-spacing-md); } +.brand { + display: flex; + align-items: center; + text-decoration: none; + color: inherit; + cursor: pointer; +} + +.brandIcon { + display: flex; + align-items: center; +} + .link { display: block; line-height: 1; @@ -16,6 +29,9 @@ color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); font-size: var(--mantine-font-size-sm); font-weight: 500; + user-select: none; + white-space: nowrap; + flex-shrink: 0; @mixin hover { background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); diff --git a/apps/client/src/components/layouts/global/app-header.tsx b/apps/client/src/components/layouts/global/app-header.tsx index 58b76b71..9add5d7a 100644 --- a/apps/client/src/components/layouts/global/app-header.tsx +++ b/apps/client/src/components/layouts/global/app-header.tsx @@ -1,8 +1,18 @@ -import { Badge, Group, Text, Tooltip } from "@mantine/core"; +import { + ActionIcon, + Badge, + Box, + Group, + Text, + Tooltip, + UnstyledButton, +} from "@mantine/core"; import classes from "./app-header.module.css"; import React from "react"; import TopMenu from "@/components/layouts/global/top-menu.tsx"; -import { Link } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; +import { IconSparkles } from "@tabler/icons-react"; +import useToggleAside from "@/hooks/use-toggle-aside.tsx"; import APP_ROUTE from "@/lib/app-route.ts"; import { useAtom } from "jotai"; import { @@ -23,8 +33,11 @@ import { shareSearchSpotlight, } from "@/features/search/constants.ts"; import { NotificationPopover } from "@/features/notification/components/notification-popover.tsx"; +import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; -const links = [{ link: APP_ROUTE.HOME, label: "Home" }]; +const links = [ + { link: APP_ROUTE.HOME, label: "Home" }, +]; export function AppHeader() { const { t } = useTranslation(); @@ -34,9 +47,14 @@ export function AppHeader() { const [desktopOpened] = useAtom(desktopSidebarAtom); const toggleDesktop = useToggleSidebar(desktopSidebarAtom); const { isTrial, trialDaysLeft } = useTrial(); + const location = useLocation(); + const toggleAside = useToggleAside(); + const [workspace] = useAtom(workspaceAtom); + const aiChatEnabled = workspace?.settings?.ai?.chat === true; const isHomeRoute = location.pathname.startsWith("/home"); const isSpacesRoute = location.pathname === "/spaces"; + const isPageRoute = location.pathname.includes("/p/"); const hideSidebar = isHomeRoute || isSpacesRoute; const items = links.map((link) => ( @@ -73,15 +91,24 @@ export function AppHeader() { )} - - Docmost - + + + Docmost + + + Docmost + + {items} @@ -98,6 +125,49 @@ export function AppHeader() {
+ {aiChatEnabled && ( + <> + { + if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { + return; + } + if (isPageRoute) { + e.preventDefault(); + toggleAside("chat"); + } + }} + > + {t("AI Chat")} + + + { + if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) { + return; + } + if (isPageRoute) { + e.preventDefault(); + toggleAside("chat"); + } + }} + > + + + + + )} {isCloud() && isTrial && trialDaysLeft !== 0 && ( ; title = "Table of contents"; break; + case "chat": + component = ; + title = "AI Chat"; + break; default: component = null; title = null; @@ -34,12 +39,14 @@ export default function Aside() { {component && ( <> - - {t(title)} - + {tab !== "chat" && ( + + {t(title)} + + )} - {tab === "comments" ? ( - + {tab === "comments" || tab === "chat" ? ( + component ) : ( -
+ {!isAiRoute &&
} {isSpaceRoute && } {isSettingsRoute && } + {isAiRoute && } )} diff --git a/apps/client/src/components/ui/card-carousel.module.css b/apps/client/src/components/ui/card-carousel.module.css new file mode 100644 index 00000000..4ec513f2 --- /dev/null +++ b/apps/client/src/components/ui/card-carousel.module.css @@ -0,0 +1,68 @@ +.root { + position: relative; +} + +.track { + display: flex; + gap: var(--mantine-spacing-md); + overflow-x: auto; + scroll-snap-type: x mandatory; + scroll-behavior: smooth; + scrollbar-width: none; + -ms-overflow-style: none; + padding: 2px; + margin: -2px; +} + +.track::-webkit-scrollbar { + display: none; +} + +.track > * { + scroll-snap-align: start; + flex: 0 0 auto; +} + +.arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + cursor: pointer; + padding: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + opacity: 0; + pointer-events: none; + transition: opacity 120ms ease, background-color 120ms ease, transform 120ms ease; + z-index: 2; +} + +.root:hover .arrow.visible, +.arrow.visible:focus-visible { + opacity: 1; + pointer-events: auto; +} + +.arrow:hover { + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5)); +} + +.arrow:active { + transform: translateY(-50%) scale(0.95); +} + +.arrowLeft { + left: -14px; +} + +.arrowRight { + right: -14px; +} diff --git a/apps/client/src/components/ui/card-carousel.tsx b/apps/client/src/components/ui/card-carousel.tsx new file mode 100644 index 00000000..60d13aba --- /dev/null +++ b/apps/client/src/components/ui/card-carousel.tsx @@ -0,0 +1,77 @@ +import { useCallback, useEffect, useRef, useState, type ReactNode } from "react"; +import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import classes from "./card-carousel.module.css"; + +type Props = { + children: ReactNode; + ariaLabel?: string; +}; + +export default function CardCarousel({ children, ariaLabel }: Props) { + const { t } = useTranslation(); + const trackRef = useRef(null); + const [canScrollLeft, setCanScrollLeft] = useState(false); + const [canScrollRight, setCanScrollRight] = useState(false); + + const updateScrollState = useCallback(() => { + const el = trackRef.current; + if (!el) return; + const maxScroll = el.scrollWidth - el.clientWidth; + setCanScrollLeft(el.scrollLeft > 1); + setCanScrollRight(el.scrollLeft < maxScroll - 1); + }, []); + + useEffect(() => { + updateScrollState(); + const el = trackRef.current; + if (!el) return; + + const observer = new ResizeObserver(updateScrollState); + observer.observe(el); + for (const child of Array.from(el.children)) { + observer.observe(child); + } + + return () => observer.disconnect(); + }, [updateScrollState, children]); + + const scrollBy = (direction: 1 | -1) => { + const el = trackRef.current; + if (!el) return; + el.scrollBy({ left: direction * el.clientWidth * 0.85, behavior: "smooth" }); + }; + + return ( +
+
+ {children} +
+ + + + +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/ai-chat-layout.tsx b/apps/client/src/ee/ai-chat/components/ai-chat-layout.tsx new file mode 100644 index 00000000..f0fe3035 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/ai-chat-layout.tsx @@ -0,0 +1,106 @@ +import { useEffect, useRef } from "react"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; +import { useChatInfoQuery } from "../queries/ai-chat-query"; +import { useChatStream } from "../hooks/use-chat-stream"; +import ChatMessageList from "./chat-message-list"; +import ChatEmptyState from "./chat-empty-state"; +import ChatInput from "./chat-input"; +import type { HomeAiPromptInitialState } from "@/features/home/components/home-ai-prompt"; +import classes from "../styles/ai-chat.module.css"; + +export default function AiChatLayout() { + const { chatId } = useParams<{ chatId: string }>(); + const location = useLocation(); + const navigate = useNavigate(); + const chatInfoQuery = useChatInfoQuery(chatId); + + // If the URL points at a chat the user does not own, the info fetch 404s. + // Bounce them back to /ai so they cannot interact with any chat UI (including + // kicking off orphan uploads) tied to a chat they have no access to. + useEffect(() => { + if (chatId && chatInfoQuery.isError) { + navigate("/ai", { replace: true }); + } + }, [chatId, chatInfoQuery.isError, navigate]); + const { + messages, + streamingContent, + streamingToolCalls, + isStreaming, + error, + sendMessage, + stopGeneration, + hydrateFromServer, + } = useChatStream(chatId); + + const autoSentRef = useRef(false); + + useEffect(() => { + if (chatInfoQuery.data?.messages) { + hydrateFromServer(chatInfoQuery.data.messages); + } + }, [chatInfoQuery.data, hydrateFromServer]); + + useEffect(() => { + if (autoSentRef.current || chatId) return; + const state = location.state as HomeAiPromptInitialState | null; + if (!state?.initialContent && !state?.initialAttachments?.length) return; + + autoSentRef.current = true; + sendMessage( + state.initialContent ?? "", + state.initialMentions ?? [], + state.initialAttachments ?? [], + ); + navigate(location.pathname, { replace: true, state: null }); + }, [chatId, location, navigate, sendMessage]); + + const hasMessages = messages.length > 0 || isStreaming; + + // While the redirect effect is running (or if the user is still on this + // component for any reason) never render the chat UI for a forbidden chat. + if (chatId && chatInfoQuery.isError) { + return null; + } + + return ( +
+ {error && ( +
+ {error} +
+ )} + + {hasMessages ? ( + <> + +
+ +
+ + ) : ( + + )} +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx b/apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx new file mode 100644 index 00000000..f59b740e --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/ai-chat-sidebar-item.tsx @@ -0,0 +1,166 @@ +import { useState, useRef, useEffect, useMemo, useCallback } from "react"; +import { ActionIcon, Menu, TextInput } from "@mantine/core"; +import { IconDots, IconTrash, IconEdit } from "@tabler/icons-react"; +import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import type { AiChat } from "../types/ai-chat.types"; +import classes from "../styles/chat-sidebar.module.css"; + +type Props = { + chat: AiChat; + isActive: boolean; + onDelete: (chatId: string) => void; + onRename: (chatId: string, title: string) => void; +}; + +function formatChatDate( + isoString: string | Date, + locale: string | undefined, +): string { + const date = new Date(isoString); + if (Number.isNaN(date.getTime())) return ""; + + const now = new Date(); + const startOfToday = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + ).getTime(); + const ts = date.getTime(); + const sameYear = date.getFullYear() === now.getFullYear(); + + if (ts >= startOfToday) { + return date.toLocaleTimeString(locale, { + hour: "numeric", + minute: "2-digit", + }); + } + + if (sameYear) { + return date.toLocaleDateString(locale, { + month: "short", + day: "numeric", + }); + } + + return date.toLocaleDateString(locale, { + month: "short", + day: "numeric", + year: "numeric", + }); +} + +export default function AiChatSidebarItem({ + chat, + isActive, + onDelete, + onRename, +}: Props) { + const { t, i18n } = useTranslation(); + const [renaming, setRenaming] = useState(false); + const [renameValue, setRenameValue] = useState(""); + const inputRef = useRef(null); + + const formattedDate = useMemo( + () => formatChatDate(chat.updatedAt, i18n.language), + [chat.updatedAt, i18n.language], + ); + + useEffect(() => { + if (renaming) { + // Wait for the input to be mounted before selecting. + const id = window.setTimeout(() => inputRef.current?.select(), 0); + return () => window.clearTimeout(id); + } + }, [renaming]); + + const startRename = useCallback(() => { + setRenameValue(chat.title || ""); + setRenaming(true); + }, [chat.title]); + + const submitRename = useCallback(() => { + const trimmed = renameValue.trim(); + if (trimmed && trimmed !== chat.title) { + onRename(chat.id, trimmed); + } + setRenaming(false); + }, [renameValue, chat.id, chat.title, onRename]); + + if (renaming) { + return ( +
+ setRenameValue(e.currentTarget.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + submitRename(); + } else if (e.key === "Escape") { + e.preventDefault(); + setRenaming(false); + } + }} + onBlur={submitRename} + classNames={{ input: classes.chatItemRenameInput }} + style={{ flex: 1 }} + /> +
+ ); + } + + return ( + + + {chat.title || t("Untitled chat")} + + {formattedDate} +
+ + + e.preventDefault()} + > + + + + + } + onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + startRename(); + }} + > + {t("Rename")} + + } + color="red" + onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + onDelete(chat.id); + }} + > + {t("Delete")} + + + +
+ + ); +} diff --git a/apps/client/src/ee/ai-chat/components/ai-chat-sidebar.tsx b/apps/client/src/ee/ai-chat/components/ai-chat-sidebar.tsx new file mode 100644 index 00000000..e3df3e52 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/ai-chat-sidebar.tsx @@ -0,0 +1,224 @@ +import { useState, useCallback, useEffect, useMemo, useRef } from "react"; +import { Link, useNavigate, useParams } from "react-router-dom"; +import { ActionIcon, Center, TextInput, Loader, Tooltip } from "@mantine/core"; +import { useDebouncedValue } from "@mantine/hooks"; +import { IconPlus, IconSearch, IconMessageCircle2 } from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import { + useChatsQuery, + useDeleteChatMutation, + useUpdateChatTitleMutation, + useSearchChatsQuery, +} from "../queries/ai-chat-query"; +import AiChatSidebarItem from "./ai-chat-sidebar-item"; +import type { AiChat } from "../types/ai-chat.types"; +import classes from "../styles/chat-sidebar.module.css"; + +type ChatGroup = { key: string; label: string; chats: AiChat[] }; + +function groupChatsByAge( + chats: AiChat[], + t: (key: string) => string, +): ChatGroup[] { + if (chats.length === 0) return []; + + const now = new Date(); + const startOfToday = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + ).getTime(); + const startOfYesterday = startOfToday - 24 * 60 * 60 * 1000; + const startOfLast7 = startOfToday - 7 * 24 * 60 * 60 * 1000; + const startOfLast30 = startOfToday - 30 * 24 * 60 * 60 * 1000; + + const buckets: Record = { + today: { key: "today", label: t("Today"), chats: [] }, + yesterday: { key: "yesterday", label: t("Yesterday"), chats: [] }, + last7: { key: "last7", label: t("Previous 7 days"), chats: [] }, + last30: { key: "last30", label: t("Previous 30 days"), chats: [] }, + older: { key: "older", label: t("Older"), chats: [] }, + }; + + for (const chat of chats) { + const ts = new Date(chat.updatedAt).getTime(); + if (ts >= startOfToday) buckets.today.chats.push(chat); + else if (ts >= startOfYesterday) buckets.yesterday.chats.push(chat); + else if (ts >= startOfLast7) buckets.last7.chats.push(chat); + else if (ts >= startOfLast30) buckets.last30.chats.push(chat); + else buckets.older.chats.push(chat); + } + + return [ + buckets.today, + buckets.yesterday, + buckets.last7, + buckets.last30, + buckets.older, + ].filter((b) => b.chats.length > 0); +} + +export default function AiChatSidebar() { + const { t } = useTranslation(); + const navigate = useNavigate(); + const { chatId } = useParams<{ chatId: string }>(); + const [search, setSearch] = useState(""); + const [debouncedSearch] = useDebouncedValue(search, 300); + const chatsQuery = useChatsQuery(); + const searchQuery = useSearchChatsQuery(debouncedSearch); + const deleteMutation = useDeleteChatMutation(); + const renameMutation = useUpdateChatTitleMutation(); + + const chats = useMemo(() => { + if (debouncedSearch) { + return searchQuery.data || []; + } + return chatsQuery.data?.pages.flatMap((p) => p.items) || []; + }, [debouncedSearch, searchQuery.data, chatsQuery.data]); + + const groupedChats = useMemo(() => groupChatsByAge(chats, t), [chats, t]); + + const sentinelRef = useRef(null); + const { hasNextPage, fetchNextPage, isFetchingNextPage } = chatsQuery; + const isSearching = Boolean(debouncedSearch); + + useEffect(() => { + if (isSearching) return; + const sentinel = sentinelRef.current; + if (!sentinel) return; + + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, + { threshold: 0.1 }, + ); + + observer.observe(sentinel); + return () => observer.disconnect(); + }, [isSearching, hasNextPage, isFetchingNextPage, fetchNextPage]); + + const handleNewChat = useCallback( + (event: React.MouseEvent) => { + if ( + event.button !== 0 || + event.ctrlKey || + event.metaKey || + event.shiftKey + ) { + return; + } + event.preventDefault(); + navigate("/ai"); + }, + [navigate], + ); + + const handleDelete = useCallback( + (id: string) => { + deleteMutation.mutate(id, { + onSuccess: () => { + if (chatId === id) { + navigate("/ai"); + } + }, + }); + }, + [deleteMutation, chatId, navigate], + ); + + const handleRename = useCallback( + (chatId: string, title: string) => { + renameMutation.mutate({ chatId, title }); + }, + [renameMutation], + ); + + const isLoading = chatsQuery.isLoading || searchQuery.isLoading; + + return ( +
+
+ {t("AI Chat")} + + + + + +
+ + } + size="xs" + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + +
+ {isLoading && } + {!isLoading && chats.length === 0 && ( +
+ +
+ {isSearching ? t("No chats found") : t("No conversations yet")} +
+
+ {isSearching + ? t("Try a different search term.") + : t("Start a new chat to see it here.")} +
+
+ )} + {isSearching + ? chats.map((chat) => ( + + )) + : groupedChats.map((group) => ( +
+
{group.label}
+ {group.chats.map((chat) => ( + + ))} +
+ ))} + {!isSearching && ( + <> +
+ {isFetchingNextPage && ( +
+ +
+ )} + + )} +
+
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/aside-chat-history.tsx b/apps/client/src/ee/ai-chat/components/aside-chat-history.tsx new file mode 100644 index 00000000..c31e5b10 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/aside-chat-history.tsx @@ -0,0 +1,67 @@ +import { useState } from "react"; +import { TextInput, Loader, Text, ScrollArea } from "@mantine/core"; +import { IconSearch } from "@tabler/icons-react"; +import { useChatsQuery, useSearchChatsQuery } from "../queries/ai-chat-query"; +import { useDebouncedValue } from "@mantine/hooks"; +import { useTranslation } from "react-i18next"; +import classes from "../styles/aside-chat-panel.module.css"; + +type Props = { + activeChatId: string | undefined; + onSelect: (chatId: string) => void; +}; + +export default function AsideChatHistory({ activeChatId, onSelect }: Props) { + const { t } = useTranslation(); + const [searchValue, setSearchValue] = useState(""); + const [debouncedSearch] = useDebouncedValue(searchValue, 300); + + const chatsQuery = useChatsQuery(); + const searchQuery = useSearchChatsQuery(debouncedSearch); + + const isSearching = debouncedSearch.length > 0; + const chats = isSearching + ? (searchQuery.data ?? []) + : (chatsQuery.data?.pages.flatMap((p) => p.items) ?? []); + const isLoading = isSearching ? searchQuery.isLoading : chatsQuery.isLoading; + + return ( +
+ } + size="xs" + mb="xs" + value={searchValue} + onChange={(e) => setSearchValue(e.currentTarget.value)} + /> + + {isLoading ? ( +
+ +
+ ) : chats.length === 0 ? ( + + {isSearching ? t("No chats found") : t("No chat history")} + + ) : ( + +
+ {chats.map((chat) => ( +
onSelect(chat.id)} + > + + {chat.title || t("Untitled chat")} + +
+ ))} +
+
+ )} +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/aside-chat-panel.tsx b/apps/client/src/ee/ai-chat/components/aside-chat-panel.tsx new file mode 100644 index 00000000..bd2b9569 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/aside-chat-panel.tsx @@ -0,0 +1,258 @@ +import { useState, useEffect, useCallback } from "react"; +import { ActionIcon, Popover, Tooltip, UnstyledButton } from "@mantine/core"; +import { + IconPlus, + IconChevronDown, + IconArrowsDiagonal, + IconX, + IconSparkles, + IconFileText, + IconLanguage, + IconSearch, +} from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import { useNavigate, useParams } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { asideStateAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom"; +import { usePageQuery } from "@/features/page/queries/page-query"; +import { extractPageSlugId } from "@/lib"; +import { useChatStream } from "../hooks/use-chat-stream"; +import { useChatInfoQuery } from "../queries/ai-chat-query"; +import ChatMessageList from "./chat-message-list"; +import ChatInput from "./chat-input"; +import AsideChatHistory from "./aside-chat-history"; +import type { ChatAttachment, PageMention } from "../types/ai-chat.types"; +import classes from "../styles/aside-chat-panel.module.css"; + +type QuickAction = { + icon: React.ReactNode; + label: string; + prompt: string; +}; + +export default function AsideChatPanel() { + const { t } = useTranslation(); + const navigate = useNavigate(); + const [, setAsideState] = useAtom(asideStateAtom); + const [chatId, setChatId] = useState(undefined); + const [historyOpen, setHistoryOpen] = useState(false); + const [contextPages, setContextPages] = useState([]); + const { pageSlug } = useParams(); + const slugId = extractPageSlugId(pageSlug); + const { data: page } = usePageQuery({ pageId: slugId }); + + const chatInfoQuery = useChatInfoQuery(chatId); + const { + messages, + streamingContent, + streamingToolCalls, + isStreaming, + error, + sendMessage, + stopGeneration, + hydrateFromServer, + } = useChatStream(chatId, { + onChatCreated: (newChatId) => { + setChatId(newChatId); + }, + }); + + useEffect(() => { + if (page && !chatId) { + setContextPages([{ id: page.id, title: page.title || "", slugId: page.slugId }]); + } + }, [page, chatId]); + + const handleRemoveContextPage = useCallback((pageId: string) => { + setContextPages((prev) => prev.filter((p) => p.id !== pageId)); + }, []); + + useEffect(() => { + if (chatInfoQuery.data?.messages) { + hydrateFromServer(chatInfoQuery.data.messages); + } + }, [chatInfoQuery.data, hydrateFromServer]); + + // Drop the open chatId if the current user lost access to it (404/403 on + // the info fetch). Reverts the panel to a fresh chat instead of presenting + // an input tied to a chat the user does not own. + useEffect(() => { + if (chatId && chatInfoQuery.isError) { + setChatId(undefined); + } + }, [chatId, chatInfoQuery.isError]); + + const handleNewChat = useCallback( + (event: React.MouseEvent) => { + if ( + event.button !== 0 || + event.ctrlKey || + event.metaKey || + event.shiftKey + ) { + return; + } + event.preventDefault(); + setChatId(undefined); + if (page) { + setContextPages([ + { id: page.id, title: page.title || "", slugId: page.slugId }, + ]); + } + }, + [page], + ); + + const handleSelectChat = useCallback((selectedChatId: string) => { + setChatId(selectedChatId); + setHistoryOpen(false); + }, []); + + const handleExpand = useCallback(() => { + if (chatId) { + navigate(`/ai/chat/${chatId}`); + } else { + navigate("/ai"); + } + setAsideState({ tab: "", isAsideOpen: false }); + }, [chatId, navigate, setAsideState]); + + const handleClose = useCallback(() => { + setAsideState({ tab: "", isAsideOpen: false }); + }, [setAsideState]); + + const handleSend = useCallback( + (content: string, mentions: PageMention[], attachments: ChatAttachment[]) => { + const contextPageId = contextPages.length > 0 ? contextPages[0].id : undefined; + sendMessage(content, mentions, attachments, contextPageId); + }, + [sendMessage, contextPages], + ); + + const handleQuickAction = useCallback( + (prompt: string) => { + handleSend(prompt, [], []); + }, + [handleSend], + ); + + const hasMessages = messages.length > 0 || isStreaming; + + const quickActions: QuickAction[] = [ + { icon: , label: t("Summarize this page"), prompt: "Summarize this page" }, + { icon: , label: t("Translate this page"), prompt: "Translate this page" }, + { icon: , label: t("Analyze for insights"), prompt: "Analyze this page for insights" }, + ]; + + return ( +
+
+ + + setHistoryOpen((o) => !o)} + > + + {chatInfoQuery.data?.chat?.title || t("New chat")} + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + {error && ( +
+ {error} +
+ )} + + {hasMessages ? ( + <> +
+ +
+ + ) : ( +
+ +
{t("How can I help you today?")}
+
+ {quickActions.map((action) => ( + + ))} +
+
+ )} + +
+ +
+
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/chat-empty-state.tsx b/apps/client/src/ee/ai-chat/components/chat-empty-state.tsx new file mode 100644 index 00000000..d7bacf18 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/chat-empty-state.tsx @@ -0,0 +1,91 @@ +import { + IconSparkles, + IconSearch, + IconFilePlus, + IconEdit, + IconFileText, +} from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import ChatInput from "./chat-input"; +import type { ChatAttachment, PageMention } from "../types/ai-chat.types"; +import classes from "../styles/ai-chat.module.css"; + +type Suggestion = { + icon: React.ReactNode; + text: string; + prompt: string; +}; + +const SUGGESTIONS: Suggestion[] = [ + { + icon: , + text: "Search across all pages", + prompt: "Search for pages about ", + }, + { + icon: , + text: "Create a new page", + prompt: "Create a new page titled ", + }, + { + icon: , + text: "Summarize a page", + prompt: "Summarize the page @", + }, + { + icon: , + text: "Update page content", + prompt: "Update the page @", + }, +]; + +type Props = { + isStreaming: boolean; + onSend: (content: string, mentions: PageMention[], attachments: ChatAttachment[]) => void; + onStop: () => void; +}; + +export default function ChatEmptyState({ isStreaming, onSend, onStop }: Props) { + const { t } = useTranslation(); + + const handleSuggestionClick = (prompt: string) => { + onSend(prompt, [], []); + }; + + return ( +
+ +
{t("Docmost AI")}
+
+ {t("What can I help you with?")} +
+ +
+ +
+ +
+
Get started
+
+ {SUGGESTIONS.map((s) => ( + + ))} +
+
+
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/chat-input.tsx b/apps/client/src/ee/ai-chat/components/chat-input.tsx new file mode 100644 index 00000000..d003adfb --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/chat-input.tsx @@ -0,0 +1,409 @@ +import { useCallback, useRef, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { IconArrowUp, IconPaperclip, IconPlayerStopFilled, IconX, IconFile, IconPhoto, IconPlus, IconAt, IconFileText } from "@tabler/icons-react"; +import { Popover } from "@mantine/core"; +import { notifications } from "@mantine/notifications"; +import { EditorContent, ReactNodeViewRenderer, useEditor } from "@tiptap/react"; +import { Placeholder } from "@tiptap/extension-placeholder"; +import { CharacterCount } from "@tiptap/extensions"; +import { StarterKit } from "@tiptap/starter-kit"; +import { Mention, LinkExtension } from "@docmost/editor-ext"; +import EmojiCommand from "@/features/editor/extensions/emoji-command"; +import mentionRenderItems from "@/features/editor/components/mention/mention-suggestion"; +import MentionView from "@/features/editor/components/mention/mention-view"; +import { uploadChatFile } from "../services/ai-chat-service"; +import type { ChatAttachment, PageMention } from "../types/ai-chat.types"; +import classes from "../styles/chat-input.module.css"; + +type PendingAttachment = ChatAttachment & { uploading: boolean }; + +const IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"]; +const ACCEPTED_FILE_TYPES = ".pdf,.docx,.txt,.csv,.md,.png,.jpg,.jpeg,.webp"; +// Kept in sync with MAX_ATTACHMENTS_PER_MESSAGE in apps/server/src/ee/ai-chat/ai-chat-limits.ts +const MAX_ATTACHMENTS_PER_MESSAGE = 5; + +type Props = { + isStreaming: boolean; + onSend: (content: string, mentions: PageMention[], attachments: ChatAttachment[]) => void; + onStop: () => void; + placeholder?: string; + autofocus?: boolean; + contextPages?: PageMention[]; + onRemoveContextPage?: (pageId: string) => void; + variant?: "card" | "flat"; + showDisclaimer?: boolean; + chatId?: string; +}; + +function extractMentions(json: any): PageMention[] { + const mentions: PageMention[] = []; + const seen = new Set(); + + function walk(node: any) { + if (node.type === "mention" && node.attrs?.entityType === "page" && node.attrs?.entityId) { + if (!seen.has(node.attrs.entityId)) { + seen.add(node.attrs.entityId); + mentions.push({ + id: node.attrs.entityId, + title: node.attrs.label || "", + slugId: node.attrs.slugId || "", + }); + } + } + if (node.content) { + for (const child of node.content) { + walk(child); + } + } + } + + walk(json); + return mentions; +} + +function editorJsonToText(json: any): string { + let text = ""; + + function walk(node: any) { + if (node.type === "text") { + text += node.text || ""; + } else if (node.type === "mention") { + text += `@${node.attrs?.label || ""}`; + } else if (node.type === "paragraph") { + if (text.length > 0) text += "\n"; + if (node.content) { + for (const child of node.content) { + walk(child); + } + } + return; + } + if (node.content) { + for (const child of node.content) { + walk(child); + } + } + } + + walk(json); + return text; +} + +export default function ChatInput({ + isStreaming, + onSend, + onStop, + placeholder, + autofocus = true, + contextPages, + onRemoveContextPage, + variant = "card", + showDisclaimer = true, + chatId, +}: Props) { + const chatIdRef = useRef(chatId); + chatIdRef.current = chatId; + const { t } = useTranslation(); + const [isEmpty, setIsEmpty] = useState(true); + const [pendingAttachments, setPendingAttachments] = useState([]); + const [plusMenuOpen, setPlusMenuOpen] = useState(false); + const fileInputRef = useRef(null); + const onSendRef = useRef(onSend); + onSendRef.current = onSend; + + const handleFileSelect = useCallback(async (files: FileList | null) => { + if (!files?.length) return; + + const room = MAX_ATTACHMENTS_PER_MESSAGE - pendingAttachments.length; + if (room <= 0) { + notifications.show({ + color: "yellow", + message: t("You can attach up to {{max}} files per message.", { + max: MAX_ATTACHMENTS_PER_MESSAGE, + }), + }); + if (fileInputRef.current) fileInputRef.current.value = ""; + return; + } + + const incoming = Array.from(files); + const accepted = incoming.slice(0, room); + + if (incoming.length > accepted.length) { + notifications.show({ + color: "yellow", + message: t( + "Only the first {{n}} file(s) were added (max {{max}} per message).", + { n: accepted.length, max: MAX_ATTACHMENTS_PER_MESSAGE }, + ), + }); + } + + for (const file of accepted) { + const tempId = `uploading-${Date.now()}-${Math.random()}`; + const ext = file.name.split(".").pop()?.toLowerCase() || ""; + + const placeholder: PendingAttachment = { + id: tempId, + fileName: file.name, + fileExt: ext, + fileSize: file.size, + mimeType: file.type, + uploading: true, + }; + + setPendingAttachments((prev) => [...prev, placeholder]); + + try { + const uploaded = await uploadChatFile(file, chatIdRef.current); + setPendingAttachments((prev) => + prev.map((a) => + a.id === tempId ? { ...uploaded, uploading: false } : a, + ), + ); + } catch { + setPendingAttachments((prev) => prev.filter((a) => a.id !== tempId)); + } + } + + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + }, [pendingAttachments.length, t]); + + const removeAttachment = useCallback((id: string) => { + setPendingAttachments((prev) => prev.filter((a) => a.id !== id)); + }, []); + + const handleSubmit = useCallback(() => { + if (!editor || isStreaming) return; + const json = editor.getJSON(); + const text = editorJsonToText(json).trim(); + const readyAttachments = pendingAttachments.filter((a) => !a.uploading); + if (!text && readyAttachments.length === 0) return; + + const mentions = extractMentions(json); + onSendRef.current(text, mentions, readyAttachments); + editor.commands.clearContent(); + editor.commands.focus(); + setPendingAttachments([]); + }, [isStreaming, pendingAttachments]); + + const handleSubmitRef = useRef(handleSubmit); + handleSubmitRef.current = handleSubmit; + + const editor = useEditor({ + extensions: [ + StarterKit.configure({ + gapcursor: false, + dropcursor: false, + link: false, + }), + Placeholder.configure({ + placeholder: placeholder || "Ask anything... Use @ to mention pages", + }), + CharacterCount.configure({ + limit: 50000, + }), + LinkExtension, + EmojiCommand, + Mention.configure({ + suggestion: { + allowSpaces: true, + items: () => [], + // @ts-ignore + render: mentionRenderItems, + }, + HTMLAttributes: { + class: "mention", + }, + }).extend({ + addNodeView() { + this.editor.isInitialized = true; + return ReactNodeViewRenderer(MentionView); + }, + }), + ], + editorProps: { + handleDOMEvents: { + keydown: (_view, event) => { + if ( + ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Enter"].includes( + event.key, + ) + ) { + const emojiCommand = document.querySelector("#emoji-command"); + const mentionPopup = document.querySelector("#mention"); + if (emojiCommand || mentionPopup) { + return true; + } + } + + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + handleSubmitRef.current(); + return true; + } + }, + }, + }, + content: "", + editable: true, + immediatelyRender: true, + shouldRerenderOnTransaction: false, + autofocus: autofocus ? "end" : false, + onUpdate: ({ editor: e }) => { + setIsEmpty(!e.getText().trim()); + }, + }); + + useEffect(() => { + if (editor && autofocus) { + editor.commands.focus(); + } + }, [editor]); + + const hasContent = !isEmpty || pendingAttachments.some((a) => !a.uploading) || (contextPages?.length ?? 0) > 0; + + const wrapperClass = variant === "flat" ? classes.inputWrapperFlat : classes.inputWrapper; + + return ( + <> +
+ handleFileSelect(e.target.files)} + /> + + {((contextPages?.length ?? 0) > 0 || pendingAttachments.length > 0) && ( +
+ {contextPages?.map((page) => ( +
+ + + {page.title || "Untitled"} + + {onRemoveContextPage && ( + + )} +
+ ))} + {pendingAttachments.map((attachment) => ( +
+ {IMAGE_EXTENSIONS.includes(attachment.fileExt) ? ( + + ) : ( + + )} + + {attachment.fileName} + + {!attachment.uploading && ( + + )} +
+ ))} +
+ )} + + +
+ + + + + + + + + + +
+ + {isStreaming ? ( + + ) : ( + + )} +
+
+ {showDisclaimer && ( +
+ {t("AI-generated content may not be accurate.")} +
+ )} + + ); +} diff --git a/apps/client/src/ee/ai-chat/components/chat-message-list.tsx b/apps/client/src/ee/ai-chat/components/chat-message-list.tsx new file mode 100644 index 00000000..3a6fffef --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/chat-message-list.tsx @@ -0,0 +1,174 @@ +import { useEffect, useRef, useCallback, useState } from "react"; +import { ErrorBoundary } from "react-error-boundary"; +import { IconArrowDown, IconAlertTriangle } from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import type { AiChatMessage, AiChatToolCall } from "../types/ai-chat.types"; +import ChatMessage from "./chat-message"; +import classes from "../styles/ai-chat.module.css"; + +function ChatMessageErrorFallback() { + const { t } = useTranslation(); + return ( +
+ + {t("Failed to render this message.")} +
+ ); +} + +type Props = { + messages: AiChatMessage[]; + isStreaming: boolean; + streamingContent: string; + streamingToolCalls: AiChatToolCall[]; +}; + +const BOTTOM_THRESHOLD_PX = 32; +const SCROLL_UP_THRESHOLD_PX = 5; +const SMOOTH_SCROLL_SETTLE_MS = 600; + +export default function ChatMessageList({ + messages, + isStreaming, + streamingContent, + streamingToolCalls, +}: Props) { + const containerRef = useRef(null); + const bottomRef = useRef(null); + const isAtBottomRef = useRef(true); + const isAutoScrollingRef = useRef(false); + const prevScrollTopRef = useRef(0); + const [showScrollButton, setShowScrollButton] = useState(false); + + const scrollToBottom = useCallback((behavior: ScrollBehavior = "smooth") => { + const container = containerRef.current; + if (!container) return; + + isAutoScrollingRef.current = true; + const target = container.scrollHeight - container.clientHeight; + container.scrollTo({ top: target, behavior }); + prevScrollTopRef.current = target; + isAtBottomRef.current = true; + setShowScrollButton(false); + + if (behavior === "smooth") { + setTimeout(() => { + isAutoScrollingRef.current = false; + if (containerRef.current) { + prevScrollTopRef.current = containerRef.current.scrollTop; + } + }, SMOOTH_SCROLL_SETTLE_MS); + } else { + isAutoScrollingRef.current = false; + } + }, []); + + const handleScroll = useCallback(() => { + if (isAutoScrollingRef.current) return; + + const container = containerRef.current; + if (!container) return; + + const currentScrollTop = container.scrollTop; + const scrolledUp = + currentScrollTop < prevScrollTopRef.current - SCROLL_UP_THRESHOLD_PX; + prevScrollTopRef.current = currentScrollTop; + + const distanceFromBottom = + container.scrollHeight - currentScrollTop - container.clientHeight; + const atBottom = distanceFromBottom <= BOTTOM_THRESHOLD_PX; + + if (scrolledUp) { + isAtBottomRef.current = atBottom; + } else if (atBottom) { + isAtBottomRef.current = true; + } + + setShowScrollButton(!atBottom); + }, []); + + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + container.addEventListener("scroll", handleScroll, { passive: true }); + return () => container.removeEventListener("scroll", handleScroll); + }, [handleScroll]); + + // Instant scroll during streaming to keep up with rapid updates + useEffect(() => { + if (isAtBottomRef.current) { + scrollToBottom("instant"); + } + }, [streamingContent, streamingToolCalls.length, scrollToBottom]); + + // Smooth scroll for new messages. Always force-scroll when the latest + // message is from the user (they just sent it), even if they were reading + // scrollback. + useEffect(() => { + const lastMessage = messages[messages.length - 1]; + const lastIsUser = lastMessage?.role === "user"; + if (lastIsUser || isAtBottomRef.current) { + scrollToBottom("smooth"); + return; + } + + // No auto-scroll: recompute from actual layout so that chat switches to + // content that doesn't overflow correctly hide the button even when no + // scroll event fires. + const container = containerRef.current; + if (!container) return; + const distanceFromBottom = + container.scrollHeight - container.scrollTop - container.clientHeight; + const atBottom = distanceFromBottom <= BOTTOM_THRESHOLD_PX; + isAtBottomRef.current = atBottom; + setShowScrollButton(!atBottom); + }, [messages, scrollToBottom]); + + return ( +
+
+ {messages.map((msg) => ( + } + > + + + ))} + {isStreaming && ( + } + > + + + )} +
+
+ {showScrollButton && ( + + )} +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/chat-message.tsx b/apps/client/src/ee/ai-chat/components/chat-message.tsx new file mode 100644 index 00000000..d0e9443a --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/chat-message.tsx @@ -0,0 +1,139 @@ +import { useCallback } from "react"; +import { useNavigate } from "react-router"; +import DOMPurify from "dompurify"; +import { ActionIcon, Tooltip } from "@mantine/core"; +import { + IconCheck, + IconCopy, + IconFile, + IconLoader2, + IconPhoto, +} from "@tabler/icons-react"; +import { markdownToHtml } from "@docmost/editor-ext"; +import { CopyButton } from "@/components/common/copy-button"; +import type { AiChatMessage, AiChatToolCall } from "../types/ai-chat.types"; +import ChatToolGroup from "./chat-tool-group"; +import classes from "../styles/chat-message.module.css"; +import CopyTextButton from "@/components/common/copy.tsx"; + +const chatSanitizer = DOMPurify(); +chatSanitizer.addHook("afterSanitizeAttributes", (node) => { + if (node.tagName === "A") { + const href = node.getAttribute("href") || ""; + if (href.startsWith("http://") || href.startsWith("https://")) { + node.setAttribute("target", "_blank"); + node.setAttribute("rel", "noopener noreferrer"); + } + } +}); + +const IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif"]; + +type Props = { + message: AiChatMessage; + isStreaming?: boolean; + streamingContent?: string; + streamingToolCalls?: AiChatToolCall[]; +}; + +export default function ChatMessage({ + message, + isStreaming, + streamingContent, + streamingToolCalls, +}: Props) { + const navigate = useNavigate(); + + const handleContentClick = useCallback( + (e: React.MouseEvent) => { + const target = e.target as HTMLElement; + const anchor = target.closest("a"); + if (!anchor) return; + + const href = anchor.getAttribute("href"); + if (href && (href.startsWith("/s/") || href.startsWith("/p/"))) { + e.preventDefault(); + navigate(href); + } + }, + [navigate], + ); + + if (message.role === "tool") return null; + + const isUser = message.role === "user"; + const content = isStreaming ? streamingContent : message.content; + const toolCalls = isStreaming ? streamingToolCalls : message.toolCalls; + + if (isUser) { + const displayContent = (content || "").replace( + /\n\n[\s\S]*<\/referenced_pages>$/, + "", + ); + const attachments = + (message.metadata?.attachments as { + id: string; + fileName: string; + fileExt: string; + }[]) || []; + + return ( +
+
+ {attachments.length > 0 && ( +
+ {attachments.map((a) => ( + + {IMAGE_EXTENSIONS.includes(a.fileExt) ? ( + + ) : ( + + )} + {a.fileName} + + ))} +
+ )} + {displayContent} +
+
+ ); + } + + return ( +
+
+ {toolCalls && toolCalls.length > 0 && ( + + )} + {content && ( +
+ )} + {isStreaming && ( + <> + {!content && ( + + + Thinking + + )} + + + )} +
+ {!isStreaming && message.content && ( +
+ +
+ )} +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/chat-tool-group.tsx b/apps/client/src/ee/ai-chat/components/chat-tool-group.tsx new file mode 100644 index 00000000..b4e002f6 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/chat-tool-group.tsx @@ -0,0 +1,56 @@ +import { useState } from "react"; +import { + IconChevronRight, + IconChevronDown, + IconLoader2, +} from "@tabler/icons-react"; +import type { AiChatToolCall } from "../types/ai-chat.types"; +import ChatToolResult, { TOOL_LABELS } from "./chat-tool-result"; +import classes from "../styles/chat-message.module.css"; + +type Props = { + toolCalls: AiChatToolCall[]; + isStreaming?: boolean; +}; + +export default function ChatToolGroup({ toolCalls, isStreaming }: Props) { + const [expanded, setExpanded] = useState(false); + + if (!toolCalls || toolCalls.length === 0) return null; + + const activeCall = + isStreaming && toolCalls.length > 0 + ? [...toolCalls].reverse().find((tc) => tc.result === undefined) + : undefined; + + const activeLabel = activeCall + ? TOOL_LABELS[activeCall.name] || activeCall.name + : null; + + return ( +
+
setExpanded((prev) => !prev)} + > + {activeLabel ? ( + + ) : expanded ? ( + + ) : ( + + )} + + {activeLabel ? `${activeLabel}…` : `Steps ${toolCalls.length}`} + +
+ {expanded && ( +
+ {toolCalls.map((tc) => ( + + ))} +
+ )} +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/chat-tool-result.tsx b/apps/client/src/ee/ai-chat/components/chat-tool-result.tsx new file mode 100644 index 00000000..55f20f91 --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/chat-tool-result.tsx @@ -0,0 +1,49 @@ +import { useState } from "react"; +import { IconChevronRight, IconChevronDown } from "@tabler/icons-react"; +import type { AiChatToolCall } from "../types/ai-chat.types"; +import classes from "../styles/chat-message.module.css"; + +export const TOOL_LABELS: Record = { + list_spaces: "Listed spaces", + search_pages: "Searched pages", + get_page: "Read page", + create_page: "Created page", + update_page: "Updated page", +}; + +type Props = { + toolCall: AiChatToolCall; +}; + +export default function ChatToolResult({ toolCall }: Props) { + const [expanded, setExpanded] = useState(false); + const label = TOOL_LABELS[toolCall.name] || toolCall.name; + + return ( +
+
setExpanded((prev) => !prev)} + > + · + {expanded ? ( + + ) : ( + + )} + {label} +
+ {expanded && ( +
+
+            {JSON.stringify(
+              { args: toolCall.args, result: toolCall.result },
+              null,
+              2,
+            )}
+          
+
+ )} +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/components/enable-ai-chat.tsx b/apps/client/src/ee/ai-chat/components/enable-ai-chat.tsx new file mode 100644 index 00000000..f4200e5b --- /dev/null +++ b/apps/client/src/ee/ai-chat/components/enable-ai-chat.tsx @@ -0,0 +1,67 @@ +import { Badge, Group, Text, Switch, Tooltip } from "@mantine/core"; +import { useAtom } from "jotai"; +import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { updateWorkspace } from "@/features/workspace/services/workspace-service.ts"; +import { notifications } from "@mantine/notifications"; +import { useHasFeature } from "@/ee/hooks/use-feature"; +import { Feature } from "@/ee/features"; +import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label"; + +export default function EnableAiChat() { + const { t } = useTranslation(); + + return ( + +
+ + {t("AI Chat")} + + {t("Beta")} + + + + {t( + "Enable AI Chat to allow users to have multi-turn conversations with AI about your workspace content.", + )} + +
+ + +
+ ); +} + +function AiChatToggle() { + const { t } = useTranslation(); + const [workspace, setWorkspace] = useAtom(workspaceAtom); + const [checked, setChecked] = useState(workspace?.settings?.ai?.chat); + const hasAccess = useHasFeature(Feature.AI); + const upgradeLabel = useUpgradeLabel(); + + const handleChange = async (event: React.ChangeEvent) => { + const value = event.currentTarget.checked; + try { + const updatedWorkspace = await updateWorkspace({ aiChat: value } as any); + setChecked(value); + setWorkspace(updatedWorkspace); + } catch (err: any) { + notifications.show({ + message: err?.response?.data?.message, + color: "red", + }); + } + }; + + return ( + + + + ); +} diff --git a/apps/client/src/ee/ai-chat/hooks/use-chat-stream.ts b/apps/client/src/ee/ai-chat/hooks/use-chat-stream.ts new file mode 100644 index 00000000..65a93a97 --- /dev/null +++ b/apps/client/src/ee/ai-chat/hooks/use-chat-stream.ts @@ -0,0 +1,227 @@ +import { useState, useCallback, useEffect, useRef } from "react"; +import { useQueryClient } from "@tanstack/react-query"; +import { useNavigate } from "react-router-dom"; +import { sendChatMessage } from "../services/ai-chat-service"; +import type { + AiChatMessage, + AiChatStreamEvent, + AiChatToolCall, + ChatAttachment, + PageMention, +} from "../types/ai-chat.types"; + +type ChatStreamOptions = { + onChatCreated?: (chatId: string) => void; +}; + +export function useChatStream( + chatId: string | undefined, + options?: ChatStreamOptions, +) { + const [messages, setMessages] = useState([]); + const [streamingContent, setStreamingContent] = useState(""); + const [streamingToolCalls, setStreamingToolCalls] = useState( + [], + ); + const [isStreaming, setIsStreaming] = useState(false); + const [error, setError] = useState(null); + const [errorCode, setErrorCode] = useState(null); + const [isRetryable, setIsRetryable] = useState(false); + const abortRef = useRef(null); + const queryClient = useQueryClient(); + const navigate = useNavigate(); + const currentChatIdRef = useRef(chatId); + currentChatIdRef.current = chatId; + // Tracks which chatId the local `messages` state currently represents. + // Set when we seed from a server fetch AND when we optimistically own a + // freshly-created chat after `chat_created`. This is the single authority + // marker that keeps server-state effects from clobbering in-flight streams. + const hydratedChatIdRef = useRef(undefined); + + // Reset local state when the consumer switches to a different chat. + // Skip the reset if the new chatId is one the hook itself already claimed + // during a new-chat flow — in that case our optimistic state is the truth. + useEffect(() => { + if (chatId && chatId === hydratedChatIdRef.current) return; + hydratedChatIdRef.current = undefined; + setMessages([]); + setError(null); + setErrorCode(null); + setIsRetryable(false); + }, [chatId]); + + const hydrateFromServer = useCallback((msgs: AiChatMessage[]) => { + const forId = currentChatIdRef.current; + if (!forId) return; + if (hydratedChatIdRef.current === forId) return; + hydratedChatIdRef.current = forId; + setMessages(msgs); + }, []); + + const sendMessage = useCallback( + (content: string, mentions: PageMention[] = [], attachments: ChatAttachment[] = [], contextPageId?: string) => { + if (isStreaming || (!content.trim() && attachments.length === 0)) return; + + setError(null); + setErrorCode(null); + setIsRetryable(false); + setIsStreaming(true); + setStreamingContent(""); + setStreamingToolCalls([]); + + const metadata: Record = {}; + if (mentions.length) { + metadata.mentionedPageIds = mentions.map((m) => m.id); + } + if (attachments.length) { + metadata.attachments = attachments.map((a) => ({ + id: a.id, + fileName: a.fileName, + fileExt: a.fileExt, + })); + } + + const userMessage: AiChatMessage = { + id: `temp-${Date.now()}`, + chatId: currentChatIdRef.current || "", + role: "user", + content, + toolCalls: null, + metadata: Object.keys(metadata).length ? metadata : null, + createdAt: new Date().toISOString(), + }; + + setMessages((prev) => [...prev, userMessage]); + + const attachmentIds = attachments.map((a) => a.id); + + const abortController = sendChatMessage( + { + chatId: currentChatIdRef.current, + content, + mentionedPageIds: mentions.map((m) => m.id), + ...(contextPageId && { contextPageId }), + ...(attachmentIds.length && { attachmentIds }), + }, + (event: AiChatStreamEvent) => { + switch (event.type) { + case "chat_created": + currentChatIdRef.current = event.chatId; + // Claim authority over this new chatId so when the consumer's + // prop catches up via navigation/onChatCreated, the reset effect + // sees a match and preserves our optimistic messages. + hydratedChatIdRef.current = event.chatId; + if (options?.onChatCreated) { + options.onChatCreated(event.chatId); + } else { + navigate(`/ai/chat/${event.chatId}`, { replace: true }); + } + queryClient.invalidateQueries({ queryKey: ["ai-chats"] }); + break; + case "content": + setStreamingContent((prev) => prev + event.text); + break; + case "tool_call": + setStreamingToolCalls((prev) => [ + ...prev, + { + id: event.id, + name: event.name, + args: event.args, + }, + ]); + break; + case "tool_result": + setStreamingToolCalls((prev) => + prev.map((tc) => + tc.id === event.id ? { ...tc, result: event.result } : tc, + ), + ); + break; + case "done": { + setStreamingContent((currentContent) => { + setStreamingToolCalls((currentToolCalls) => { + const assistantMessage: AiChatMessage = { + id: event.messageId, + chatId: currentChatIdRef.current || "", + role: "assistant", + content: currentContent || null, + toolCalls: currentToolCalls.length + ? currentToolCalls + : null, + metadata: event.usage ? { tokenUsage: event.usage } : null, + createdAt: new Date().toISOString(), + }; + + setMessages((prev) => [...prev, assistantMessage]); + return []; + }); + return ""; + }); + setIsStreaming(false); + queryClient.invalidateQueries({ + queryKey: ["ai-chat", currentChatIdRef.current], + }); + break; + } + case "error": + setError(event.message); + setErrorCode(event.code || null); + setIsRetryable(event.retryable || false); + setIsStreaming(false); + break; + } + }, + (errorMsg) => { + setError(errorMsg); + setIsStreaming(false); + }, + () => { + setIsStreaming(false); + }, + ); + + abortRef.current = abortController; + }, + [isStreaming, navigate, queryClient], + ); + + const stopGeneration = useCallback(() => { + abortRef.current?.abort(); + abortRef.current = null; + + setStreamingContent((currentContent) => { + setStreamingToolCalls((currentToolCalls) => { + if (currentContent || currentToolCalls.length > 0) { + const partialMessage: AiChatMessage = { + id: `stopped-${Date.now()}`, + chatId: currentChatIdRef.current || "", + role: "assistant", + content: currentContent || null, + toolCalls: currentToolCalls.length ? currentToolCalls : null, + metadata: null, + createdAt: new Date().toISOString(), + }; + setMessages((prev) => [...prev, partialMessage]); + } + return []; + }); + return ""; + }); + + setIsStreaming(false); + }, []); + + return { + messages, + streamingContent, + streamingToolCalls, + isStreaming, + error, + errorCode, + isRetryable, + sendMessage, + stopGeneration, + hydrateFromServer, + }; +} diff --git a/apps/client/src/ee/ai-chat/pages/ai-chat.tsx b/apps/client/src/ee/ai-chat/pages/ai-chat.tsx new file mode 100644 index 00000000..bb264b73 --- /dev/null +++ b/apps/client/src/ee/ai-chat/pages/ai-chat.tsx @@ -0,0 +1,39 @@ +import { useParams } from "react-router-dom"; +import { ErrorBoundary } from "react-error-boundary"; +import { Button } from "@mantine/core"; +import { IconAlertTriangle } from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import AiChatLayout from "../components/ai-chat-layout"; +import { EmptyState } from "@/components/ui/empty-state.tsx"; +import classes from "../styles/ai-chat.module.css"; + +export default function AiChat() { + const { t } = useTranslation(); + const { chatId } = useParams<{ chatId: string }>(); + + return ( +
+ ( + + {t("Try again")} + + } + /> + )} + > + + +
+ ); +} diff --git a/apps/client/src/ee/ai-chat/queries/ai-chat-query.ts b/apps/client/src/ee/ai-chat/queries/ai-chat-query.ts new file mode 100644 index 00000000..8992a115 --- /dev/null +++ b/apps/client/src/ee/ai-chat/queries/ai-chat-query.ts @@ -0,0 +1,61 @@ +import { + useQuery, + useMutation, + useQueryClient, + useInfiniteQuery, +} from "@tanstack/react-query"; +import { + listChats, + getChatInfo, + deleteChat, + updateChatTitle, + searchChats, +} from "../services/ai-chat-service"; + +export function useChatsQuery() { + return useInfiniteQuery({ + queryKey: ["ai-chats"], + queryFn: ({ pageParam }) => + listChats({ cursor: pageParam, limit: 30 }), + initialPageParam: undefined as string | undefined, + getNextPageParam: (lastPage) => + lastPage.meta.hasNextPage ? lastPage.meta.nextCursor : undefined, + }); +} + +export function useChatInfoQuery(chatId: string | undefined) { + return useQuery({ + queryKey: ["ai-chat", chatId], + queryFn: () => getChatInfo(chatId!), + enabled: !!chatId, + }); +} + +export function useDeleteChatMutation() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (chatId: string) => deleteChat(chatId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["ai-chats"] }); + }, + }); +} + +export function useUpdateChatTitleMutation() { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ chatId, title }: { chatId: string; title: string }) => + updateChatTitle(chatId, title), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["ai-chats"] }); + }, + }); +} + +export function useSearchChatsQuery(query: string) { + return useQuery({ + queryKey: ["ai-chats-search", query], + queryFn: () => searchChats(query), + enabled: query.length > 0, + }); +} diff --git a/apps/client/src/ee/ai-chat/services/ai-chat-service.ts b/apps/client/src/ee/ai-chat/services/ai-chat-service.ts new file mode 100644 index 00000000..2932372e --- /dev/null +++ b/apps/client/src/ee/ai-chat/services/ai-chat-service.ts @@ -0,0 +1,144 @@ +import api from "@/lib/api-client.ts"; +import type { + AiChat, + AiChatMessage, + AiChatStreamEvent, + ChatAttachment, +} from "../types/ai-chat.types"; +import { IPagination } from "@/lib/types.ts"; + +export async function createChat(): Promise { + const req = await api.post("/ai/chats/create"); + return req.data; +} + +export async function listChats(params?: { + limit?: number; + cursor?: string; +}): Promise> { + const req = await api.post("/ai/chats", params); + return req.data; +} + +export async function getChatInfo( + chatId: string, +): Promise<{ chat: AiChat; messages: AiChatMessage[] }> { + const req = await api.post("/ai/chats/info", { chatId }); + return req.data; +} + +export async function deleteChat(chatId: string): Promise { + await api.post("/ai/chats/delete", { chatId }); +} + +export async function updateChatTitle( + chatId: string, + title: string, +): Promise { + await api.post("/ai/chats/update", { chatId, title }); +} + +export async function searchChats(query: string): Promise { + const req = await api.post("/ai/chats/search", { query }); + return req.data; +} + +export async function uploadChatFile( + file: File, + chatId?: string, +): Promise { + const formData = new FormData(); + formData.append("file", file); + if (chatId) { + formData.append("chatId", chatId); + } + return await api.post("/ai/chats/upload", formData, { + headers: { "Content-Type": "multipart/form-data" }, + }); +} + +export function sendChatMessage( + params: { + chatId?: string; + content: string; + mentionedPageIds?: string[]; + contextPageId?: string; + attachmentIds?: string[]; + }, + onEvent: (event: AiChatStreamEvent) => void, + onError?: (error: string) => void, + onComplete?: () => void, +): AbortController { + const abortController = new AbortController(); + + (async () => { + try { + const response = await fetch("/api/ai/chats/send", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(params), + signal: abortController.signal, + credentials: "include", + }); + + if (!response.ok) { + const errorBody = await response.text(); + let errorMessage = `HTTP error ${response.status}`; + try { + const parsed = JSON.parse(errorBody); + errorMessage = parsed.message || errorMessage; + } catch { + // use default + } + onError?.(errorMessage); + return; + } + + const reader = response.body?.getReader(); + const decoder = new TextDecoder(); + + if (!reader) { + onError?.("Response body is not readable"); + return; + } + + let buffer = ""; + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split("\n"); + buffer = lines.pop() || ""; + + for (const line of lines) { + if (line.startsWith("data: ")) { + const data = line.slice(6); + if (data === "[DONE]") { + onComplete?.(); + return; + } + try { + const parsed = JSON.parse(data) as AiChatStreamEvent; + onEvent(parsed); + } catch { + // Skip invalid JSON + } + } + } + } + } finally { + reader.releaseLock(); + } + + onComplete?.(); + } catch (error: any) { + if (error.name !== "AbortError") { + onError?.(error.message); + } + } + })(); + + return abortController; +} diff --git a/apps/client/src/ee/ai-chat/styles/ai-chat.module.css b/apps/client/src/ee/ai-chat/styles/ai-chat.module.css new file mode 100644 index 00000000..27b0f0c0 --- /dev/null +++ b/apps/client/src/ee/ai-chat/styles/ai-chat.module.css @@ -0,0 +1,169 @@ +.layout { + display: flex; + height: 100%; + width: 100%; +} + +.main { + flex: 1; + display: flex; + flex-direction: column; + height: calc(100vh - 45px - 2 * var(--mantine-spacing-md)); + max-width: 900px; + margin: 0 auto; + width: 100%; +} + +.messageListWrapper { + flex: 1; + position: relative; + display: flex; + flex-direction: column; + min-height: 0; + height: 100%; + width: 100%; +} + +.messageList { + flex: 1; + overflow-y: auto; + padding: var(--mantine-spacing-md) var(--mantine-spacing-lg); +} + +.messageErrorFallback { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + margin-bottom: var(--mantine-spacing-lg); + border-radius: var(--mantine-radius-sm); + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); + font-size: var(--mantine-font-size-xs); +} + +.scrollToBottomButton { + position: absolute; + bottom: 12px; + left: 50%; + transform: translateX(-50%); + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + cursor: pointer; + padding: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + transition: background-color 120ms ease, border-color 120ms ease, transform 120ms ease; + z-index: 2; +} + +.scrollToBottomButton:hover { + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5)); + border-color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-3)); +} + +.scrollToBottomButton:active { + transform: translateX(-50%) scale(0.95); +} + +.inputArea { + padding: var(--mantine-spacing-xs) var(--mantine-spacing-lg) var(--mantine-spacing-lg); +} + +/* Empty state - Notion AI style centered layout */ +.emptyState { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: var(--mantine-spacing-xl) var(--mantine-spacing-lg); +} + +.emptyStateIcon { + width: 48px; + height: 48px; + margin-bottom: var(--mantine-spacing-sm); + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); +} + +.emptyStateBrand { + font-size: var(--mantine-font-size-xs); + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); + margin-bottom: var(--mantine-spacing-xs); +} + +.emptyStateTitle { + font-size: 1.5rem; + font-weight: 600; + color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); + margin-bottom: var(--mantine-spacing-xl); + text-align: center; +} + +.emptyStateInput { + width: 100%; + max-width: 600px; + margin-bottom: var(--mantine-spacing-xl); + padding: 6px 0; +} + +.suggestionsSection { + width: 100%; + max-width: 600px; +} + +.suggestionsLabel { + font-size: var(--mantine-font-size-xs); + font-weight: 500; + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: var(--mantine-spacing-sm); +} + +.suggestionsGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--mantine-spacing-sm); +} + +.suggestionCard { + display: flex; + align-items: flex-start; + gap: var(--mantine-spacing-sm); + padding: var(--mantine-spacing-sm) var(--mantine-spacing-md); + border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + border-radius: var(--mantine-radius-md); + cursor: pointer; + background: transparent; + transition: background-color 150ms, border-color 150ms; + text-align: left; + width: 100%; + + @mixin hover { + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + border-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + } +} + +.suggestionIcon { + flex-shrink: 0; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); + margin-top: 1px; +} + +.suggestionText { + font-size: var(--mantine-font-size-sm); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + line-height: 1.4; +} diff --git a/apps/client/src/ee/ai-chat/styles/aside-chat-panel.module.css b/apps/client/src/ee/ai-chat/styles/aside-chat-panel.module.css new file mode 100644 index 00000000..f8958615 --- /dev/null +++ b/apps/client/src/ee/ai-chat/styles/aside-chat-panel.module.css @@ -0,0 +1,139 @@ +.panel { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; +} + +.toolbar { + display: flex; + align-items: center; + gap: 4px; + padding: 0 0 var(--mantine-spacing-sm) 0; + border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); +} + +.toolbarSpacer { + flex: 1; +} + +.titleButton { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 8px; + border-radius: var(--mantine-radius-sm); + font-size: var(--mantine-font-size-sm); + font-weight: 500; + color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); + max-width: 60%; + min-width: 0; +} + +.titleButton:hover { + background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6)); +} + +.titleText { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} + +.messages { + flex: 1; + overflow-y: auto; + padding: var(--mantine-spacing-sm) 0; + scroll-behavior: smooth; +} + +.inputArea { + padding-top: var(--mantine-spacing-sm); +} + +.emptyState { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--mantine-spacing-md); + padding: var(--mantine-spacing-xl) var(--mantine-spacing-sm); +} + +.emptyStateIcon { + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); +} + +.emptyStateTitle { + font-size: var(--mantine-font-size-lg); + font-weight: 600; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + text-align: center; +} + +.quickActions { + display: flex; + flex-direction: column; + gap: 6px; + width: 100%; +} + +.quickAction { + display: flex; + align-items: center; + gap: var(--mantine-spacing-sm); + padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm); + border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + border-radius: var(--mantine-radius-md); + cursor: pointer; + background: transparent; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + font-size: var(--mantine-font-size-sm); + text-align: left; + width: 100%; + transition: background-color 150ms, border-color 150ms; + + @mixin hover { + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + border-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + } +} + +.quickActionIcon { + flex-shrink: 0; + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); +} + +.historyList { + max-height: 300px; + overflow-y: auto; +} + +.historyItem { + display: flex; + align-items: center; + padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm); + cursor: pointer; + border-radius: var(--mantine-radius-sm); + font-size: var(--mantine-font-size-sm); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + transition: background-color 150ms; + + @mixin hover { + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + } + + &[data-active] { + background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5)); + } +} + +.historyItemTitle { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/apps/client/src/ee/ai-chat/styles/chat-input.module.css b/apps/client/src/ee/ai-chat/styles/chat-input.module.css new file mode 100644 index 00000000..20b287c1 --- /dev/null +++ b/apps/client/src/ee/ai-chat/styles/chat-input.module.css @@ -0,0 +1,242 @@ +.inputWrapper { + position: relative; + overflow: hidden; + border: 1px solid + light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + border-radius: 16px; + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); + box-shadow: light-dark( + 0 2px 40px 4px rgba(0, 0, 0, 0.07), + 0 2px 40px 4px rgba(0, 0, 0, 0.5) + ); + transition: + border-color 150ms, + box-shadow 150ms; + + &:focus-within { + border-color: light-dark( + var(--mantine-color-gray-3), + var(--mantine-color-dark-4) + ); + box-shadow: light-dark( + 0 4px 48px 6px rgba(0, 0, 0, 0.09), + 0 4px 48px 6px rgba(0, 0, 0, 0.6) + ); + } +} + +.inputWrapperFlat { + position: relative; + overflow: hidden; + border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + border-radius: 12px; + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); + box-shadow: none; + transition: border-color 150ms; + + &:focus-within { + border-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + } +} + +.disclaimer { + margin-top: 6px; + text-align: center; + font-size: var(--mantine-font-size-xs); + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); +} + +.attachmentChips { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 10px 14px 0; +} + +.attachmentChip { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 4px 8px; + border-radius: 8px; + background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + font-size: var(--mantine-font-size-xs); + max-width: 200px; +} + +.attachmentChipUploading { + opacity: 0.55; +} + +.attachmentChipName { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.attachmentChipRemove { + display: flex; + align-items: center; + justify-content: center; + border: none; + background: none; + cursor: pointer; + padding: 0; + margin-left: 2px; + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + border-radius: 50%; + + @mixin hover { + color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); + } +} + +.editorContent { + overflow: hidden; + + :global(.ProseMirror) { + outline: none; + border: none; + background-color: transparent; + padding: 14px 18px 8px; + font-size: 15px; + line-height: 1.6; + max-height: 200px; + overflow-y: auto; + min-height: 24px; + color: light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0)); + } + + :global(.ProseMirror p) { + margin-block-start: 0; + margin-block-end: 0; + } + + :global(.ProseMirror p.is-editor-empty:first-child::before) { + color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-3)); + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; + } +} + +.actions { + display: flex; + align-items: center; + justify-content: flex-end; + padding: 4px 12px 10px; + gap: var(--mantine-spacing-xs); +} + +.sendButton { + width: 28px; + height: 28px; + min-width: 28px; + min-height: 28px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + border: none; + cursor: pointer; + transition: background-color 150ms, opacity 150ms; + background: light-dark(var(--mantine-color-dark-9), var(--mantine-color-gray-0)); + color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-9)); + + &:disabled { + opacity: 0.25; + cursor: default; + } + + @mixin hover { + &:not(:disabled) { + opacity: 0.85; + } + } +} + +.attachButton { + display: flex; + align-items: center; + justify-content: center; + border: none; + background: none; + cursor: pointer; + padding: 2px; + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + transition: color 150ms; + + @mixin hover { + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + } +} + +.plusButton { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 50%; + border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + background: none; + cursor: pointer; + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + transition: color 150ms, background-color 150ms; + + @mixin hover { + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + } +} + +.plusMenuItem { + display: flex; + align-items: center; + gap: var(--mantine-spacing-sm); + padding: var(--mantine-spacing-xs) var(--mantine-spacing-sm); + border: none; + background: none; + cursor: pointer; + width: 100%; + font-size: var(--mantine-font-size-sm); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + border-radius: var(--mantine-radius-sm); + transition: background-color 150ms; + + @mixin hover { + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + } + + &:disabled { + opacity: 0.45; + cursor: not-allowed; + background: none; + } +} + +.plusMenuIcon { + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); +} + +.stopButton { + width: 28px; + height: 28px; + min-width: 28px; + min-height: 28px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + cursor: pointer; + transition: background-color 150ms; + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + + @mixin hover { + background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5)); + } +} diff --git a/apps/client/src/ee/ai-chat/styles/chat-message.module.css b/apps/client/src/ee/ai-chat/styles/chat-message.module.css new file mode 100644 index 00000000..33e39dd6 --- /dev/null +++ b/apps/client/src/ee/ai-chat/styles/chat-message.module.css @@ -0,0 +1,286 @@ +.message { + margin-bottom: var(--mantine-spacing-lg); +} + +.userMessage { + composes: message; + display: flex; + justify-content: flex-end; +} + +.userBubble { + max-width: 75%; + padding: 10px 16px; + border-radius: 18px; + background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6)); + color: light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0)); + font-size: 15px; + line-height: 1.6; + word-wrap: break-word; + overflow-wrap: break-word; +} + +[data-aside-chat] .userBubble { + background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); + border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); +} + +.userBubble p { + margin: 0; +} + +.messageAttachments { + display: flex; + flex-wrap: wrap; + gap: 4px; + margin-bottom: 6px; +} + +.messageAttachmentChip { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 8px; + border-radius: 6px; + background: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-2)); + font-size: var(--mantine-font-size-xs); + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.assistantMessage { + composes: message; +} + +.messageContent { + font-size: 15px; + line-height: 1.7; + color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-1)); + word-wrap: break-word; + overflow-wrap: break-word; +} + +.messageContent p { + margin: 0 0 0.75em 0; +} + +.messageContent p:last-child { + margin-bottom: 0; +} + +.messageContent ul, +.messageContent ol { + margin: 0.5em 0 0.75em 0; + padding-left: 1.5em; +} + +.messageContent li { + margin-bottom: 0.3em; +} + +.messageContent h1, +.messageContent h2, +.messageContent h3 { + margin: 1em 0 0.5em 0; + font-weight: 600; + color: light-dark(var(--mantine-color-gray-9), var(--mantine-color-dark-0)); +} + +.messageContent h1 { + font-size: 1.4em; +} + +.messageContent h2 { + font-size: 1.2em; +} + +.messageContent h3 { + font-size: 1.05em; +} + +.messageContent pre { + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); + padding: var(--mantine-spacing-sm) var(--mantine-spacing-md); + border-radius: var(--mantine-radius-md); + overflow-x: auto; + font-size: var(--mantine-font-size-sm); + margin: 0.75em 0; +} + +.messageContent code { + background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6)); + padding: 2px 6px; + border-radius: 4px; + font-size: 0.88em; +} + +.messageContent pre code { + background: none; + padding: 0; +} + +.messageContent blockquote { + border-left: 3px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + padding-left: var(--mantine-spacing-md); + margin: 0.75em 0; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-2)); +} + +.messageContent a { + color: light-dark(var(--mantine-color-blue-7), var(--mantine-color-blue-4)); + text-decoration: none; + + @mixin hover { + text-decoration: underline; + } +} + +.messageContent a[href^="/s/"], +.messageContent a[href^="/p/"] { + color: light-dark(var(--mantine-color-dark-4), var(--mantine-color-dark-1)); + font-weight: 500; + text-decoration: none; + cursor: pointer; + + @mixin light { + border-bottom: 0.05em solid var(--mantine-color-dark-0); + } + + @mixin dark { + border-bottom: 0.05em solid var(--mantine-color-dark-2); + } + + @mixin hover { + text-decoration: none; + @mixin light { + border-bottom-color: var(--mantine-color-dark-2); + } + @mixin dark { + border-bottom-color: var(--mantine-color-dark-0); + } + } +} + +.messageContent hr { + border: none; + border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + margin: 1em 0; +} + +.toolGroup { + margin: 6px 0; + font-size: var(--mantine-font-size-xs); +} + +.toolGroupHeader { + display: inline-flex; + align-items: center; + gap: 6px; + cursor: pointer; + user-select: none; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); + line-height: 1.4; + transition: color 120ms ease; +} + +.toolGroupHeader:hover { + color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); +} + +.toolGroupLabel { + font-weight: 500; +} + +.toolGroupSteps { + margin-top: 4px; + padding-left: 14px; + display: flex; + flex-direction: column; + gap: 2px; +} + +.toolStep { + font-size: var(--mantine-font-size-xs); +} + +.toolStepRow { + display: inline-flex; + align-items: center; + gap: 4px; + cursor: pointer; + user-select: none; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); + line-height: 1.5; + transition: color 120ms ease; +} + +.toolStepRow:hover { + color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); +} + +.toolStepBullet { + display: inline-block; + width: 8px; + text-align: center; + opacity: 0.6; +} + +.toolStepDetails { + margin-top: 4px; + margin-left: 18px; + padding: 6px 10px; + border-radius: var(--mantine-radius-sm); + background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7)); + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + font-size: 11px; + line-height: 1.5; + overflow-x: auto; +} + +.messageActions { + display: flex; + align-items: center; + gap: 4px; + margin-top: 4px; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); +} + +.processingIndicator { + display: inline-flex; + align-items: center; + gap: 6px; + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + font-size: var(--mantine-font-size-sm); +} + +.processingSpinner { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.streamingCursor { + display: inline-block; + width: 2px; + height: 1em; + background: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); + animation: blink 1s step-end infinite; + vertical-align: text-bottom; + margin-left: 1px; +} + +@keyframes blink { + 50% { + opacity: 0; + } +} diff --git a/apps/client/src/ee/ai-chat/styles/chat-sidebar.module.css b/apps/client/src/ee/ai-chat/styles/chat-sidebar.module.css new file mode 100644 index 00000000..c7991147 --- /dev/null +++ b/apps/client/src/ee/ai-chat/styles/chat-sidebar.module.css @@ -0,0 +1,138 @@ +.sidebar { + height: 100%; + width: 100%; + padding: var(--mantine-spacing-md); + display: flex; + flex-direction: column; + gap: var(--mantine-spacing-xs); +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: var(--mantine-spacing-xs); +} + +.title { + font-weight: 600; + font-size: var(--mantine-font-size-sm); +} + +.searchInput { + margin-bottom: var(--mantine-spacing-xs); +} + +.chatList { + flex: 1; + overflow-y: auto; +} + +.chatGroup + .chatGroup { + margin-top: var(--mantine-spacing-sm); +} + +.chatGroupLabel { + padding: 4px var(--mantine-spacing-xs); + font-size: var(--mantine-font-size-xs); + font-weight: 600; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); + user-select: none; +} + +.chatListEmpty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: var(--mantine-spacing-xl) var(--mantine-spacing-md); + text-align: center; + gap: 4px; + user-select: none; +} + +.chatListEmptyIcon { + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + margin-bottom: var(--mantine-spacing-xs); +} + +.chatListEmptyTitle { + font-size: var(--mantine-font-size-sm); + font-weight: 600; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); +} + +.chatListEmptyHint { + font-size: var(--mantine-font-size-xs); + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-3)); + line-height: 1.4; +} + +.chatItem { + display: flex; + align-items: center; + padding: 8px var(--mantine-spacing-xs); + border-radius: var(--mantine-radius-sm); + cursor: pointer; + text-decoration: none; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1)); + font-size: var(--mantine-font-size-sm); + user-select: none; + gap: var(--mantine-spacing-xs); + + @mixin hover { + background-color: light-dark( + var(--mantine-color-gray-1), + var(--mantine-color-dark-6) + ); + } + + &[data-active] { + background-color: light-dark( + var(--mantine-color-gray-2), + var(--mantine-color-dark-6) + ); + } +} + +.chatItemTitle { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.chatItemDate { + font-size: var(--mantine-font-size-xs); + color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3)); + white-space: nowrap; + transition: opacity 150ms; +} + +.chatItemRenameInput { + font-size: var(--mantine-font-size-sm); + padding: 0; + height: auto; + min-height: 0; + background: transparent; + color: inherit; +} + +.chatItem:hover .chatItemDate { + opacity: 0; +} + +.chatItemActions { + position: absolute; + right: var(--mantine-spacing-xs); + opacity: 0; + transition: opacity 150ms; +} + +.chatItem { + position: relative; +} + +.chatItem:hover .chatItemActions { + opacity: 1; +} diff --git a/apps/client/src/ee/ai-chat/types/ai-chat.types.ts b/apps/client/src/ee/ai-chat/types/ai-chat.types.ts new file mode 100644 index 00000000..89754d26 --- /dev/null +++ b/apps/client/src/ee/ai-chat/types/ai-chat.types.ts @@ -0,0 +1,49 @@ +export type AiChat = { + id: string; + workspaceId: string; + creatorId: string; + title: string | null; + createdAt: string; + updatedAt: string; +}; + +export type AiChatToolCall = { + id: string; + name: string; + args: Record; + result?: unknown; +}; + +export type AiChatMessage = { + id: string; + chatId: string; + role: 'user' | 'assistant' | 'tool'; + content: string | null; + toolCalls: AiChatToolCall[] | null; + metadata: Record | null; + createdAt: string; +}; + +export type AiChatStreamEvent = + | { type: 'chat_created'; chatId: string } + | { type: 'content'; text: string } + | { type: 'tool_call'; id: string; name: string; args: Record } + | { type: 'tool_result'; id: string; result: unknown } + | { type: 'done'; messageId: string; usage?: Record } + | { type: 'error'; message: string; code?: string; retryable?: boolean }; + +export type PageMention = { + id: string; + title: string; + slugId: string; + spaceSlug?: string; + icon?: string; +}; + +export type ChatAttachment = { + id: string; + fileName: string; + fileExt: string; + fileSize: number; + mimeType: string; +}; diff --git a/apps/client/src/ee/ai/pages/ai-settings.tsx b/apps/client/src/ee/ai/pages/ai-settings.tsx index c3f93810..3a5a281e 100644 --- a/apps/client/src/ee/ai/pages/ai-settings.tsx +++ b/apps/client/src/ee/ai/pages/ai-settings.tsx @@ -6,6 +6,7 @@ import useUserRole from "@/hooks/use-user-role.tsx"; import { useTranslation } from "react-i18next"; import EnableAiSearch from "@/ee/ai/components/enable-ai-search.tsx"; import EnableGenerativeAi from "@/ee/ai/components/enable-generative-ai.tsx"; +import EnableAiChat from "@/ee/ai-chat/components/enable-ai-chat.tsx"; import McpSettings from "@/ee/ai/components/mcp-settings.tsx"; import { Alert, Stack, Tabs } from "@mantine/core"; import { IconInfoCircle } from "@tabler/icons-react"; @@ -71,6 +72,7 @@ export default function AiSettings() { {!isCloud() && } + diff --git a/apps/client/src/features/editor/components/mention/mention-list.tsx b/apps/client/src/features/editor/components/mention/mention-list.tsx index af6f8a1d..3ddf3976 100644 --- a/apps/client/src/features/editor/components/mention/mention-list.tsx +++ b/apps/client/src/features/editor/components/mention/mention-list.tsx @@ -62,7 +62,7 @@ const MentionList = forwardRef((props, ref) => { query: props.query, includeUsers: true, includePages: true, - spaceId: space.id, + spaceId: space?.id, limit: props.query ? 10 : 5, preload: true, }); diff --git a/apps/client/src/features/editor/components/mention/mention-suggestion.ts b/apps/client/src/features/editor/components/mention/mention-suggestion.ts index 658fd182..894e7204 100644 --- a/apps/client/src/features/editor/components/mention/mention-suggestion.ts +++ b/apps/client/src/features/editor/components/mention/mention-suggestion.ts @@ -53,8 +53,8 @@ const mentionRenderItems = () => { const editorDom = props.editor?.view?.dom; const asideEl = editorDom?.closest(".mantine-AppShell-aside"); const dialogEl = editorDom?.closest("[data-comment-dialog]"); - const isInCommentContext = !!(asideEl || dialogEl); - // const isInCommentContext = !!asideEl; + const chatInput = editorDom?.closest("[data-chat-input]"); + const isInCommentContext = !!(asideEl || dialogEl || chatInput); component = new ReactRenderer(MentionList, { props: { ...props, isInCommentContext }, diff --git a/apps/client/src/features/home/components/home-ai-prompt.module.css b/apps/client/src/features/home/components/home-ai-prompt.module.css new file mode 100644 index 00000000..e6d81606 --- /dev/null +++ b/apps/client/src/features/home/components/home-ai-prompt.module.css @@ -0,0 +1,28 @@ +.wrapper { + display: flex; + flex-direction: column; + align-items: center; + padding: var(--mantine-spacing-xl) var(--mantine-spacing-md) var(--mantine-spacing-lg); +} + +.heading { + font-size: 1.75rem; + font-weight: 600; + color: light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0)); + text-align: center; + margin: 0; + line-height: 1.2; +} + +.subtitle { + font-size: var(--mantine-font-size-sm); + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-2)); + text-align: center; + margin-top: 6px; + margin-bottom: var(--mantine-spacing-lg); +} + +.inputContainer { + width: 100%; + max-width: 640px; +} diff --git a/apps/client/src/features/home/components/home-ai-prompt.tsx b/apps/client/src/features/home/components/home-ai-prompt.tsx new file mode 100644 index 00000000..c3c69398 --- /dev/null +++ b/apps/client/src/features/home/components/home-ai-prompt.tsx @@ -0,0 +1,60 @@ +import { useAtomValue } from "jotai"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; +import ChatInput from "@/ee/ai-chat/components/chat-input"; +import type { + ChatAttachment, + PageMention, +} from "@/ee/ai-chat/types/ai-chat.types"; +import classes from "./home-ai-prompt.module.css"; + +export type HomeAiPromptInitialState = { + initialContent: string; + initialMentions: PageMention[]; + initialAttachments: ChatAttachment[]; +}; + +export default function HomeAiPrompt() { + const { t } = useTranslation(); + const navigate = useNavigate(); + const workspace = useAtomValue(workspaceAtom); + + const aiChatEnabled = workspace?.settings?.ai?.chat === true; + if (!aiChatEnabled) return null; + + const handleSend = ( + content: string, + mentions: PageMention[], + attachments: ChatAttachment[], + ) => { + if (!content.trim() && attachments.length === 0) return; + const state: HomeAiPromptInitialState = { + initialContent: content, + initialMentions: mentions, + initialAttachments: attachments, + }; + navigate("/ai", { state }); + }; + + return ( +
+

+ {t("Welcome to {{name}}", { name: workspace?.name ?? "Docmost" })} +

+
+ {t("Ask anything or search your workspace")} +
+ +
+ {}} + placeholder={t("Ask anything... Use @ to mention pages")} + autofocus={false} + /> +
+
+ ); +} diff --git a/apps/client/src/features/search/components/search-control.module.css b/apps/client/src/features/search/components/search-control.module.css index 5e5a9c26..80be81d9 100644 --- a/apps/client/src/features/search/components/search-control.module.css +++ b/apps/client/src/features/search/components/search-control.module.css @@ -29,6 +29,8 @@ border-radius: var(--mantine-radius-sm); border: 1px solid; font-weight: bold; + white-space: nowrap; + flex-shrink: 0; @mixin light { color: var(--mantine-color-gray-7); diff --git a/apps/client/src/features/space/components/space-carousel.module.css b/apps/client/src/features/space/components/space-carousel.module.css new file mode 100644 index 00000000..87b9f1de --- /dev/null +++ b/apps/client/src/features/space/components/space-carousel.module.css @@ -0,0 +1,22 @@ +.card { + background-color: var(--mantine-color-body); + width: 220px; + + @mixin hover { + box-shadow: var(--mantine-shadow-xs); + transform: scale(1.02); + } +} + +.cardSection { + background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6)); +} + +.title { + font-family: + Greycliff CF, + var(--mantine-font-family); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} diff --git a/apps/client/src/features/space/components/space-carousel.tsx b/apps/client/src/features/space/components/space-carousel.tsx new file mode 100644 index 00000000..e8c4eada --- /dev/null +++ b/apps/client/src/features/space/components/space-carousel.tsx @@ -0,0 +1,77 @@ +import { Text, Card, rem, Group, Button } from "@mantine/core"; +import { + prefetchSpace, + useGetSpacesQuery, +} from "@/features/space/queries/space-query.ts"; +import { getSpaceUrl } from "@/lib/config.ts"; +import { Link } from "react-router-dom"; +import classes from "./space-carousel.module.css"; +import { formatMemberCount } from "@/lib"; +import { useTranslation } from "react-i18next"; +import { IconArrowRight } from "@tabler/icons-react"; +import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; +import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts"; +import CardCarousel from "@/components/ui/card-carousel"; + +export default function SpaceCarousel() { + const { t } = useTranslation(); + const { data } = useGetSpacesQuery({ limit: 20 }); + + const cards = data?.items.map((space) => ( + prefetchSpace(space.slug, space.id)} + className={classes.card} + withBorder + > + + + + + {space.name} + + + + {formatMemberCount(space.memberCount, t)} + + + )); + + return ( + <> + + + {t("Spaces you belong to")} + + + + {cards} + + {data?.items && data.items.length > 1 && ( + + + + )} + + ); +} diff --git a/apps/client/src/features/workspace/types/workspace.types.ts b/apps/client/src/features/workspace/types/workspace.types.ts index 4d98a5ef..96df25b7 100644 --- a/apps/client/src/features/workspace/types/workspace.types.ts +++ b/apps/client/src/features/workspace/types/workspace.types.ts @@ -43,6 +43,7 @@ export interface IWorkspaceAiSettings { search?: boolean; generative?: boolean; mcp?: boolean; + chat?: boolean; } export interface IWorkspaceSharingSettings { diff --git a/apps/client/src/pages/dashboard/home.tsx b/apps/client/src/pages/dashboard/home.tsx index 900afa40..cefff053 100644 --- a/apps/client/src/pages/dashboard/home.tsx +++ b/apps/client/src/pages/dashboard/home.tsx @@ -1,6 +1,7 @@ import { Container, Space } from "@mantine/core"; import HomeTabs from "@/features/home/components/home-tabs"; -import SpaceGrid from "@/features/space/components/space-grid.tsx"; +import HomeAiPrompt from "@/features/home/components/home-ai-prompt"; +import SpaceCarousel from "@/features/space/components/space-carousel.tsx"; import { getAppName } from "@/lib/config.ts"; import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; @@ -16,7 +17,11 @@ export default function Home() { - + + + + + diff --git a/apps/server/package.json b/apps/server/package.json index 9994ba43..a1a061db 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -75,10 +75,12 @@ "class-transformer": "^0.5.1", "class-validator": "^0.15.1", "cookie": "^1.1.1", + "fast-bm25": "0.0.5", "fastify-ip": "^2.0.0", "fs-extra": "^11.3.4", "happy-dom": "20.8.9", "ioredis": "^5.10.1", + "js-tiktoken": "^1.0.21", "jsonwebtoken": "^9.0.3", "kysely": "^0.28.14", "kysely-migration-cli": "^0.4.2", diff --git a/apps/server/src/core/attachment/attachment.constants.ts b/apps/server/src/core/attachment/attachment.constants.ts index a9bce5c1..a5d90692 100644 --- a/apps/server/src/core/attachment/attachment.constants.ts +++ b/apps/server/src/core/attachment/attachment.constants.ts @@ -3,6 +3,7 @@ export enum AttachmentType { WorkspaceIcon = 'workspace-icon', SpaceIcon = 'space-icon', File = 'file', + Chat = 'chat', } export const validImageExtensions = ['.jpg', '.png', '.jpeg']; diff --git a/apps/server/src/core/attachment/attachment.controller.ts b/apps/server/src/core/attachment/attachment.controller.ts index 6382b34e..7b24cc35 100644 --- a/apps/server/src/core/attachment/attachment.controller.ts +++ b/apps/server/src/core/attachment/attachment.controller.ts @@ -178,21 +178,29 @@ export class AttachmentController { } const attachment = await this.attachmentRepo.findById(fileId); - if ( - !attachment || - attachment.workspaceId !== workspace.id || - !attachment.pageId || - !attachment.spaceId - ) { + if (!attachment || attachment.workspaceId !== workspace.id) { throw new NotFoundException(); } - const page = await this.pageRepo.findById(attachment.pageId); - if (!page) { - throw new NotFoundException(); - } + if (attachment.aiChatId) { + // Chat-owned attachment: only the user who uploaded (and therefore + // owns the chat, per AttachmentRepo.claimAttachmentsForChat) can + // read it back. + if (attachment.creatorId !== user.id) { + throw new NotFoundException(); + } + } else { + if (!attachment.pageId || !attachment.spaceId) { + throw new NotFoundException(); + } - await this.pageAccessService.validateCanView(page, user); + const page = await this.pageRepo.findById(attachment.pageId); + if (!page) { + throw new NotFoundException(); + } + + await this.pageAccessService.validateCanView(page, user); + } try { return await this.sendFileResponse(req, res, attachment, 'private'); diff --git a/apps/server/src/core/attachment/attachment.utils.ts b/apps/server/src/core/attachment/attachment.utils.ts index 88edb2af..616fed53 100644 --- a/apps/server/src/core/attachment/attachment.utils.ts +++ b/apps/server/src/core/attachment/attachment.utils.ts @@ -71,6 +71,8 @@ export function getAttachmentFolderPath( return `${workspaceId}/space-logos`; case AttachmentType.File: return `${workspaceId}/files`; + case AttachmentType.Chat: + return `${workspaceId}/chat-files`; default: return `${workspaceId}/files`; } diff --git a/apps/server/src/core/attachment/processors/attachment.processor.ts b/apps/server/src/core/attachment/processors/attachment.processor.ts index bcf8a08a..7249a9fa 100644 --- a/apps/server/src/core/attachment/processors/attachment.processor.ts +++ b/apps/server/src/core/attachment/processors/attachment.processor.ts @@ -28,6 +28,11 @@ export class AttachmentProcessor extends WorkerHost implements OnModuleDestroy { job.data.pageId, ); } + if (job.name === QueueJob.DELETE_AI_CHAT_ATTACHMENTS) { + await this.attachmentService.handleDeleteAiChatAttachments( + job.data.aiChatId, + ); + } if ( job.name === QueueJob.ATTACHMENT_INDEX_CONTENT || job.name === QueueJob.ATTACHMENT_INDEXING diff --git a/apps/server/src/core/attachment/services/attachment.service.ts b/apps/server/src/core/attachment/services/attachment.service.ts index 6419ed58..766c9f65 100644 --- a/apps/server/src/core/attachment/services/attachment.service.ts +++ b/apps/server/src/core/attachment/services/attachment.service.ts @@ -289,6 +289,31 @@ export class AttachmentService { ); } + async handleDeleteAiChatAttachments(aiChatId: string) { + try { + const attachments = await this.attachmentRepo.findByAiChatId(aiChatId); + if (!attachments || attachments.length === 0) { + return; + } + + await Promise.all( + attachments.map(async (attachment) => { + try { + await this.storageService.delete(attachment.filePath); + await this.attachmentRepo.deleteAttachmentById(attachment.id); + } catch (err) { + this.logger.log( + `DeleteAiChatAttachments: failed to delete attachment ${attachment.id}:`, + err, + ); + } + }), + ); + } catch (err) { + throw err; + } + } + async handleDeleteSpaceAttachments(spaceId: string) { try { const attachments = await this.attachmentRepo.findBySpaceId(spaceId); diff --git a/apps/server/src/core/auth/auth.controller.ts b/apps/server/src/core/auth/auth.controller.ts index 441bfc1c..89bb9e1b 100644 --- a/apps/server/src/core/auth/auth.controller.ts +++ b/apps/server/src/core/auth/auth.controller.ts @@ -11,6 +11,10 @@ import { Logger, } from '@nestjs/common'; import { SkipThrottle, ThrottlerGuard } from '@nestjs/throttler'; +import { + AI_CHAT_THROTTLER, + AUTH_THROTTLER, +} from '../../integrations/throttle/throttler-names'; import { LoginDto } from './dto/login.dto'; import { AuthService } from './services/auth.service'; import { SessionService } from '../session/session.service'; @@ -34,6 +38,7 @@ import { IAuditService, } from '../../integrations/audit/audit.service'; +@SkipThrottle({ [AI_CHAT_THROTTLER]: true }) @UseGuards(ThrottlerGuard) @Controller('auth') export class AuthController { @@ -113,7 +118,7 @@ export class AuthController { return workspace; } - @SkipThrottle() + @SkipThrottle({ [AUTH_THROTTLER]: true }) @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post('change-password') @@ -176,7 +181,7 @@ export class AuthController { return this.authService.verifyUserToken(verifyUserTokenDto, workspace.id); } - @SkipThrottle() + @SkipThrottle({ [AUTH_THROTTLER]: true }) @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post('collab-token') @@ -187,7 +192,7 @@ export class AuthController { return this.authService.getCollabToken(user, workspace.id); } - @SkipThrottle() + @SkipThrottle({ [AUTH_THROTTLER]: true }) @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post('logout') diff --git a/apps/server/src/core/workspace/dto/update-workspace.dto.ts b/apps/server/src/core/workspace/dto/update-workspace.dto.ts index 9b822f50..d3f029d4 100644 --- a/apps/server/src/core/workspace/dto/update-workspace.dto.ts +++ b/apps/server/src/core/workspace/dto/update-workspace.dto.ts @@ -46,6 +46,10 @@ export class UpdateWorkspaceDto extends PartialType(CreateWorkspaceDto) { @IsBoolean() mcpEnabled: boolean; + @IsOptional() + @IsBoolean() + aiChat: boolean; + @IsOptional() @IsInt() @Min(1) diff --git a/apps/server/src/core/workspace/services/workspace.service.ts b/apps/server/src/core/workspace/services/workspace.service.ts index d0bd27c6..c6d822ff 100644 --- a/apps/server/src/core/workspace/services/workspace.service.ts +++ b/apps/server/src/core/workspace/services/workspace.service.ts @@ -142,7 +142,7 @@ export class WorkspaceService { status = WorkspaceStatus.Active; plan = 'standard'; billingEmail = user.email; - settings = { ai: { generative: true } }; + settings = { ai: { generative: true, chat: true } }; } // create workspace @@ -458,11 +458,26 @@ export class WorkspaceService { ); } + if (typeof updateWorkspaceDto.aiChat !== 'undefined') { + const prev = settingsBefore?.ai?.chat ?? false; + if (prev !== updateWorkspaceDto.aiChat) { + before.aiChat = prev; + after.aiChat = updateWorkspaceDto.aiChat; + } + await this.workspaceRepo.updateAiSettings( + workspaceId, + 'chat', + updateWorkspaceDto.aiChat, + trx, + ); + } + delete updateWorkspaceDto.restrictApiToAdmins; delete updateWorkspaceDto.aiSearch; delete updateWorkspaceDto.generativeAi; delete updateWorkspaceDto.disablePublicSharing; delete updateWorkspaceDto.mcpEnabled; + delete updateWorkspaceDto.aiChat; await this.workspaceRepo.updateWorkspace( updateWorkspaceDto, diff --git a/apps/server/src/database/migrations/20260409T132415-ai-chat.ts b/apps/server/src/database/migrations/20260409T132415-ai-chat.ts new file mode 100644 index 00000000..28b595f1 --- /dev/null +++ b/apps/server/src/database/migrations/20260409T132415-ai-chat.ts @@ -0,0 +1,118 @@ +import { type Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await db.schema + .createTable('ai_chats') + .ifNotExists() + .addColumn('id', 'uuid', (col) => + col.primaryKey().defaultTo(sql`gen_uuid_v7()`), + ) + .addColumn('workspace_id', 'uuid', (col) => + col.references('workspaces.id').onDelete('cascade').notNull(), + ) + .addColumn('creator_id', 'uuid', (col) => + col.references('users.id').notNull(), + ) + .addColumn('title', 'varchar', (col) => col) + .addColumn('created_at', 'timestamptz', (col) => + col.notNull().defaultTo(sql`now()`), + ) + .addColumn('updated_at', 'timestamptz', (col) => + col.notNull().defaultTo(sql`now()`), + ) + .addColumn('deleted_at', 'timestamptz', (col) => col) + .execute(); + + await db.schema + .createIndex('idx_ai_chats_workspace_creator') + .ifNotExists() + .on('ai_chats') + .columns(['workspace_id', 'creator_id', 'id']) + .execute(); + + await db.schema + .createTable('ai_chat_messages') + .ifNotExists() + .addColumn('id', 'uuid', (col) => + col.primaryKey().defaultTo(sql`gen_uuid_v7()`), + ) + .addColumn('chat_id', 'uuid', (col) => + col.references('ai_chats.id').onDelete('cascade').notNull(), + ) + .addColumn('workspace_id', 'uuid', (col) => + col.references('workspaces.id').onDelete('cascade').notNull(), + ) + .addColumn('user_id', 'uuid', (col) => + col.references('users.id').onDelete('set null'), + ) + .addColumn('role', 'varchar', (col) => col.notNull()) + .addColumn('content', 'text', (col) => col) + .addColumn('tool_calls', 'jsonb', (col) => col) + .addColumn('metadata', 'jsonb', (col) => col) + .addColumn('tsv', sql`tsvector`, (col) => col) + .addColumn('created_at', 'timestamptz', (col) => + col.notNull().defaultTo(sql`now()`), + ) + .addColumn('updated_at', 'timestamptz', (col) => + col.notNull().defaultTo(sql`now()`), + ) + .addColumn('deleted_at', 'timestamptz', (col) => col) + .execute(); + + await db.schema + .createIndex('idx_ai_chat_messages_chat_id') + .ifNotExists() + .on('ai_chat_messages') + .columns(['chat_id', 'id']) + .execute(); + + await db.schema + .createIndex('idx_ai_chat_messages_tsv') + .ifNotExists() + .on('ai_chat_messages') + .using('GIN') + .column('tsv') + .execute(); + + //ts-vector + await sql` + CREATE OR REPLACE FUNCTION ai_chat_messages_tsvector_trigger() RETURNS trigger AS $$ + BEGIN + NEW.tsv := to_tsvector('english', f_unaccent(substring(coalesce(NEW.content, ''), 1, 100000))); + RETURN NEW; + END; + $$ LANGUAGE plpgsql; + `.execute(db); + + await sql` + CREATE OR REPLACE TRIGGER ai_chat_messages_tsvector_update + BEFORE INSERT OR UPDATE ON ai_chat_messages + FOR EACH ROW EXECUTE FUNCTION ai_chat_messages_tsvector_trigger(); + `.execute(db); + + await db.schema + .alterTable('attachments') + .addColumn('ai_chat_id', 'uuid', (col) => col) + .execute(); + + await db.schema + .createIndex('idx_attachments_ai_chat_id') + .ifNotExists() + .on('attachments') + .column('ai_chat_id') + .execute(); +} + +export async function down(db: Kysely): Promise { + await db.schema.dropIndex('idx_attachments_ai_chat_id').execute(); + await db.schema.alterTable('attachments').dropColumn('ai_chat_id').execute(); + + await sql`DROP TRIGGER IF EXISTS ai_chat_messages_tsvector_update ON ai_chat_messages`.execute( + db, + ); + await sql`DROP FUNCTION IF EXISTS ai_chat_messages_tsvector_trigger`.execute( + db, + ); + await db.schema.dropTable('ai_chat_messages').execute(); + await db.schema.dropTable('ai_chats').execute(); +} diff --git a/apps/server/src/database/repos/attachment/attachment.repo.ts b/apps/server/src/database/repos/attachment/attachment.repo.ts index 5824ce5f..bf2b5ecb 100644 --- a/apps/server/src/database/repos/attachment/attachment.repo.ts +++ b/apps/server/src/database/repos/attachment/attachment.repo.ts @@ -7,6 +7,7 @@ import { InsertableAttachment, UpdatableAttachment, } from '@docmost/db/types/entity.types'; +import { AttachmentType } from '../../../core/attachment/attachment.constants'; @Injectable() export class AttachmentRepo { @@ -23,6 +24,7 @@ export class AttachmentRepo { 'creatorId', 'pageId', 'spaceId', + 'aiChatId', 'workspaceId', 'createdAt', 'updatedAt', @@ -44,6 +46,21 @@ export class AttachmentRepo { .executeTakeFirst(); } + async findByIdWithContent( + attachmentId: string, + opts?: { + trx?: KyselyTransaction; + }, + ): Promise { + const db = dbOrTx(this.db, opts?.trx); + + return db + .selectFrom('attachments') + .select([...this.baseFields, 'textContent']) + .where('id', '=', attachmentId) + .executeTakeFirst(); + } + async insertAttachment( insertableAttachment: InsertableAttachment, trx?: KyselyTransaction, @@ -72,6 +89,21 @@ export class AttachmentRepo { .execute(); } + async findByAiChatId( + aiChatId: string, + opts?: { + trx?: KyselyTransaction; + }, + ): Promise { + const db = dbOrTx(this.db, opts?.trx); + + return db + .selectFrom('attachments') + .select(this.baseFields) + .where('aiChatId', '=', aiChatId) + .execute(); + } + updateAttachmentsByPageId( updatableAttachment: UpdatableAttachment, pageIds: string[], @@ -97,6 +129,25 @@ export class AttachmentRepo { .executeTakeFirst(); } + async claimAttachmentsForChat( + attachmentIds: string[], + aiChatId: string, + creatorId: string, + workspaceId: string, + ): Promise { + if (attachmentIds.length === 0) return; + + await this.db + .updateTable('attachments') + .set({ aiChatId }) + .where('id', 'in', attachmentIds) + .where('creatorId', '=', creatorId) + .where('workspaceId', '=', workspaceId) + .where('type', '=', AttachmentType.Chat) + .where('aiChatId', 'is', null) + .execute(); + } + async deleteAttachmentById(attachmentId: string): Promise { await this.db .deleteFrom('attachments') diff --git a/apps/server/src/database/types/db.d.ts b/apps/server/src/database/types/db.d.ts index fe4d5a13..3f4081e1 100644 --- a/apps/server/src/database/types/db.d.ts +++ b/apps/server/src/database/types/db.d.ts @@ -43,6 +43,7 @@ export interface ApiKeys { } export interface Attachments { + aiChatId: string | null; createdAt: Generated; creatorId: string; deletedAt: Timestamp | null; @@ -429,6 +430,31 @@ export interface PagePermissions { updatedAt: Generated; } +export interface AiChats { + id: Generated; + workspaceId: string; + creatorId: string; + title: string | null; + createdAt: Generated; + updatedAt: Generated; + deletedAt: Timestamp | null; +} + +export interface AiChatMessages { + id: Generated; + chatId: string; + workspaceId: string; + userId: string | null; + role: string; + content: string | null; + toolCalls: Json | null; + metadata: Json | null; + tsv: string | null; + createdAt: Generated; + updatedAt: Generated; + deletedAt: Timestamp | null; +} + export interface UserSessions { id: Generated; userId: string; @@ -445,6 +471,8 @@ export interface UserSessions { } export interface DB { + aiChats: AiChats; + aiChatMessages: AiChatMessages; apiKeys: ApiKeys; attachments: Attachments; audit: Audit; diff --git a/apps/server/src/database/types/entity.types.ts b/apps/server/src/database/types/entity.types.ts index d23e7475..8d4f482a 100644 --- a/apps/server/src/database/types/entity.types.ts +++ b/apps/server/src/database/types/entity.types.ts @@ -1,5 +1,7 @@ import { Insertable, Selectable, Updateable } from 'kysely'; import { + AiChats, + AiChatMessages, Attachments, Comments, Groups, @@ -29,6 +31,21 @@ import { } from './db'; import { PageEmbeddings } from '@docmost/db/types/embeddings.types'; +// AI Chat +export type AiChat = Selectable; +export type InsertableAiChat = Insertable; +export type UpdatableAiChat = Updateable>; + +// AI Chat Message +// `tsv` is an internal tsvector column maintained by a trigger for +// full-text search. It is omitted from the public type so it never leaks +// into HTTP responses or the chat history fed to the language model. +export type AiChatMessage = Omit, 'tsv'>; +export type InsertableAiChatMessage = Omit< + Insertable, + 'tsv' +>; + // Workspace export type Workspace = Selectable; export type InsertableWorkspace = Insertable; diff --git a/apps/server/src/ee b/apps/server/src/ee index dc7ae0e3..d3bc4c51 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit dc7ae0e3b066df842248b2295ccfad190a0c5a93 +Subproject commit d3bc4c5160fec9c4fabf769180f0ff00ef12042b diff --git a/apps/server/src/integrations/environment/environment.service.ts b/apps/server/src/integrations/environment/environment.service.ts index b1b27d40..ceb2eae7 100644 --- a/apps/server/src/integrations/environment/environment.service.ts +++ b/apps/server/src/integrations/environment/environment.service.ts @@ -252,6 +252,13 @@ export class EnvironmentService { return this.configService.get('AI_COMPLETION_MODEL'); } + getAiChatModel(): string { + return ( + this.configService.get('AI_CHAT_MODEL') || + this.configService.get('AI_COMPLETION_MODEL') + ); + } + getAiEmbeddingDimension(): number { return parseInt( this.configService.get('AI_EMBEDDING_DIMENSION'), diff --git a/apps/server/src/integrations/export/export.service.ts b/apps/server/src/integrations/export/export.service.ts index d93f9ba0..3f8da1bf 100644 --- a/apps/server/src/integrations/export/export.service.ts +++ b/apps/server/src/integrations/export/export.service.ts @@ -353,7 +353,7 @@ export class ExportService { if (attachmentIds.length > 0) { const attachments = await this.db .selectFrom('attachments') - .selectAll() + .select(['id', 'fileName', 'filePath']) .where('id', 'in', attachmentIds) .where('spaceId', '=', spaceId) .execute(); diff --git a/apps/server/src/integrations/queue/constants/queue.constants.ts b/apps/server/src/integrations/queue/constants/queue.constants.ts index 92d15426..6d92f09c 100644 --- a/apps/server/src/integrations/queue/constants/queue.constants.ts +++ b/apps/server/src/integrations/queue/constants/queue.constants.ts @@ -17,6 +17,7 @@ export enum QueueJob { ATTACHMENT_INDEX_CONTENT = 'attachment-index-content', ATTACHMENT_INDEXING = 'attachment-indexing', DELETE_PAGE_ATTACHMENTS = 'delete-page-attachments', + DELETE_AI_CHAT_ATTACHMENTS = 'delete-ai-chat-attachments', DELETE_USER_AVATARS = 'delete-user-avatars', diff --git a/apps/server/src/integrations/throttle/throttle.module.ts b/apps/server/src/integrations/throttle/throttle.module.ts index 8f080e1d..42dd0ec4 100644 --- a/apps/server/src/integrations/throttle/throttle.module.ts +++ b/apps/server/src/integrations/throttle/throttle.module.ts @@ -4,6 +4,7 @@ import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis' import { EnvironmentService } from '../environment/environment.service'; import { EnvironmentModule } from '../environment/environment.module'; import { parseRedisUrl } from '../../common/helpers'; +import { AUTH_THROTTLER, AI_CHAT_THROTTLER } from './throttler-names'; import Redis from 'ioredis'; @Module({ @@ -14,7 +15,10 @@ import Redis from 'ioredis'; const redisConfig = parseRedisUrl(environmentService.getRedisUrl()); return { - throttlers: [{ name: 'auth', ttl: 60_000, limit: 10 }], + throttlers: [ + { name: AUTH_THROTTLER, ttl: 60_000, limit: 10 }, + { name: AI_CHAT_THROTTLER, ttl: 60_000, limit: 25 }, + ], errorMessage: 'Too many requests', storage: new ThrottlerStorageRedisService( new Redis({ diff --git a/apps/server/src/integrations/throttle/throttler-names.ts b/apps/server/src/integrations/throttle/throttler-names.ts new file mode 100644 index 00000000..388ba29d --- /dev/null +++ b/apps/server/src/integrations/throttle/throttler-names.ts @@ -0,0 +1,2 @@ +export const AUTH_THROTTLER = 'auth'; +export const AI_CHAT_THROTTLER = 'ai-chat'; diff --git a/apps/server/src/integrations/throttle/user-throttler.guard.ts b/apps/server/src/integrations/throttle/user-throttler.guard.ts new file mode 100644 index 00000000..35744c09 --- /dev/null +++ b/apps/server/src/integrations/throttle/user-throttler.guard.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common'; +import { ThrottlerGuard } from '@nestjs/throttler'; + +type AuthedRequest = { user?: { id?: string } }; + +@Injectable() +export class UserThrottlerGuard extends ThrottlerGuard { + protected async getTracker(req: AuthedRequest): Promise { + const userId = req.user?.id; + if (userId) return `user:${userId}`; + return super.getTracker(req as Parameters[0]); + } +} diff --git a/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts b/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts index 04dc1978..7556aa4f 100644 --- a/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts +++ b/packages/editor-ext/src/lib/markdown/utils/marked.utils.ts @@ -37,6 +37,8 @@ marked.use({ extensions: [calloutExtension, mathBlockExtension, mathInlineExtension], }); +marked.setOptions({ breaks: true }); + export function markdownToHtml( markdownInput: string, ): string | Promise { @@ -46,8 +48,5 @@ export function markdownToHtml( .replace(YAML_FONT_MATTER_REGEX, "") .trimStart(); - return marked - .options({ breaks: true }) - .parse(markdown) - .toString(); + return marked.parse(markdown).toString(); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff37ec78..cfdfcdcb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -589,6 +589,9 @@ importers: cookie: specifier: ^1.1.1 version: 1.1.1 + fast-bm25: + specifier: 0.0.5 + version: 0.0.5(typescript@5.9.3) fastify-ip: specifier: ^2.0.0 version: 2.0.0 @@ -601,6 +604,9 @@ importers: ioredis: specifier: ^5.10.1 version: 5.10.1 + js-tiktoken: + specifier: ^1.0.21 + version: 1.0.21 jsonwebtoken: specifier: ^9.0.3 version: 9.0.3 @@ -6874,6 +6880,12 @@ packages: exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + fast-bm25@0.0.5: + resolution: {integrity: sha512-6HTiLmPkgeqcPJHccN0pXdqnA7OzhaEQZTFzWnfjIyPoX5sGVKUUpfRc2K2o6zMwK+g009miRhADYn/f2Ax0Mg==} + engines: {node: '>=14.0.0'} + peerDependencies: + typescript: ^5.6.3 + fast-copy@4.0.2: resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} @@ -8966,6 +8978,9 @@ packages: points-on-path@0.2.1: resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + porter2@1.1.0: + resolution: {integrity: sha512-Io2cLEdZn0O1dH60pRsjmr/cH/qJJ/j6Cjubz8wQWi0b6vPdQIUxSBQKyx9d+8CN7fSnY+5uOU3rErMFjNqcLw==} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -17795,6 +17810,11 @@ snapshots: exsolve@1.0.7: {} + fast-bm25@0.0.5(typescript@5.9.3): + dependencies: + porter2: 1.1.0 + typescript: 5.9.3 + fast-copy@4.0.2: {} fast-decode-uri-component@1.0.1: {} @@ -20144,6 +20164,8 @@ snapshots: path-data-parser: 0.1.0 points-on-curve: 0.2.0 + porter2@1.1.0: {} + possible-typed-array-names@1.0.0: {} postcss-js@4.0.1(postcss@8.5.8):