feat: enhancements (#2107)

* refactor
* fix
* update packages
This commit is contained in:
Philip Okugbe
2026-04-13 23:34:40 +01:00
committed by GitHub
parent bd68e47e03
commit 4056bd0104
18 changed files with 412 additions and 155 deletions
+1 -1
View File
@@ -25,7 +25,7 @@
"@tabler/icons-react": "^3.40.0", "@tabler/icons-react": "^3.40.0",
"@tanstack/react-query": "5.90.17", "@tanstack/react-query": "5.90.17",
"alfaaz": "^1.1.0", "alfaaz": "^1.1.0",
"axios": "1.13.6", "axios": "1.15.0",
"blueimp-load-image": "^5.16.0", "blueimp-load-image": "^5.16.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"emoji-mart": "^5.6.0", "emoji-mart": "^5.6.0",
@@ -55,7 +55,7 @@ export default function AiChatLayout() {
navigate(location.pathname, { replace: true, state: null }); navigate(location.pathname, { replace: true, state: null });
}, [chatId, location, navigate, sendMessage]); }, [chatId, location, navigate, sendMessage]);
const hasMessages = messages.length > 0 || isStreaming; const hasMessages = messages.length > 0 || isStreaming || !!chatId;
// While the redirect effect is running (or if the user is still on this // 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. // component for any reason) never render the chat UI for a forbidden chat.
@@ -65,6 +65,14 @@ export default function AiChatLayout() {
return ( return (
<div className={classes.main}> <div className={classes.main}>
{hasMessages ? (
<>
<ChatMessageList
messages={messages}
isStreaming={isStreaming}
streamingContent={streamingContent}
streamingToolCalls={streamingToolCalls}
/>
{error && ( {error && (
<div <div
style={{ style={{
@@ -76,15 +84,6 @@ export default function AiChatLayout() {
{error} {error}
</div> </div>
)} )}
{hasMessages ? (
<>
<ChatMessageList
messages={messages}
isStreaming={isStreaming}
streamingContent={streamingContent}
streamingToolCalls={streamingToolCalls}
/>
<div className={classes.inputArea}> <div className={classes.inputArea}>
<ChatInput <ChatInput
isStreaming={isStreaming} isStreaming={isStreaming}
@@ -1,3 +1,4 @@
import { useMemo } from "react";
import { import {
useQuery, useQuery,
useInfiniteQuery, useInfiniteQuery,
@@ -8,10 +9,10 @@ import {
addFavorite, addFavorite,
removeFavorite, removeFavorite,
getFavorites, getFavorites,
getFavoriteIds,
ToggleFavoriteParams, ToggleFavoriteParams,
} from "../services/favorite-service"; } from "../services/favorite-service";
import { IPagination } from "@/lib/types.ts"; import { FavoriteType } from "../types/favorite.types";
import { IFavorite, FavoriteType } from "../types/favorite.types";
export function useFavoritesQuery(type?: FavoriteType) { export function useFavoritesQuery(type?: FavoriteType) {
return useInfiniteQuery({ return useInfiniteQuery({
@@ -26,23 +27,21 @@ export function useFavoritesQuery(type?: FavoriteType) {
} }
export function useFavoriteIds(type: FavoriteType): Set<string> { export function useFavoriteIds(type: FavoriteType): Set<string> {
const { data } = useQuery<IPagination<IFavorite>>({ const { data } = useQuery({
queryKey: ["favorite-ids", type], queryKey: ["favorite-ids", type],
queryFn: () => getFavorites({ type, limit: 50 }), queryFn: () => getFavoriteIds(type),
refetchOnMount: true, refetchOnMount: true,
}); });
const ids = new Set<string>(); const items = data?.items;
if (data?.items) { return useMemo(() => new Set(items ?? []), [items]);
for (const fav of data.items) {
let id: string | undefined;
if (type === "page") id = fav.pageId;
else if (type === "space") id = fav.spaceId;
else if (type === "template") id = fav.templateId;
if (id) ids.add(id);
} }
}
return ids; function getEntityId(variables: ToggleFavoriteParams): string | undefined {
if (variables.type === "page") return variables.pageId;
if (variables.type === "space") return variables.spaceId;
if (variables.type === "template") return variables.templateId;
return undefined;
} }
export function useAddFavoriteMutation() { export function useAddFavoriteMutation() {
@@ -51,9 +50,17 @@ export function useAddFavoriteMutation() {
return useMutation<void, Error, ToggleFavoriteParams>({ return useMutation<void, Error, ToggleFavoriteParams>({
mutationFn: (data) => addFavorite(data), mutationFn: (data) => addFavorite(data),
onSuccess: (_result, variables) => { onSuccess: (_result, variables) => {
queryClient.invalidateQueries({ const entityId = getEntityId(variables);
queryKey: ["favorite-ids", variables.type], if (entityId) {
}); queryClient.setQueryData(
["favorite-ids", variables.type],
(old: { items: string[]; meta: any } | undefined) => {
if (!old) return old;
if (old.items.includes(entityId)) return old;
return { ...old, items: [...old.items, entityId] };
},
);
}
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["favorites", variables.type], queryKey: ["favorites", variables.type],
}); });
@@ -67,9 +74,16 @@ export function useRemoveFavoriteMutation() {
return useMutation<void, Error, ToggleFavoriteParams>({ return useMutation<void, Error, ToggleFavoriteParams>({
mutationFn: (data) => removeFavorite(data), mutationFn: (data) => removeFavorite(data),
onSuccess: (_result, variables) => { onSuccess: (_result, variables) => {
queryClient.invalidateQueries({ const entityId = getEntityId(variables);
queryKey: ["favorite-ids", variables.type], if (entityId) {
}); queryClient.setQueryData(
["favorite-ids", variables.type],
(old: { items: string[]; meta: any } | undefined) => {
if (!old) return old;
return { ...old, items: old.items.filter((id) => id !== entityId) };
},
);
}
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["favorites", variables.type], queryKey: ["favorites", variables.type],
}); });
@@ -21,6 +21,11 @@ export async function removeFavorite(
await api.post("/favorites/remove", params); await api.post("/favorites/remove", params);
} }
export async function getFavoriteIds(type: FavoriteType): Promise<IPagination<string>> {
const req = await api.post<IPagination<string>>("/favorites/ids", { type });
return req.data;
}
export async function getFavorites(params?: { export async function getFavorites(params?: {
type?: FavoriteType; type?: FavoriteType;
limit?: number; limit?: number;
@@ -16,6 +16,8 @@ import {
IconPlus, IconPlus,
IconSearch, IconSearch,
IconSettings, IconSettings,
IconStar,
IconStarFilled,
IconTrash, IconTrash,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import { import {
@@ -43,6 +45,11 @@ import PageImportModal from "@/features/page/components/page-import-modal.tsx";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { SwitchSpace } from "./switch-space"; import { SwitchSpace } from "./switch-space";
import ExportModal from "@/components/common/export-modal"; import ExportModal from "@/components/common/export-modal";
import {
useFavoriteIds,
useAddFavoriteMutation,
useRemoveFavoriteMutation,
} from "@/features/favorite/queries/favorite-query";
import { mobileSidebarAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts"; import { mobileSidebarAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts";
import { useToggleSidebar } from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar.ts"; import { useToggleSidebar } from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar.ts";
import { searchSpotlight } from "@/features/search/constants"; import { searchSpotlight } from "@/features/search/constants";
@@ -56,7 +63,6 @@ export function SpaceSidebar() {
const [mobileSidebarOpened] = useAtom(mobileSidebarAtom); const [mobileSidebarOpened] = useAtom(mobileSidebarAtom);
const toggleMobileSidebar = useToggleSidebar(mobileSidebarAtom); const toggleMobileSidebar = useToggleSidebar(mobileSidebarAtom);
const { spaceSlug } = useParams(); const { spaceSlug } = useParams();
const { data: space } = useGetSpaceBySlugQuery(spaceSlug); const { data: space } = useGetSpaceBySlugQuery(spaceSlug);
@@ -82,7 +88,12 @@ export function SpaceSidebar() {
marginBottom: 3, marginBottom: 3,
}} }}
> >
<Group gap={4} wrap="nowrap" justify="space-between" style={{ width: "100%" }}> <Group
gap={4}
wrap="nowrap"
justify="space-between"
style={{ width: "100%" }}
>
<SwitchSpace <SwitchSpace
spaceName={space?.name} spaceName={space?.name}
spaceSlug={space?.slug} spaceSlug={space?.slug}
@@ -241,6 +252,20 @@ function SpaceMenu({
const unwatchMutation = useUnwatchSpaceMutation(); const unwatchMutation = useUnwatchSpaceMutation();
const isWatching = watchStatus?.watching ?? false; const isWatching = watchStatus?.watching ?? false;
const favoriteIds = useFavoriteIds("space");
const addFavoriteMutation = useAddFavoriteMutation();
const removeFavoriteMutation = useRemoveFavoriteMutation();
const isFavorited = favoriteIds.has(spaceId);
const handleToggleFavorite = () => {
const params = { type: "space" as const, spaceId };
if (isFavorited) {
removeFavoriteMutation.mutate(params);
} else {
addFavoriteMutation.mutate(params);
}
};
const handleToggleWatch = () => { const handleToggleWatch = () => {
if (isWatching) { if (isWatching) {
unwatchMutation.mutate(spaceId); unwatchMutation.mutate(spaceId);
@@ -265,6 +290,22 @@ function SpaceMenu({
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <Menu.Dropdown>
<Menu.Item
onClick={handleToggleFavorite}
leftSection={
isFavorited ? (
<IconStarFilled
size={16}
color="var(--mantine-color-yellow-filled)"
/>
) : (
<IconStar size={16} />
)
}
>
{isFavorited ? t("Remove from favorites") : t("Add to favorites")}
</Menu.Item>
<Menu.Item <Menu.Item
onClick={handleToggleWatch} onClick={handleToggleWatch}
leftSection={ leftSection={
@@ -7,9 +7,15 @@ import {
Space, Space,
Menu, Menu,
Anchor, Anchor,
Tooltip,
} from "@mantine/core"; } from "@mantine/core";
import { IconDots, IconSettings } from "@tabler/icons-react"; import { IconDots, IconSettings, IconEye, IconEyeOff } from "@tabler/icons-react";
import StarButton from "@/features/favorite/components/star-button"; import StarButton from "@/features/favorite/components/star-button";
import {
useWatchedSpaceIds,
useWatchSpaceMutation,
useUnwatchSpaceMutation,
} from "@/features/space/queries/space-watcher-query";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -26,6 +32,45 @@ import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts"; import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts";
import { AutoTooltipText } from "@/components/ui/auto-tooltip-text.tsx"; import { AutoTooltipText } from "@/components/ui/auto-tooltip-text.tsx";
function WatchButton({ spaceId, watchedIds, size = 16 }: { spaceId: string; watchedIds: Set<string>; size?: number }) {
const { t } = useTranslation();
const watchMutation = useWatchSpaceMutation();
const unwatchMutation = useUnwatchSpaceMutation();
const isWatching = watchedIds.has(spaceId);
const isPending = watchMutation.isPending || unwatchMutation.isPending;
const handleToggle = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
if (isWatching) {
unwatchMutation.mutate(spaceId);
} else {
watchMutation.mutate(spaceId);
}
};
return (
<Tooltip
label={isWatching ? t("Stop watching space") : t("Watch space")}
openDelay={250}
withArrow
>
<ActionIcon
variant="subtle"
color={isWatching ? "blue" : "gray"}
onClick={handleToggle}
loading={isPending}
>
{isWatching ? (
<IconEyeOff size={size} stroke={2} />
) : (
<IconEye size={size} stroke={2} />
)}
</ActionIcon>
</Tooltip>
);
}
interface AllSpacesListProps { interface AllSpacesListProps {
spaces: any[]; spaces: any[];
onSearch: (query: string) => void; onSearch: (query: string) => void;
@@ -44,6 +89,7 @@ export default function AllSpacesList({
onPrev, onPrev,
}: AllSpacesListProps) { }: AllSpacesListProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const watchedIds = useWatchedSpaceIds();
const [settingsOpened, { open: openSettings, close: closeSettings }] = const [settingsOpened, { open: openSettings, close: closeSettings }] =
useDisclosure(false); useDisclosure(false);
const [selectedSpaceId, setSelectedSpaceId] = useState<string | null>(null); const [selectedSpaceId, setSelectedSpaceId] = useState<string | null>(null);
@@ -65,7 +111,7 @@ export default function AllSpacesList({
<Table.Tr> <Table.Tr>
<Table.Th>{t("Space")}</Table.Th> <Table.Th>{t("Space")}</Table.Th>
<Table.Th>{t("Members")}</Table.Th> <Table.Th>{t("Members")}</Table.Th>
<Table.Th w={100}></Table.Th> <Table.Th w={130}></Table.Th>
</Table.Tr> </Table.Tr>
</Table.Thead> </Table.Thead>
@@ -117,8 +163,9 @@ export default function AllSpacesList({
</Text> </Text>
</Table.Td> </Table.Td>
<Table.Td> <Table.Td>
<Group gap="xs" justify="flex-end"> <Group gap="xs" justify="flex-end" wrap="nowrap">
<StarButton type="space" spaceId={space.id} size={16} /> <StarButton type="space" spaceId={space.id} size={16} />
<WatchButton spaceId={space.id} watchedIds={watchedIds} size={16} />
<Menu position="bottom-end"> <Menu position="bottom-end">
<Menu.Target> <Menu.Target>
<ActionIcon variant="subtle" color="gray"> <ActionIcon variant="subtle" color="gray">
@@ -1,13 +1,27 @@
import { useMemo } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { import {
watchSpace, watchSpace,
unwatchSpace, unwatchSpace,
getSpaceWatchStatus, getSpaceWatchStatus,
getWatchedSpaceIds,
} from "@/features/space/services/space-watcher-service"; } from "@/features/space/services/space-watcher-service";
import { notifications } from "@mantine/notifications"; import { notifications } from "@mantine/notifications";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const SPACE_WATCHER_KEY = "space-watcher"; const SPACE_WATCHER_KEY = "space-watcher";
const WATCHED_SPACE_IDS_KEY = "watched-space-ids";
export function useWatchedSpaceIds(): Set<string> {
const { data } = useQuery({
queryKey: [WATCHED_SPACE_IDS_KEY],
queryFn: () => getWatchedSpaceIds(),
refetchOnMount: true,
});
const items = data?.items;
return useMemo(() => new Set(items ?? []), [items]);
}
export function useSpaceWatchStatusQuery(spaceId: string) { export function useSpaceWatchStatusQuery(spaceId: string) {
return useQuery({ return useQuery({
@@ -27,6 +41,14 @@ export function useWatchSpaceMutation() {
queryClient.setQueryData([SPACE_WATCHER_KEY, spaceId], { queryClient.setQueryData([SPACE_WATCHER_KEY, spaceId], {
watching: true, watching: true,
}); });
queryClient.setQueryData(
[WATCHED_SPACE_IDS_KEY],
(old: { items: string[]; meta: any } | undefined) => {
if (!old) return old;
if (old.items.includes(spaceId)) return old;
return { ...old, items: [...old.items, spaceId] };
},
);
notifications.show({ message: t("You are now watching this space") }); notifications.show({ message: t("You are now watching this space") });
}, },
}); });
@@ -41,6 +63,13 @@ export function useUnwatchSpaceMutation() {
queryClient.setQueryData([SPACE_WATCHER_KEY, spaceId], { queryClient.setQueryData([SPACE_WATCHER_KEY, spaceId], {
watching: false, watching: false,
}); });
queryClient.setQueryData(
[WATCHED_SPACE_IDS_KEY],
(old: { items: string[]; meta: any } | undefined) => {
if (!old) return old;
return { ...old, items: old.items.filter((id) => id !== spaceId) };
},
);
notifications.show({ notifications.show({
message: t("You are no longer watching this space"), message: t("You are no longer watching this space"),
}); });
@@ -1,4 +1,5 @@
import api from "@/lib/api-client"; import api from "@/lib/api-client";
import { IPagination } from "@/lib/types";
export async function watchSpace( export async function watchSpace(
spaceId: string, spaceId: string,
@@ -18,6 +19,11 @@ export async function unwatchSpace(
return req.data; return req.data;
} }
export async function getWatchedSpaceIds(): Promise<IPagination<string>> {
const req = await api.post<IPagination<string>>("/spaces/watched-ids");
return req.data;
}
export async function getSpaceWatchStatus( export async function getSpaceWatchStatus(
spaceId: string, spaceId: string,
): Promise<{ watching: boolean }> { ): Promise<{ watching: boolean }> {
+5 -5
View File
@@ -41,9 +41,9 @@
"@fastify/multipart": "^9.4.0", "@fastify/multipart": "^9.4.0",
"@fastify/static": "^9.0.0", "@fastify/static": "^9.0.0",
"@keyv/redis": "^5.1.6", "@keyv/redis": "^5.1.6",
"@langchain/core": "1.1.34", "@langchain/core": "1.1.39",
"@langchain/textsplitters": "1.0.1", "@langchain/textsplitters": "1.0.1",
"@modelcontextprotocol/sdk": "^1.27.1", "@modelcontextprotocol/sdk": "^1.29.0",
"@nest-lab/throttler-storage-redis": "^1.2.0", "@nest-lab/throttler-storage-redis": "^1.2.0",
"@nestjs-labs/nestjs-ioredis": "^11.0.4", "@nestjs-labs/nestjs-ioredis": "^11.0.4",
"@nestjs/bullmq": "^11.0.4", "@nestjs/bullmq": "^11.0.4",
@@ -94,7 +94,7 @@
"nestjs-cls": "^6.2.0", "nestjs-cls": "^6.2.0",
"nestjs-kysely": "^3.1.2", "nestjs-kysely": "^3.1.2",
"nestjs-pino": "^4.6.1", "nestjs-pino": "^4.6.1",
"nodemailer": "^8.0.4", "nodemailer": "^8.0.5",
"openid-client": "^6.8.2", "openid-client": "^6.8.2",
"otpauth": "^9.5.0", "otpauth": "^9.5.0",
"p-limit": "^7.3.0", "p-limit": "^7.3.0",
@@ -116,8 +116,8 @@
"tlds": "^1.261.0", "tlds": "^1.261.0",
"tmp-promise": "^3.0.3", "tmp-promise": "^3.0.3",
"tseep": "^1.3.1", "tseep": "^1.3.1",
"typesense": "^3.0.3", "typesense": "^3.0.5",
"ws": "^8.19.0", "ws": "^8.20.0",
"yauzl": "^3.2.1", "yauzl": "^3.2.1",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },
@@ -0,0 +1,8 @@
import { IsIn, IsNotEmpty, IsString } from 'class-validator';
export class FavoriteIdsDto {
@IsString()
@IsNotEmpty()
@IsIn(['page', 'space', 'template'])
type: 'page' | 'space' | 'template';
}
@@ -11,6 +11,7 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { FavoriteService } from './services/favorite.service'; import { FavoriteService } from './services/favorite.service';
import { AddFavoriteDto, RemoveFavoriteDto } from './dto/favorite.dto'; import { AddFavoriteDto, RemoveFavoriteDto } from './dto/favorite.dto';
import { FavoriteIdsDto } from './dto/favorite-ids.dto';
import { ListFavoritesDto } from './dto/list-favorites.dto'; import { ListFavoritesDto } from './dto/list-favorites.dto';
import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
@@ -70,6 +71,20 @@ export class FavoriteController {
}); });
} }
@HttpCode(HttpStatus.OK)
@Post('ids')
async getFavoriteIds(
@Body() dto: FavoriteIdsDto,
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
) {
return this.favoriteService.getFavoriteIds(
user.id,
workspace.id,
dto.type as FavoriteType,
);
}
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Post() @Post()
async getUserFavorites( async getUserFavorites(
@@ -16,6 +16,40 @@ export class FavoriteService {
private readonly spaceMemberRepo: SpaceMemberRepo, private readonly spaceMemberRepo: SpaceMemberRepo,
) {} ) {}
async getFavoriteIds(
userId: string,
workspaceId: string,
type: FavoriteType,
) {
const result = await this.favoriteRepo.getFavoriteIds(
userId,
workspaceId,
type,
);
if (result.items.length === 0) {
return result;
}
if (type === FavoriteType.PAGE) {
const accessibleIds =
await this.pagePermissionRepo.filterAccessiblePageIds({
pageIds: result.items,
userId,
});
const accessibleSet = new Set(accessibleIds);
result.items = result.items.filter((id) => accessibleSet.has(id));
}
if (type === FavoriteType.SPACE) {
const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds(userId);
const spaceSet = new Set(userSpaceIds);
result.items = result.items.filter((id) => spaceSet.has(id));
}
return result;
}
async addFavorite( async addFavorite(
userId: string, userId: string,
workspaceId: string, workspaceId: string,
@@ -48,6 +48,15 @@ export class SpaceWatcherController {
return space; return space;
} }
@HttpCode(HttpStatus.OK)
@Post('watched-ids')
async getWatchedSpaceIds(
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
) {
return this.watcherService.getWatchedSpaceIds(user.id, workspace.id);
}
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Post('watch') @Post('watch')
async watchSpace( async watchSpace(
@@ -6,10 +6,14 @@ import {
import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
import { KyselyTransaction } from '@docmost/db/types/kysely.types'; import { KyselyTransaction } from '@docmost/db/types/kysely.types';
import { InsertableWatcher } from '@docmost/db/types/entity.types'; import { InsertableWatcher } from '@docmost/db/types/entity.types';
import { SpaceMemberRepo } from '@docmost/db/repos/space/space-member.repo';
@Injectable() @Injectable()
export class WatcherService { export class WatcherService {
constructor(private readonly watcherRepo: WatcherRepo) {} constructor(
private readonly watcherRepo: WatcherRepo,
private readonly spaceMemberRepo: SpaceMemberRepo,
) {}
async watchPage( async watchPage(
userId: string, userId: string,
@@ -84,6 +88,24 @@ export class WatcherService {
return this.watcherRepo.deleteSpaceWatch(userId, spaceId); return this.watcherRepo.deleteSpaceWatch(userId, spaceId);
} }
async getWatchedSpaceIds(userId: string, workspaceId: string) {
const result = await this.watcherRepo.getWatchedSpaceIds(userId, workspaceId);
const spaceIds = result.items.map((r) => r.spaceId);
if (spaceIds.length === 0) {
return { items: spaceIds, meta: result.meta };
}
const userSpaceIds = await this.spaceMemberRepo.getUserSpaceIds(userId);
const spaceSet = new Set(userSpaceIds);
return {
items: spaceIds.filter((id) => spaceSet.has(id)),
meta: result.meta,
};
}
async isWatchingSpace(userId: string, spaceId: string): Promise<boolean> { async isWatchingSpace(userId: string, spaceId: string): Promise<boolean> {
return this.watcherRepo.isWatchingSpace(userId, spaceId); return this.watcherRepo.isWatchingSpace(userId, spaceId);
} }
@@ -62,6 +62,39 @@ export class FavoriteRepo {
.execute(); .execute();
} }
async getFavoriteIds(
userId: string,
workspaceId: string,
type: FavoriteType,
): Promise<{ items: string[]; meta: any }> {
const idColumn =
type === FavoriteType.PAGE
? 'pageId'
: type === FavoriteType.SPACE
? 'spaceId'
: 'templateId';
const query = this.db
.selectFrom('favorites')
.select(['favorites.id', `favorites.${idColumn} as entityId`])
.where('userId', '=', userId)
.where('workspaceId', '=', workspaceId)
.where('type', '=', type);
const result = await executeWithCursorPagination(query, {
perPage: 250,
fields: [{ expression: 'favorites.id', direction: 'desc' }],
parseCursor: (cursor) => ({ id: cursor.id }),
});
return {
items: result.items
.map((r) => (r as any).entityId as string)
.filter(Boolean),
meta: result.meta,
};
}
async findUserFavorites( async findUserFavorites(
userId: string, userId: string,
workspaceId: string, workspaceId: string,
@@ -207,6 +207,22 @@ export class WatcherRepo {
.execute(); .execute();
} }
async getWatchedSpaceIds(userId: string, workspaceId: string) {
const query = this.db
.selectFrom('watchers')
.select(['watchers.id', 'watchers.spaceId'])
.where('userId', '=', userId)
.where('workspaceId', '=', workspaceId)
.where('pageId', 'is', null)
.where('type', '=', WatcherType.SPACE);
return executeWithCursorPagination(query, {
perPage: 250,
fields: [{ expression: 'watchers.id', direction: 'asc' }],
parseCursor: (cursor) => ({ id: cursor.id }),
});
}
async isWatchingSpace(userId: string, spaceId: string): Promise<boolean> { async isWatchingSpace(userId: string, spaceId: string): Promise<boolean> {
const watcher = await this.db const watcher = await this.db
.selectFrom('watchers') .selectFrom('watchers')
+6 -4
View File
@@ -101,17 +101,17 @@
"prosemirror-changeset": "2.4.0", "prosemirror-changeset": "2.4.0",
"y-prosemirror": "1.3.7", "y-prosemirror": "1.3.7",
"glob": "13.0.6", "glob": "13.0.6",
"ws": "8.19.0", "ws": "8.20.0",
"dompurify": "3.3.3", "dompurify": "3.3.3",
"tmp": "0.2.5", "tmp": "0.2.5",
"hono": "4.12.8", "hono": "4.12.12",
"mermaid": "11.13.0", "mermaid": "11.13.0",
"nanoid@^3": "3.3.8", "nanoid@^3": "3.3.8",
"socket.io-parser": "4.2.6", "socket.io-parser": "4.2.6",
"serialize-javascript": "7.0.3", "serialize-javascript": "7.0.3",
"lodash-es": "4.18.1", "lodash-es": "4.18.1",
"lodash": "4.18.1", "lodash": "4.18.1",
"@hono/node-server": "1.19.10", "@hono/node-server": "1.19.13",
"undici": "7.24.0", "undici": "7.24.0",
"ajv@^6": "6.14.0", "ajv@^6": "6.14.0",
"ajv@^8": "8.18.0", "ajv@^8": "8.18.0",
@@ -129,7 +129,9 @@
"path-to-regexp@^8": "8.4.0", "path-to-regexp@^8": "8.4.0",
"brace-expansion@^5": "5.0.5", "brace-expansion@^5": "5.0.5",
"@xmldom/xmldom": "0.8.12", "@xmldom/xmldom": "0.8.12",
"handlebars": "4.7.9" "handlebars": "4.7.9",
"axios": "1.15.0",
"langsmith": "0.5.18"
}, },
"neverBuiltDependencies": [] "neverBuiltDependencies": []
} }
+82 -105
View File
@@ -8,17 +8,17 @@ overrides:
prosemirror-changeset: 2.4.0 prosemirror-changeset: 2.4.0
y-prosemirror: 1.3.7 y-prosemirror: 1.3.7
glob: 13.0.6 glob: 13.0.6
ws: 8.19.0 ws: 8.20.0
dompurify: 3.3.3 dompurify: 3.3.3
tmp: 0.2.5 tmp: 0.2.5
hono: 4.12.8 hono: 4.12.12
mermaid: 11.13.0 mermaid: 11.13.0
nanoid@^3: 3.3.8 nanoid@^3: 3.3.8
socket.io-parser: 4.2.6 socket.io-parser: 4.2.6
serialize-javascript: 7.0.3 serialize-javascript: 7.0.3
lodash-es: 4.18.1 lodash-es: 4.18.1
lodash: 4.18.1 lodash: 4.18.1
'@hono/node-server': 1.19.10 '@hono/node-server': 1.19.13
undici: 7.24.0 undici: 7.24.0
ajv@^6: 6.14.0 ajv@^6: 6.14.0
ajv@^8: 8.18.0 ajv@^8: 8.18.0
@@ -37,6 +37,8 @@ overrides:
brace-expansion@^5: 5.0.5 brace-expansion@^5: 5.0.5
'@xmldom/xmldom': 0.8.12 '@xmldom/xmldom': 0.8.12
handlebars: 4.7.9 handlebars: 4.7.9
axios: 1.15.0
langsmith: 0.5.18
patchedDependencies: patchedDependencies:
react-arborist@3.4.0: react-arborist@3.4.0:
@@ -289,8 +291,8 @@ importers:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0 version: 1.1.0
axios: axios:
specifier: 1.13.6 specifier: 1.15.0
version: 1.13.6 version: 1.15.0
blueimp-load-image: blueimp-load-image:
specifier: ^5.16.0 specifier: ^5.16.0
version: 5.16.0 version: 5.16.0
@@ -488,14 +490,14 @@ importers:
specifier: ^5.1.6 specifier: ^5.1.6
version: 5.1.6(keyv@5.6.0) version: 5.1.6(keyv@5.6.0)
'@langchain/core': '@langchain/core':
specifier: 1.1.34 specifier: 1.1.39
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)) version: 1.1.39(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
'@langchain/textsplitters': '@langchain/textsplitters':
specifier: 1.0.1 specifier: 1.0.1
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))) version: 1.0.1(@langchain/core@1.1.39(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))
'@modelcontextprotocol/sdk': '@modelcontextprotocol/sdk':
specifier: ^1.27.1 specifier: ^1.29.0
version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6) version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)
'@nest-lab/throttler-storage-redis': '@nest-lab/throttler-storage-redis':
specifier: ^1.2.0 specifier: ^1.2.0
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) 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)
@@ -647,8 +649,8 @@ importers:
specifier: ^4.6.1 specifier: ^4.6.1
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) 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: nodemailer:
specifier: ^8.0.4 specifier: ^8.0.5
version: 8.0.4 version: 8.0.5
openid-client: openid-client:
specifier: ^6.8.2 specifier: ^6.8.2
version: 6.8.2 version: 6.8.2
@@ -713,11 +715,11 @@ importers:
specifier: ^1.3.1 specifier: ^1.3.1
version: 1.3.1 version: 1.3.1
typesense: typesense:
specifier: ^3.0.3 specifier: ^3.0.5
version: 3.0.3(@babel/runtime@7.29.2) version: 3.0.5(@babel/runtime@7.29.2)
ws: ws:
specifier: 8.19.0 specifier: 8.20.0
version: 8.19.0 version: 8.20.0
yauzl: yauzl:
specifier: ^3.2.1 specifier: ^3.2.1
version: 3.2.1 version: 3.2.1
@@ -2294,11 +2296,11 @@ packages:
y-prosemirror: 1.3.7 y-prosemirror: 1.3.7
yjs: ^13.6.8 yjs: ^13.6.8
'@hono/node-server@1.19.10': '@hono/node-server@1.19.13':
resolution: {integrity: sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==} resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==}
engines: {node: '>=18.14.1'} engines: {node: '>=18.14.1'}
peerDependencies: peerDependencies:
hono: 4.12.8 hono: 4.12.12
'@humanfs/core@0.19.1': '@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
@@ -2614,8 +2616,8 @@ packages:
'@keyv/serialize@1.1.1': '@keyv/serialize@1.1.1':
resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
'@langchain/core@1.1.34': '@langchain/core@1.1.39':
resolution: {integrity: sha512-IDlZES5Vexo5meLQRCGkAU7NM0tPGPfPP5wcUzBd7Ot+JoFBmSXutC4gGzvZod5AKRVn3I0Qy5k8vkTraY21jA==} resolution: {integrity: sha512-DP9c7TREy6iA7HnywstmUAsNyJNYTFpRg2yBfQ+6H0l1HnvQzei9GsQ36GeOLxgRaD3vm9K8urCcawSC7yQpCw==}
engines: {node: '>=20'} engines: {node: '>=20'}
'@langchain/textsplitters@1.0.1': '@langchain/textsplitters@1.0.1':
@@ -2699,8 +2701,8 @@ packages:
'@mermaid-js/parser@1.0.1': '@mermaid-js/parser@1.0.1':
resolution: {integrity: sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==} resolution: {integrity: sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==}
'@modelcontextprotocol/sdk@1.27.1': '@modelcontextprotocol/sdk@1.29.0':
resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==} resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@cfworker/json-schema': ^4.1.1 '@cfworker/json-schema': ^4.1.1
@@ -5164,9 +5166,6 @@ packages:
'@types/use-sync-external-store@0.0.6': '@types/use-sync-external-store@0.0.6':
resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
'@types/uuid@10.0.0':
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
'@types/validator@13.15.10': '@types/validator@13.15.10':
resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==}
@@ -5661,8 +5660,8 @@ packages:
avvio@9.1.0: avvio@9.1.0:
resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==} resolution: {integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==}
axios@1.13.6: axios@1.15.0:
resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==}
babel-jest@30.3.0: babel-jest@30.3.0:
resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==}
@@ -6060,9 +6059,6 @@ packages:
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
engines: {node: ^14.18.0 || >=16.10.0} engines: {node: ^14.18.0 || >=16.10.0}
console-table-printer@2.14.6:
resolution: {integrity: sha512-MCBl5HNVaFuuHW6FGbL/4fB7N/ormCy+tQ+sxTrF6QtSbSNETvPuOVbkJBhzDgYhvjWGrTma4eYJa37ZuoQsPw==}
content-disposition@1.0.1: content-disposition@1.0.1:
resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -6164,10 +6160,6 @@ packages:
cross-fetch@4.0.0: cross-fetch@4.0.0:
resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
cross-spawn@7.0.5:
resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==}
engines: {node: '>= 8'}
cross-spawn@7.0.6: cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@@ -7243,8 +7235,8 @@ packages:
hoist-non-react-statics@3.3.2: hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
hono@4.12.8: hono@4.12.12:
resolution: {integrity: sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==} resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==}
engines: {node: '>=16.9.0'} engines: {node: '>=16.9.0'}
hookified@1.15.1: hookified@1.15.1:
@@ -8030,13 +8022,14 @@ packages:
resolution: {integrity: sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==} resolution: {integrity: sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==}
engines: {node: '>=20.10.0', npm: '>=10.2.3'} engines: {node: '>=20.10.0', npm: '>=10.2.3'}
langsmith@0.5.7: langsmith@0.5.18:
resolution: {integrity: sha512-FjYf2oBGMoSXnaT4SRaFguIiGJaonZ5VKWKJDPl9awLZjz2RkN29AcQWceecSINVzXzTvtRWPOjAWT+XggqNNg==} resolution: {integrity: sha512-3zuZUWffTHQ+73EAwnodADtf534VNEZUpXr9jC12qyG8/IQuJET7PRsCpTb9wX2lmBspakwLUpqpj3tNm/0bVA==}
peerDependencies: peerDependencies:
'@opentelemetry/api': '*' '@opentelemetry/api': '*'
'@opentelemetry/exporter-trace-otlp-proto': '*' '@opentelemetry/exporter-trace-otlp-proto': '*'
'@opentelemetry/sdk-trace-base': '*' '@opentelemetry/sdk-trace-base': '*'
openai: '*' openai: '*'
ws: 8.20.0
peerDependenciesMeta: peerDependenciesMeta:
'@opentelemetry/api': '@opentelemetry/api':
optional: true optional: true
@@ -8046,6 +8039,8 @@ packages:
optional: true optional: true
openai: openai:
optional: true optional: true
ws:
optional: true
layout-base@1.0.2: layout-base@1.0.2:
resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
@@ -8553,8 +8548,8 @@ packages:
node-releases@2.0.27: node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
nodemailer@8.0.4: nodemailer@8.0.5:
resolution: {integrity: sha512-k+jf6N8PfQJ0Fe8ZhJlgqU5qJU44Lpvp2yvidH3vp1lPnVQMgi4yEEMPXg5eJS1gFIJTVq1NHBk7Ia9ARdSBdQ==} resolution: {integrity: sha512-0PF8Yb1yZuQfQbq+5/pZJrtF6WQcjTd5/S4JOHs9PGFxuTqoB/icwuB44pOdURHJbRKX1PPoJZtY7R4VUoCC8w==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
normalize-path@3.0.0: normalize-path@3.0.0:
@@ -8667,7 +8662,7 @@ packages:
resolution: {integrity: sha512-qqjzHls7F5xkXNGy9P1Ei1rorI5LWupUUFWP66zPU8FlZbiITX8SFcHMKNZg/NATJ0LpIZcMUFxSwQmdeQPwSw==} resolution: {integrity: sha512-qqjzHls7F5xkXNGy9P1Ei1rorI5LWupUUFWP66zPU8FlZbiITX8SFcHMKNZg/NATJ0LpIZcMUFxSwQmdeQPwSw==}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
ws: 8.19.0 ws: 8.20.0
zod: ^3.25 || ^4.0 zod: ^3.25 || ^4.0
peerDependenciesMeta: peerDependenciesMeta:
ws: ws:
@@ -9154,8 +9149,9 @@ packages:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
proxy-from-env@1.1.0: proxy-from-env@2.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'}
prr@1.0.1: prr@1.0.1:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
@@ -9694,9 +9690,6 @@ packages:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'} engines: {node: '>=14'}
simple-wcswidth@1.1.2:
resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==}
sisteransi@1.0.5: sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
@@ -10173,8 +10166,8 @@ packages:
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
typesense@3.0.3: typesense@3.0.5:
resolution: {integrity: sha512-Cue72Hbz0Aj7DMNXzHIuitBKHWK12GprIFC/7AQ8kR73vJ4iNaKz4eUPICJmQOdWhblEeBv8Am4f9wqEDFd66A==} resolution: {integrity: sha512-Pw/yWosbqEOFMM/wQDsnS8FA6r3Qp5ilxuqZTMBoUc95SGCEBflMd39kvDEZZFoTORzNDxCLiiQ+LfYJTl1ulQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
'@babel/runtime': ^7.23.2 '@babel/runtime': ^7.23.2
@@ -10553,8 +10546,8 @@ packages:
resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
ws@8.19.0: ws@8.20.0:
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
peerDependencies: peerDependencies:
bufferutil: ^4.0.1 bufferutil: ^4.0.1
@@ -12571,7 +12564,7 @@ snapshots:
'@hocuspocus/common': 3.4.4 '@hocuspocus/common': 3.4.4
'@lifeomic/attempt': 3.0.3 '@lifeomic/attempt': 3.0.3
lib0: 0.2.117 lib0: 0.2.117
ws: 8.19.0 ws: 8.20.0
y-protocols: 1.0.6(yjs@13.6.30) y-protocols: 1.0.6(yjs@13.6.30)
yjs: 13.6.30 yjs: 13.6.30
transitivePeerDependencies: transitivePeerDependencies:
@@ -12585,7 +12578,7 @@ snapshots:
async-mutex: 0.5.0 async-mutex: 0.5.0
kleur: 4.1.5 kleur: 4.1.5
lib0: 0.2.117 lib0: 0.2.117
ws: 8.19.0 ws: 8.20.0
y-protocols: 1.0.6(yjs@13.6.30) y-protocols: 1.0.6(yjs@13.6.30)
yjs: 13.6.30 yjs: 13.6.30
transitivePeerDependencies: transitivePeerDependencies:
@@ -12600,9 +12593,9 @@ snapshots:
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) 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 yjs: 13.6.30
'@hono/node-server@1.19.10(hono@4.12.8)': '@hono/node-server@1.19.13(hono@4.12.12)':
dependencies: dependencies:
hono: 4.12.8 hono: 4.12.12
'@humanfs/core@0.19.1': {} '@humanfs/core@0.19.1': {}
@@ -13025,7 +13018,7 @@ snapshots:
'@keyv/serialize@1.1.1': {} '@keyv/serialize@1.1.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))': '@langchain/core@1.1.39(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)':
dependencies: dependencies:
'@cfworker/json-schema': 4.1.1 '@cfworker/json-schema': 4.1.1
'@standard-schema/spec': 1.1.0 '@standard-schema/spec': 1.1.0
@@ -13033,7 +13026,7 @@ snapshots:
camelcase: 6.3.0 camelcase: 6.3.0
decamelize: 1.2.0 decamelize: 1.2.0
js-tiktoken: 1.0.21 js-tiktoken: 1.0.21
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)) langsmith: 0.5.18(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
mustache: 4.2.0 mustache: 4.2.0
p-queue: 6.6.2 p-queue: 6.6.2
uuid: 11.1.0 uuid: 11.1.0
@@ -13043,10 +13036,11 @@ snapshots:
- '@opentelemetry/exporter-trace-otlp-proto' - '@opentelemetry/exporter-trace-otlp-proto'
- '@opentelemetry/sdk-trace-base' - '@opentelemetry/sdk-trace-base'
- openai - openai
- ws
'@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)))': '@langchain/textsplitters@1.0.1(@langchain/core@1.1.39(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))':
dependencies: dependencies:
'@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)) '@langchain/core': 1.1.39(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
js-tiktoken: 1.0.21 js-tiktoken: 1.0.21
'@lifeomic/attempt@3.0.3': {} '@lifeomic/attempt@3.0.3': {}
@@ -13126,19 +13120,19 @@ snapshots:
dependencies: dependencies:
langium: 4.2.1 langium: 4.2.1
'@modelcontextprotocol/sdk@1.27.1(@cfworker/json-schema@4.1.1)(zod@4.3.6)': '@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)':
dependencies: dependencies:
'@hono/node-server': 1.19.10(hono@4.12.8) '@hono/node-server': 1.19.13(hono@4.12.12)
ajv: 8.18.0 ajv: 8.18.0
ajv-formats: 3.0.1(ajv@8.18.0) ajv-formats: 3.0.1(ajv@8.18.0)
content-type: 1.0.5 content-type: 1.0.5
cors: 2.8.5 cors: 2.8.5
cross-spawn: 7.0.5 cross-spawn: 7.0.6
eventsource: 3.0.7 eventsource: 3.0.7
eventsource-parser: 3.0.6 eventsource-parser: 3.0.6
express: 5.2.1 express: 5.2.1
express-rate-limit: 8.2.2(express@5.2.1) express-rate-limit: 8.2.2(express@5.2.1)
hono: 4.12.8 hono: 4.12.12
jose: 6.1.3 jose: 6.1.3
json-schema-typed: 8.0.2 json-schema-typed: 8.0.2
pkce-challenge: 5.0.1 pkce-challenge: 5.0.1
@@ -15749,8 +15743,6 @@ snapshots:
'@types/use-sync-external-store@0.0.6': {} '@types/use-sync-external-store@0.0.6': {}
'@types/uuid@10.0.0': {}
'@types/validator@13.15.10': {} '@types/validator@13.15.10': {}
'@types/whatwg-mimetype@3.0.2': {} '@types/whatwg-mimetype@3.0.2': {}
@@ -16279,11 +16271,11 @@ snapshots:
'@fastify/error': 4.0.0 '@fastify/error': 4.0.0
fastq: 1.17.1 fastq: 1.17.1
axios@1.13.6: axios@1.15.0:
dependencies: dependencies:
follow-redirects: 1.15.11 follow-redirects: 1.15.11
form-data: 4.0.5 form-data: 4.0.5
proxy-from-env: 1.1.0 proxy-from-env: 2.1.0
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
@@ -16419,7 +16411,7 @@ snapshots:
bytes: 3.1.2 bytes: 3.1.2
content-type: 1.0.5 content-type: 1.0.5
debug: 4.4.3 debug: 4.4.3
http-errors: 2.0.0 http-errors: 2.0.1
iconv-lite: 0.7.2 iconv-lite: 0.7.2
on-finished: 2.4.1 on-finished: 2.4.1
qs: 6.14.2 qs: 6.14.2
@@ -16759,10 +16751,6 @@ snapshots:
consola@3.4.2: {} consola@3.4.2: {}
console-table-printer@2.14.6:
dependencies:
simple-wcswidth: 1.1.2
content-disposition@1.0.1: {} content-disposition@1.0.1: {}
content-type@1.0.5: {} content-type@1.0.5: {}
@@ -16859,12 +16847,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
cross-spawn@7.0.5:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
cross-spawn@7.0.6: cross-spawn@7.0.6:
dependencies: dependencies:
path-key: 3.1.1 path-key: 3.1.1
@@ -17320,7 +17302,7 @@ snapshots:
'@socket.io/component-emitter': 3.1.0 '@socket.io/component-emitter': 3.1.0
debug: 4.3.7 debug: 4.3.7
engine.io-parser: 5.2.2 engine.io-parser: 5.2.2
ws: 8.19.0 ws: 8.20.0
xmlhttprequest-ssl: 2.1.2 xmlhttprequest-ssl: 2.1.2
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
@@ -17340,7 +17322,7 @@ snapshots:
cors: 2.8.5 cors: 2.8.5
debug: 4.3.7 debug: 4.3.7
engine.io-parser: 5.2.2 engine.io-parser: 5.2.2
ws: 8.19.0 ws: 8.20.0
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- supports-color - supports-color
@@ -17740,7 +17722,7 @@ snapshots:
execa@5.1.1: execa@5.1.1:
dependencies: dependencies:
cross-spawn: 7.0.5 cross-spawn: 7.0.6
get-stream: 6.0.1 get-stream: 6.0.1
human-signals: 2.1.0 human-signals: 2.1.0
is-stream: 2.0.1 is-stream: 2.0.1
@@ -17790,7 +17772,7 @@ snapshots:
etag: 1.8.1 etag: 1.8.1
finalhandler: 2.1.1 finalhandler: 2.1.1
fresh: 2.0.0 fresh: 2.0.0
http-errors: 2.0.0 http-errors: 2.0.1
merge-descriptors: 2.0.0 merge-descriptors: 2.0.0
mime-types: 3.0.2 mime-types: 3.0.2
on-finished: 2.4.1 on-finished: 2.4.1
@@ -17802,7 +17784,7 @@ snapshots:
router: 2.2.0 router: 2.2.0
send: 1.2.1 send: 1.2.1
serve-static: 2.2.1 serve-static: 2.2.1
statuses: 2.0.1 statuses: 2.0.2
type-is: 2.0.1 type-is: 2.0.1
vary: 1.1.2 vary: 1.1.2
transitivePeerDependencies: transitivePeerDependencies:
@@ -17928,7 +17910,7 @@ snapshots:
escape-html: 1.0.3 escape-html: 1.0.3
on-finished: 2.4.1 on-finished: 2.4.1
parseurl: 1.3.3 parseurl: 1.3.3
statuses: 2.0.1 statuses: 2.0.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -18150,7 +18132,7 @@ snapshots:
'@types/ws': 8.18.1 '@types/ws': 8.18.1
entities: 7.0.1 entities: 7.0.1
whatwg-mimetype: 3.0.0 whatwg-mimetype: 3.0.0
ws: 8.19.0 ws: 8.20.0
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
@@ -18199,7 +18181,7 @@ snapshots:
dependencies: dependencies:
react-is: 16.13.1 react-is: 16.13.1
hono@4.12.8: {} hono@4.12.12: {}
hookified@1.15.1: {} hookified@1.15.1: {}
@@ -19044,7 +19026,7 @@ snapshots:
whatwg-encoding: 3.1.1 whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0 whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0 whatwg-url: 14.2.0
ws: 8.19.0 ws: 8.20.0
xml-name-validator: 5.0.0 xml-name-validator: 5.0.0
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
@@ -19193,18 +19175,15 @@ snapshots:
vscode-languageserver-textdocument: 1.0.12 vscode-languageserver-textdocument: 1.0.12
vscode-uri: 3.1.0 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)): langsmith@0.5.18(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.2.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0):
dependencies: dependencies:
'@types/uuid': 10.0.0
chalk: 5.6.2
console-table-printer: 2.14.6
p-queue: 6.6.2 p-queue: 6.6.2
semver: 7.7.4
uuid: 10.0.0 uuid: 10.0.0
optionalDependencies: optionalDependencies:
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
'@opentelemetry/sdk-trace-base': 2.2.0(@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) openai: 6.2.0(ws@8.20.0)(zod@4.3.6)
ws: 8.20.0
layout-base@1.0.2: {} layout-base@1.0.2: {}
@@ -19650,7 +19629,7 @@ snapshots:
node-releases@2.0.27: {} node-releases@2.0.27: {}
nodemailer@8.0.4: {} nodemailer@8.0.5: {}
normalize-path@3.0.0: {} normalize-path@3.0.0: {}
@@ -19673,7 +19652,7 @@ snapshots:
'@yarnpkg/lockfile': 1.1.0 '@yarnpkg/lockfile': 1.1.0
'@yarnpkg/parsers': 3.0.2 '@yarnpkg/parsers': 3.0.2
'@zkochan/js-yaml': 0.0.7 '@zkochan/js-yaml': 0.0.7
axios: 1.13.6 axios: 1.15.0
cli-cursor: 3.1.0 cli-cursor: 3.1.0
cli-spinners: 2.6.1 cli-spinners: 2.6.1
cliui: 8.0.1 cliui: 8.0.1
@@ -19805,9 +19784,9 @@ snapshots:
is-docker: 2.2.1 is-docker: 2.2.1
is-wsl: 2.2.0 is-wsl: 2.2.0
openai@6.2.0(ws@8.19.0)(zod@4.3.6): openai@6.2.0(ws@8.20.0)(zod@4.3.6):
optionalDependencies: optionalDependencies:
ws: 8.19.0 ws: 8.20.0
zod: 4.3.6 zod: 4.3.6
optional: true optional: true
@@ -20241,7 +20220,7 @@ snapshots:
postmark@4.0.7: postmark@4.0.7:
dependencies: dependencies:
axios: 1.13.6 axios: 1.15.0
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
@@ -20405,7 +20384,7 @@ snapshots:
forwarded: 0.2.0 forwarded: 0.2.0
ipaddr.js: 1.9.1 ipaddr.js: 1.9.1
proxy-from-env@1.1.0: {} proxy-from-env@2.1.0: {}
prr@1.0.1: prr@1.0.1:
optional: true optional: true
@@ -21082,8 +21061,6 @@ snapshots:
signal-exit@4.1.0: {} signal-exit@4.1.0: {}
simple-wcswidth@1.1.2: {}
sisteransi@1.0.5: {} sisteransi@1.0.5: {}
slash@3.0.0: {} slash@3.0.0: {}
@@ -21093,7 +21070,7 @@ snapshots:
socket.io-adapter@2.5.4: socket.io-adapter@2.5.4:
dependencies: dependencies:
debug: 4.3.7 debug: 4.3.7
ws: 8.19.0 ws: 8.20.0
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- supports-color - supports-color
@@ -21627,10 +21604,10 @@ snapshots:
typescript@5.9.3: {} typescript@5.9.3: {}
typesense@3.0.3(@babel/runtime@7.29.2): typesense@3.0.5(@babel/runtime@7.29.2):
dependencies: dependencies:
'@babel/runtime': 7.29.2 '@babel/runtime': 7.29.2
axios: 1.13.6 axios: 1.15.0
loglevel: 1.9.2 loglevel: 1.9.2
tslib: 2.8.1 tslib: 2.8.1
transitivePeerDependencies: transitivePeerDependencies:
@@ -22016,7 +21993,7 @@ snapshots:
imurmurhash: 0.1.4 imurmurhash: 0.1.4
signal-exit: 4.1.0 signal-exit: 4.1.0
ws@8.19.0: {} ws@8.20.0: {}
xml-crypto@6.1.2: xml-crypto@6.1.2:
dependencies: dependencies: