From 8fa34705f6b8e4807d95f68ce7ecb8b17b57525b Mon Sep 17 00:00:00 2001 From: andresgutgon Date: Wed, 11 Dec 2024 14:40:36 +0100 Subject: [PATCH] Implement filter logs by creation date and refactor a bit --- .../HistoryLogParams/useLogHistoryParams.ts | 4 +- .../DocumentLogsTable/index.tsx | 4 +- .../DocumentLogs/DocumentLogsTable.tsx | 4 + .../index.tsx} | 35 ++--- .../index.tsx} | 29 ++-- .../logs/_components/Filters/index.tsx | 107 +++---------- .../[documentUuid]/logs/_components/index.tsx | 26 +-- .../documents/[documentUuid]/logs/page.tsx | 49 ++---- .../logs/[documentLogUuid]/position/route.ts | 18 +-- .../[documentUuid]/logs/aggregations/route.ts | 16 +- .../[documentUuid]/logs/daily-count/route.ts | 15 +- .../[documentUuid]/logs/pagination/route.ts | 23 +-- .../documents/[documentUuid]/logs/route.ts | 48 +----- .../GoToPageInput/index.tsx | 1 + .../useDefaultLogFilterOptions.ts} | 3 +- .../hooks/logFilters/useProcessLogFilters.ts | 103 ++++++++++++ apps/web/src/services/helpers.ts | 43 ----- apps/web/src/services/routes/api.ts | 60 ++++--- packages/core/package.json | 3 +- packages/core/src/constants.ts | 2 + .../src/lib/pagination/buildPaginatedUrl.ts | 82 ++++++++-- .../src/lib/pagination/buildPagination.ts | 13 +- packages/core/src/lib/pagination/paginate.ts | 1 + .../computeDocumentLogsAggregations.test.ts | 1 + .../computeDocumentLogsDailyCount.test.ts | 4 + .../computeDocumentLogsWithMetadata.test.ts | 9 ++ .../core/src/services/documentLogs/index.ts | 1 + ...rateDocumentLogsApiRouteWithParams.test.ts | 148 ++++++++++++++++++ .../generateDocumentLogsApiRouteWithParams.ts | 77 +++++++++ .../documentLogs/logsFilterUtils/index.ts | 3 + .../parseApiLogFilterParams.ts | 48 ++++++ .../logsFilterUtils/parseLogFilterParams.ts | 50 ++++++ .../src/ds/atoms/Checkbox/Primitive/index.tsx | 2 +- pnpm-lock.yaml | 55 +++---- 34 files changed, 702 insertions(+), 385 deletions(-) rename apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/{CommitFilter.tsx => CommitFilter/index.tsx} (81%) rename apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/{LogSourceFilter.tsx => LogSourceFilter/index.tsx} (79%) rename apps/web/src/hooks/{useLogFilterOptions.ts => logFilters/useDefaultLogFilterOptions.ts} (86%) create mode 100644 apps/web/src/hooks/logFilters/useProcessLogFilters.ts delete mode 100644 apps/web/src/services/helpers.ts create mode 100644 packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.test.ts create mode 100644 packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.ts create mode 100644 packages/core/src/services/documentLogs/logsFilterUtils/index.ts create mode 100644 packages/core/src/services/documentLogs/logsFilterUtils/parseApiLogFilterParams.ts create mode 100644 packages/core/src/services/documentLogs/logsFilterUtils/parseLogFilterParams.ts diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/DocumentParams/HistoryLogParams/useLogHistoryParams.ts b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/DocumentParams/HistoryLogParams/useLogHistoryParams.ts index 0cef5d1f7..21e7bcebc 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/DocumentParams/HistoryLogParams/useLogHistoryParams.ts +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/Playground/DocumentParams/HistoryLogParams/useLogHistoryParams.ts @@ -8,7 +8,7 @@ import useDocumentLogWithPaginationPosition, { LogWithPosition, } from '$/stores/documentLogWithPaginationPosition' import useDocumentLogsPagination from '$/stores/useDocumentLogsPagination' -import { useFilterOptions } from '$/hooks/useLogFilterOptions' +import { useDefaultLogFilterOptions } from '$/hooks/logFilters/useDefaultLogFilterOptions' const ONLY_ONE_PAGE = '1' @@ -28,7 +28,7 @@ export function useLogHistoryParams({ commitVersionUuid, }) - const filterOptions = useFilterOptions() + const filterOptions = useDefaultLogFilterOptions() const { data: pagination, isLoading: isLoadingCounter } = useDocumentLogsPagination({ documentUuid: document.documentUuid, diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/ManualEvaluationResults/DocumentLogsTable/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/ManualEvaluationResults/DocumentLogsTable/index.tsx index 6912acd33..179c671e6 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/ManualEvaluationResults/DocumentLogsTable/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/ManualEvaluationResults/DocumentLogsTable/index.tsx @@ -30,7 +30,7 @@ import { useSearchParams } from 'next/navigation' import { DocumentLogWithMetadataAndErrorAndEvaluationResult } from '..' import { ResultCellContent } from '../../EvaluationResults/EvaluationResultsTable' import { useCommits } from '$/stores/commitsStore' -import { useFilterOptions } from '$/hooks/useLogFilterOptions' +import { useDefaultLogFilterOptions } from '$/hooks/logFilters/useDefaultLogFilterOptions' const countLabel = (selected: number) => (count: number) => { return selected ? `${selected} of ${count} logs selected` : `${count} logs` @@ -69,7 +69,7 @@ export const DocumentLogsTable = forwardRef( const { commit } = useCurrentCommit() const { document } = useCurrentDocument() const { data: commits } = useCommits() - const filterOptions = useFilterOptions() + const filterOptions = useDefaultLogFilterOptions() const { data: pagination, isLoading } = useDocumentLogsPagination({ documentUuid: commits ? document.documentUuid : undefined, projectId: project.id, diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx index 277f7c1ba..c34ed68a2 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx @@ -6,6 +6,7 @@ import { capitalize } from 'lodash-es' import { DocumentLogFilterOptions, EvaluationResultableType, + LOG_FILTERS_ENCODED_PARAMS, } from '@latitude-data/core/browser' import { buildPagination } from '@latitude-data/core/lib/pagination/buildPagination' import { @@ -142,6 +143,7 @@ export const DocumentLogsTable = forwardRef( }) const queryParams = typeof window !== 'undefined' ? window.location.search : '' + return ( ( .root, count: pagination.count, queryParams, + encodeQueryParams: false, + paramsToEncode: LOG_FILTERS_ENCODED_PARAMS, page: Number(page), pageSize: Number(pageSize), }) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/CommitFilter.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/CommitFilter/index.tsx similarity index 81% rename from apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/CommitFilter.tsx rename to apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/CommitFilter/index.tsx index 0877f33bb..087921470 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/CommitFilter.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/CommitFilter/index.tsx @@ -4,17 +4,17 @@ import { Commit } from '@latitude-data/core/browser' import { Button, Checkbox, Text } from '@latitude-data/web-ui' import { useCommits } from '$/stores/commitsStore' -import { BadgeCommit } from '../../../../../_components/Sidebar/CommitSelector/CommitItem' -import { FilterButton } from './FilterButton' +import { BadgeCommit } from '../../../../../../_components/Sidebar/CommitSelector/CommitItem' +import { FilterButton } from '../FilterButton' function CommitCheckbox({ commit, selectedCommitsIds, - setSelectedCommitsIds, + onSelectCommits, }: { commit: Commit selectedCommitsIds: number[] - setSelectedCommitsIds: (selectedCommitsIds: number[]) => void + onSelectCommits: (selectedCommitsIds: number[]) => void }) { const isSelected = useMemo( () => selectedCommitsIds.includes(commit.id), @@ -22,7 +22,7 @@ function CommitCheckbox({ ) const onSelect = useCallback(() => { - setSelectedCommitsIds( + onSelectCommits( isSelected ? selectedCommitsIds.filter((id) => id !== commit.id) : [...selectedCommitsIds, commit.id], @@ -51,12 +51,12 @@ function CommitsList({ title, commits, selectedCommitsIds, - setSelectedCommitsIds, + onSelectCommits, }: { title: string commits: Commit[] selectedCommitsIds: number[] - setSelectedCommitsIds: (selectedCommitsIds: number[]) => void + onSelectCommits: (selectedCommitsIds: number[]) => void }) { return (
@@ -67,7 +67,7 @@ function CommitsList({ ))} @@ -78,12 +78,12 @@ function CommitsList({ export function CommitFilter({ selectedCommitsIds, - setSelectedCommitsIds, + onSelectCommits, isDefault, reset, }: { selectedCommitsIds: number[] - setSelectedCommitsIds: (selectedCommitsIds: number[]) => void + onSelectCommits: (selectedCommitsIds: number[]) => void isDefault: boolean reset: () => void }) { @@ -131,7 +131,7 @@ export function CommitFilter({ - setSelectedCommitsIds(headerState ? [] : commits.map((c) => c.id)) + onSelectCommits(headerState ? [] : commits.map((c) => c.id)) } label={ @@ -140,29 +140,24 @@ export function CommitFilter({ } /> -
-
+
{drafts.length > 0 ? ( ) : null}
diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/LogSourceFilter.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/LogSourceFilter/index.tsx similarity index 79% rename from apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/LogSourceFilter.tsx rename to apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/LogSourceFilter/index.tsx index 0e9752334..4a87d67e5 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/LogSourceFilter.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/LogSourceFilter/index.tsx @@ -3,7 +3,7 @@ import { useCallback, useMemo } from 'react' import { LOG_SOURCES, LogSources } from '@latitude-data/core/browser' import { Button, Checkbox, Text } from '@latitude-data/web-ui' -import { FilterButton } from './FilterButton' +import { FilterButton } from '../FilterButton' const LogSourceLabel: { [key in LogSources]: string } = { [LogSources.API]: 'API', @@ -16,11 +16,11 @@ const LogSourceLabel: { [key in LogSources]: string } = { function LogSourceCheckbox({ logSource, selectedLogSources, - setSelectedLogSources, + onSelectLogSources, }: { logSource: LogSources selectedLogSources: LogSources[] - setSelectedLogSources: (selectedLogSources: LogSources[]) => void + onSelectLogSources: (selectedLogSources: LogSources[]) => void }) { const isSelected = useMemo( () => selectedLogSources.includes(logSource), @@ -28,12 +28,12 @@ function LogSourceCheckbox({ ) const onSelect = useCallback(() => { - setSelectedLogSources( + onSelectLogSources( isSelected ? selectedLogSources.filter((origin) => origin !== logSource) : [...selectedLogSources, logSource], ) - }, [selectedLogSources, logSource, isSelected]) + }, [selectedLogSources, logSource, isSelected, onSelectLogSources]) return ( void + onSelectLogSources: (selectedOrigins: LogSources[]) => void isDefault: boolean reset: () => void }) { @@ -87,7 +87,7 @@ export function LogSourceFilter({ - setSelectedLogSources(headerState ? [] : LOG_SOURCES) + onSelectLogSources(headerState ? [] : LOG_SOURCES) } label={ @@ -96,22 +96,17 @@ export function LogSourceFilter({ } /> -
-
    - {Object.values(LogSources).map((logSource) => ( +
      + {LOG_SOURCES.map((logSource) => (
    • ))} diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/index.tsx index a2f656ac7..f2bebc439 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/Filters/index.tsx @@ -1,98 +1,27 @@ -import { useCallback, useMemo } from 'react' - import { DocumentLogFilterOptions, LOG_SOURCES, - LogSources, } from '@latitude-data/core/browser' -import { usePathname, useRouter } from 'next/navigation' import { CommitFilter } from './CommitFilter' import { LogSourceFilter } from './LogSourceFilter' import { ReactStateDispatch, DatePickerRange } from '@latitude-data/web-ui' - -function useEditableSearchParams() { - const router = useRouter() - const pathname = usePathname() - - const setSearchParam = (key: string, value?: string | string[]) => { - const prevParams = window.location.search - const urlParams = new URLSearchParams(prevParams) - const params = Object.fromEntries(urlParams.entries()) - - let newParams = prevParams - if (value) { - const data = { - ...params, - [key]: Array.isArray(value) ? value.join(',') : value, - } - newParams = new URLSearchParams(data).toString() - } - - router.replace(`${pathname}${newParams ? '?' : ''}${newParams}`) - } - - return setSearchParam -} +import { useProcessLogFilters } from '$/hooks/logFilters/useProcessLogFilters' export function DocumentLogFilters({ - documentLogFilterOptions, - setDocumentLogFilterOptions, + filterOptions, + onFiltersChanged, originalSelectedCommitsIds, }: { - documentLogFilterOptions: DocumentLogFilterOptions - setDocumentLogFilterOptions: ReactStateDispatch + filterOptions: DocumentLogFilterOptions + onFiltersChanged: ReactStateDispatch originalSelectedCommitsIds: number[] }) { - const setSearchParams = useEditableSearchParams() - - const isCommitsDefault = useMemo(() => { - return ( - documentLogFilterOptions.commitIds.sort().join(',') === - originalSelectedCommitsIds.sort().join(',') - ) - }, [documentLogFilterOptions.commitIds]) - - const isLogSourcesDefault = useMemo(() => { - return ( - documentLogFilterOptions.logSources.sort().join(',') === - LOG_SOURCES.sort().join(',') - ) - }, [documentLogFilterOptions.logSources]) - - const setSelectedCommitsIds = useCallback((selectedCommitsIds: number[]) => { - setDocumentLogFilterOptions((currentFilters) => ({ - ...currentFilters, - commitIds: selectedCommitsIds, - })) - if ( - selectedCommitsIds.sort().join(',') === - originalSelectedCommitsIds.sort().join(',') - ) { - setSearchParams('versions', undefined) - } else { - setSearchParams('versions', selectedCommitsIds.map(String)) - } - }, []) - - const setSelectedLogSources = useCallback( - (selectedLogSources: LogSources[]) => { - setDocumentLogFilterOptions((currentFilters) => ({ - ...currentFilters, - logSources: selectedLogSources, - })) - if ( - selectedLogSources.sort().join(',') === - Object.values(LogSources).sort().join(',') - ) { - setSearchParams('origins', undefined) - } else { - setSearchParams('origins', selectedLogSources) - } - }, - [], - ) - + const filters = useProcessLogFilters({ + onFiltersChanged, + filterOptions, + originalSelectedCommitsIds, + }) return ( <> setSelectedCommitsIds(originalSelectedCommitsIds)} + selectedCommitsIds={filterOptions.commitIds} + onSelectCommits={filters.onSelectCommits} + isDefault={filters.isCommitsDefault} + reset={() => filters.onSelectCommits(originalSelectedCommitsIds)} /> setSelectedLogSources(Object.values(LogSources))} + selectedLogSources={filterOptions.logSources} + onSelectLogSources={filters.onSelectLogSources} + isDefault={filters.isLogSourcesDefault} + reset={() => filters.onSelectLogSources(LOG_SOURCES)} /> ) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/index.tsx index 88447e3a2..1d2292c81 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/index.tsx @@ -131,24 +131,13 @@ export function DocumentLogsPage({
      - } actions={ <> {showLogFilters && ( )} } + table={ + + } />
      ) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/page.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/page.tsx index b254d7b76..f5b5b1654 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/page.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/page.tsx @@ -1,6 +1,5 @@ import { DocumentLogFilterOptions, - LogSources, Workspace, } from '@latitude-data/core/browser' import { QueryParams } from '@latitude-data/core/lib/pagination/buildPaginatedUrl' @@ -17,6 +16,7 @@ import { redirect } from 'next/navigation' import { DocumentLogsPage } from './_components' import { DocumentLogsRepository } from '@latitude-data/core/repositories' import { DocumentLogBlankSlate } from './_components/DocumentLogs/DocumentLogBlankSlate' +import { parseLogFiltersParams } from '@latitude-data/core/services/documentLogs/index' async function fetchDocumentLogPage({ workspace, @@ -63,43 +63,19 @@ export default async function DocumentPage({ const commit = await findCommitCached({ projectId, uuid: commitUuid }) const commits = await findCommitsByProjectCached({ projectId }) - const { - logUuid, - pageSize, - page: pageString, - versions: _selectedCommitsIds, - origins: _selectedLogSources, - } = await searchParams - - const originalSelectedCommitsIds = [ - ...commits.filter((c) => !!c.mergedAt).map((c) => c.id), - ...(!commit.mergedAt ? [commit.id] : []), - ] - - const selectedCommitsIds = ( - Array.isArray(_selectedCommitsIds) - ? _selectedCommitsIds - : (_selectedCommitsIds?.split(',') ?? originalSelectedCommitsIds) - ).map(Number) - - const selectedSources = ( - Array.isArray(_selectedLogSources) - ? _selectedLogSources - : (_selectedLogSources?.split(',') ?? Object.values(LogSources)) - ).filter((s) => - Object.values(LogSources).includes(s as LogSources), - ) as LogSources[] - - const logsFilterOptions: DocumentLogFilterOptions = { - commitIds: selectedCommitsIds, - logSources: selectedSources, - } + const { logUuid, pageSize, page: pageString, ...rest } = await searchParams + const { filterOptions, redirectUrlParams, originalSelectedCommitsIds } = + parseLogFiltersParams({ + params: rest, + currentCommit: commit, + commits, + }) const documentLogUuid = logUuid?.toString() const page = pageString?.toString?.() const currentLogPage = await fetchDocumentLogPage({ workspace, - filterOptions: logsFilterOptions, + filterOptions, documentLogUuid, }) @@ -112,8 +88,7 @@ export default async function DocumentPage({ const parameters = [ `page=${currentLogPage}`, `logUuid=${documentLogUuid}`, - _selectedCommitsIds ? `versions=${_selectedCommitsIds}` : undefined, - _selectedLogSources ? `origins=${_selectedLogSources}` : undefined, + ...redirectUrlParams, ].filter(Boolean) return redirect(`${route}?${parameters.join('&')}`) @@ -122,7 +97,7 @@ export default async function DocumentPage({ const rows = await computeDocumentLogsWithMetadataQuery({ workspaceId: workspace.id, documentUuid, - filterOptions: logsFilterOptions, + filterOptions, page, pageSize: pageSize as string | undefined, }) @@ -134,7 +109,7 @@ export default async function DocumentPage({ documentLogs={rows} selectedLog={selectedLog} originalSelectedCommitsIds={originalSelectedCommitsIds} - documengLogFilterOptions={logsFilterOptions} + documengLogFilterOptions={filterOptions} /> ) } diff --git a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/[documentLogUuid]/position/route.ts b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/[documentLogUuid]/position/route.ts index af54e6524..180cea610 100644 --- a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/[documentLogUuid]/position/route.ts +++ b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/[documentLogUuid]/position/route.ts @@ -1,9 +1,9 @@ -import { LogSources, Workspace } from '@latitude-data/core/browser' +import { Workspace } from '@latitude-data/core/browser' import { fetchDocumentLogWithPosition } from '@latitude-data/core/services/documentLogs/fetchDocumentLogWithPosition' import { authHandler } from '$/middlewares/authHandler' import { errorHandler } from '$/middlewares/errorHandler' import { NextRequest, NextResponse } from 'next/server' -import { decodeParameters } from '$/services/helpers' +import { parseApiDocumentLogParams } from '@latitude-data/core/services/documentLogs/index' export const GET = errorHandler( authHandler( @@ -23,20 +23,12 @@ export const GET = errorHandler( }, ) => { const searchParams = req.nextUrl.searchParams - const excludeErrors = searchParams.get('excludeErrors') === 'true' - const { commitIds, logSources } = decodeParameters(req.nextUrl.search) - const filterOptions = { - commitIds: Array.isArray(commitIds) ? commitIds.map(Number) : [], - logSources: (Array.isArray(logSources) - ? logSources - : []) as LogSources[], - } - + const queryParams = parseApiDocumentLogParams({ searchParams }) const result = await fetchDocumentLogWithPosition({ workspace, - filterOptions, documentLogUuid, - excludeErrors, + filterOptions: queryParams.filterOptions, + excludeErrors: queryParams.excludeErrors, }) if (result.error) { diff --git a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/aggregations/route.ts b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/aggregations/route.ts index 0b4a92a9d..1bc6ef040 100644 --- a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/aggregations/route.ts +++ b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/aggregations/route.ts @@ -1,9 +1,9 @@ -import { LogSources, Workspace } from '@latitude-data/core/browser' +import { Workspace } from '@latitude-data/core/browser' import { computeDocumentLogsAggregations } from '@latitude-data/core/services/documentLogs/computeDocumentLogsAggregations' import { authHandler } from '$/middlewares/authHandler' import { errorHandler } from '$/middlewares/errorHandler' import { NextRequest, NextResponse } from 'next/server' -import { decodeParameters } from '$/services/helpers' +import { parseApiDocumentLogParams } from '@latitude-data/core/services/documentLogs/index' export const GET = errorHandler( authHandler( @@ -22,18 +22,12 @@ export const GET = errorHandler( }, ) => { const { documentUuid } = params - const { commitIds, logSources } = decodeParameters(req.nextUrl.search) - const filterOptions = { - commitIds: Array.isArray(commitIds) ? commitIds.map(Number) : [], - logSources: (Array.isArray(logSources) - ? logSources - : []) as LogSources[], - } - + const searchParams = req.nextUrl.searchParams + const queryParams = parseApiDocumentLogParams({ searchParams }) const result = await computeDocumentLogsAggregations({ workspace, documentUuid, - filterOptions, + filterOptions: queryParams.filterOptions, }) return NextResponse.json(result, { status: 200 }) diff --git a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/daily-count/route.ts b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/daily-count/route.ts index d8661bd70..84eaf8edb 100644 --- a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/daily-count/route.ts +++ b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/daily-count/route.ts @@ -1,9 +1,9 @@ -import { LogSources, Workspace } from '@latitude-data/core/browser' +import { Workspace } from '@latitude-data/core/browser' import { computeDocumentLogsDailyCount } from '@latitude-data/core/services/documentLogs/computeDocumentLogsDailyCount' import { authHandler } from '$/middlewares/authHandler' import { errorHandler } from '$/middlewares/errorHandler' import { NextRequest, NextResponse } from 'next/server' -import { decodeParameters } from '$/services/helpers' +import { parseApiDocumentLogParams } from '@latitude-data/core/services/documentLogs/index' export const GET = errorHandler( authHandler( @@ -23,21 +23,14 @@ export const GET = errorHandler( ) => { const { documentUuid } = params const searchParams = req.nextUrl.searchParams + const queryParams = parseApiDocumentLogParams({ searchParams }) const days = searchParams.get('days') ? parseInt(searchParams.get('days')!, 10) : undefined - const { commitIds, logSources } = decodeParameters(req.nextUrl.search) - const filterOptions = { - commitIds: Array.isArray(commitIds) ? commitIds.map(Number) : [], - logSources: (Array.isArray(logSources) - ? logSources - : []) as LogSources[], - } - const result = await computeDocumentLogsDailyCount({ workspace, documentUuid, - filterOptions, + filterOptions: queryParams.filterOptions, days, }) diff --git a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/pagination/route.ts b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/pagination/route.ts index ab1a6de31..027690caa 100644 --- a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/pagination/route.ts +++ b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/pagination/route.ts @@ -1,4 +1,4 @@ -import { LogSources, Workspace } from '@latitude-data/core/browser' +import { Workspace } from '@latitude-data/core/browser' import { buildPagination } from '@latitude-data/core/lib/pagination/buildPagination' import { computeDocumentLogsCount } from '@latitude-data/core/services/documentLogs/computeDocumentLogs' import { computeDocumentLogsWithMetadataCount } from '@latitude-data/core/services/documentLogs/computeDocumentLogsWithMetadata' @@ -6,7 +6,7 @@ import { authHandler } from '$/middlewares/authHandler' import { errorHandler } from '$/middlewares/errorHandler' import { ROUTES } from '$/services/routes' import { NextRequest, NextResponse } from 'next/server' -import { decodeParameters } from '$/services/helpers' +import { parseApiDocumentLogParams } from '@latitude-data/core/services/documentLogs/index' function pageUrl(params: { projectId: string @@ -37,15 +37,8 @@ export const GET = errorHandler( }, ) => { const searchParams = req.nextUrl.searchParams + const queryParams = parseApiDocumentLogParams({ searchParams }) const excludeErrors = searchParams.get('excludeErrors') === 'true' - const { commitIds, logSources } = decodeParameters(req.nextUrl.search) - const filterOptions = { - commitIds: Array.isArray(commitIds) ? commitIds.map(Number) : [], - logSources: (Array.isArray(logSources) - ? logSources - : []) as LogSources[], - } - const queryFn = excludeErrors ? computeDocumentLogsCount : computeDocumentLogsWithMetadataCount @@ -53,7 +46,7 @@ export const GET = errorHandler( const count = await queryFn({ workspaceId: workspace.id, documentUuid: params.documentUuid, - filterOptions, + filterOptions: queryParams.filterOptions, }) const pagination = buildPagination({ @@ -63,12 +56,8 @@ export const GET = errorHandler( documentUuid: params.documentUuid, }), count, - page: searchParams.get('page') - ? parseInt(searchParams.get('page') as string) - : 1, - pageSize: searchParams.get('pageSize') - ? parseInt(searchParams.get('pageSize') as string) - : 25, + page: +queryParams.page, + pageSize: +queryParams.pageSize, }) return NextResponse.json(pagination, { status: 200 }) diff --git a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/route.ts b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/route.ts index f7f5fd42a..3a0314620 100644 --- a/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/route.ts +++ b/apps/web/src/app/api/projects/[projectId]/documents/[documentUuid]/logs/route.ts @@ -1,23 +1,10 @@ -import { - DEFAULT_PAGINATION_SIZE, - LogSources, - Workspace, -} from '@latitude-data/core/browser' +import { Workspace } from '@latitude-data/core/browser' import { computeDocumentLogsQuery } from '@latitude-data/core/services/documentLogs/computeDocumentLogs' import { computeDocumentLogsWithMetadataQuery } from '@latitude-data/core/services/documentLogs/computeDocumentLogsWithMetadata' import { authHandler } from '$/middlewares/authHandler' import { errorHandler } from '$/middlewares/errorHandler' import { NextRequest, NextResponse } from 'next/server' -import { decodeParameters } from '$/services/helpers' - -function parsePage(page: string | null): string { - if (!page) return '1' - - const parsed = parseInt(page, 10) - if (isNaN(parsed)) return '1' - - return parsed < 1 ? '1' : parsed.toString() -} +import { parseApiDocumentLogParams } from '@latitude-data/core/services/documentLogs/index' export const GET = errorHandler( authHandler( @@ -36,41 +23,22 @@ export const GET = errorHandler( ) => { const { documentUuid } = params const searchParams = req.nextUrl.searchParams - const excludeErrors = searchParams.get('excludeErrors') === 'true' - - const page = parsePage(searchParams.get('page')) - const pageSize = - searchParams.get('pageSize') ?? String(DEFAULT_PAGINATION_SIZE) - - const { commitIds: _commitIds, logSources: _logSources } = - decodeParameters(req.nextUrl.search) - const commitIds = (Array.isArray(_commitIds) ? _commitIds : [_commitIds]) - .filter((c) => c !== undefined) - .map(Number) - - const logSources = ( - Array.isArray(_logSources) ? _logSources : [_logSources] - ).filter((l) => l !== undefined) as LogSources[] - - const filterOptions = { - commitIds, - logSources, - } + const queryParams = parseApiDocumentLogParams({ searchParams }) - if (!filterOptions.commitIds.length || !filterOptions.logSources.length) { + if (queryParams.isEmptyResponse) { return NextResponse.json([], { status: 200 }) } - const buildQueryFn = excludeErrors + const buildQueryFn = queryParams.excludeErrors ? computeDocumentLogsQuery : computeDocumentLogsWithMetadataQuery const rows = await buildQueryFn({ workspaceId: workspace.id, documentUuid, - filterOptions, - page, - pageSize, + filterOptions: queryParams.filterOptions, + page: queryParams.page, + pageSize: queryParams.pageSize, }) return NextResponse.json(rows, { status: 200 }) diff --git a/apps/web/src/components/TablePaginationFooter/GoToPageInput/index.tsx b/apps/web/src/components/TablePaginationFooter/GoToPageInput/index.tsx index 4ad554579..0acc69419 100644 --- a/apps/web/src/components/TablePaginationFooter/GoToPageInput/index.tsx +++ b/apps/web/src/components/TablePaginationFooter/GoToPageInput/index.tsx @@ -33,6 +33,7 @@ export function GoToPageInput({ page: targetPage, pageSize, queryParams, + encodeQueryParams: false, }), ) } diff --git a/apps/web/src/hooks/useLogFilterOptions.ts b/apps/web/src/hooks/logFilters/useDefaultLogFilterOptions.ts similarity index 86% rename from apps/web/src/hooks/useLogFilterOptions.ts rename to apps/web/src/hooks/logFilters/useDefaultLogFilterOptions.ts index c518758e6..178b4ec10 100644 --- a/apps/web/src/hooks/useLogFilterOptions.ts +++ b/apps/web/src/hooks/logFilters/useDefaultLogFilterOptions.ts @@ -3,7 +3,7 @@ import { LOG_SOURCES } from '@latitude-data/core/browser' import { useCurrentCommit } from '@latitude-data/web-ui' import { useMemo } from 'react' -export function useFilterOptions() { +export function useDefaultLogFilterOptions() { const { commit } = useCurrentCommit() const { data: commits } = useCommits() return useMemo(() => { @@ -12,6 +12,7 @@ export function useFilterOptions() { ?.filter((c) => !!c.mergedAt || c.uuid === commit.uuid) .map((c) => c.id), logSources: LOG_SOURCES, + createdAt: undefined, } }, [commits, commit]) } diff --git a/apps/web/src/hooks/logFilters/useProcessLogFilters.ts b/apps/web/src/hooks/logFilters/useProcessLogFilters.ts new file mode 100644 index 000000000..9363db264 --- /dev/null +++ b/apps/web/src/hooks/logFilters/useProcessLogFilters.ts @@ -0,0 +1,103 @@ +import { useCallback, useMemo } from 'react' +import { usePathname, useRouter } from 'next/navigation' +import { + DocumentLogFilterOptions, + LOG_FILTERS_ENCODED_PARAMS, + LOG_SOURCES, + LogSources, +} from '@latitude-data/core/browser' +import { ReactStateDispatch } from '@latitude-data/web-ui' +import { paramsToString } from '@latitude-data/core/lib/pagination/buildPaginatedUrl' + +function useEditableSearchParams() { + const router = useRouter() + const pathname = usePathname() + + const setSearchParam = (key: string, value?: string | string[]) => { + const prevParams = window.location.search + const urlParams = new URLSearchParams(prevParams) + const params = Object.fromEntries(urlParams.entries()) + + let newParams = undefined + + if (value) { + const data = { + ...params, + [key]: Array.isArray(value) ? value.join(',') : value, + } + newParams = paramsToString({ + params: data, + paramsToEncode: LOG_FILTERS_ENCODED_PARAMS, + }) + } else { + delete params[key] + newParams = paramsToString({ + params, + paramsToEncode: LOG_FILTERS_ENCODED_PARAMS, + }) + } + + router.push(`${pathname}${newParams ? '?' : ''}${newParams}`) + } + + return setSearchParam +} + +export function useProcessLogFilters({ + onFiltersChanged, + filterOptions, + originalSelectedCommitsIds, +}: { + onFiltersChanged: ReactStateDispatch + filterOptions: DocumentLogFilterOptions + originalSelectedCommitsIds: number[] +}) { + const setSearchParams = useEditableSearchParams() + + const isCommitsDefault = useMemo(() => { + return ( + filterOptions.commitIds.sort().join(',') === + originalSelectedCommitsIds.sort().join(',') + ) + }, [filterOptions.commitIds]) + + const isLogSourcesDefault = useMemo(() => { + return ( + filterOptions.logSources.sort().join(',') === LOG_SOURCES.sort().join(',') + ) + }, [filterOptions.logSources]) + + const onSelectCommits = useCallback((selectedCommitsIds: number[]) => { + onFiltersChanged((currentFilters) => ({ + ...currentFilters, + commitIds: selectedCommitsIds, + })) + if ( + selectedCommitsIds.sort().join(',') === + originalSelectedCommitsIds.sort().join(',') + ) { + setSearchParams('versions', undefined) + } else { + setSearchParams('versions', selectedCommitsIds.map(String)) + } + }, []) + + const onSelectLogSources = useCallback((selectedLogSources: LogSources[]) => { + onFiltersChanged((currentFilters) => ({ + ...currentFilters, + logSources: selectedLogSources, + })) + if (selectedLogSources.sort().join(',') === LOG_SOURCES.sort().join(',')) { + setSearchParams('origins', undefined) + } else { + setSearchParams('origins', selectedLogSources) + } + }, []) + + return { + isCommitsDefault, + isLogSourcesDefault, + onSelectLogSources, + onSelectCommits, + } +} diff --git a/apps/web/src/services/helpers.ts b/apps/web/src/services/helpers.ts deleted file mode 100644 index ae72b313a..000000000 --- a/apps/web/src/services/helpers.ts +++ /dev/null @@ -1,43 +0,0 @@ -type SimpleParam = string | number | boolean -type UriParam = SimpleParam | SimpleParam[] - -function encodeUriParam(value: UriParam) { - if (Array.isArray(value)) return value.map(encodeURIComponent).join(',') - - return encodeURIComponent(value) -} - -export function addParameters( - route: string, - parameters: Record, -): string { - const [path, query] = route.split('?') - const params = [ - ...(query?.split('&') ?? []), - ...Object.entries(parameters) - .filter(([_, v]) => v !== undefined) - .map(([key, value]) => `${key}=${encodeUriParam(value!)}`), - ] - - if (!params.length) return path! - - return `${path}?${params.join('&')}` -} - -export function decodeParameters(route: string): Record { - const [_, query] = route.split('?') - if (!query) return {} - - const params = query.split('&') - return params.reduce((acc: Record, param: string) => { - const [key, value] = param.split('=') - if (!key || !value) return acc - if (value.includes(',')) { - return { - ...acc, - [key]: value.split(',').map(decodeURIComponent), - } - } - return { ...acc, [key]: decodeURIComponent(value) } - }, {}) -} diff --git a/apps/web/src/services/routes/api.ts b/apps/web/src/services/routes/api.ts index 8652f0f2e..0a3ee51a9 100644 --- a/apps/web/src/services/routes/api.ts +++ b/apps/web/src/services/routes/api.ts @@ -1,6 +1,5 @@ import { DocumentLogFilterOptions } from '@latitude-data/core/browser' - -import { addParameters } from '../helpers' +import { generateDocumentLogsApiRouteWithParams } from '@latitude-data/core/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams' type PaginationParameters = { page: number; pageSize: number } @@ -91,13 +90,15 @@ export const _API_ROUTES = { excludeErrors?: boolean filterOptions: DocumentLogFilterOptions }) => - addParameters(`${documentRoot}/logs`, { - page, - pageSize, - excludeErrors, - ...filterOptions, + generateDocumentLogsApiRouteWithParams({ + path: `${documentRoot}/logs`, + params: { + page, + pageSize, + excludeErrors, + filterOptions, + }, }), - pagination: ({ page, pageSize, @@ -107,18 +108,22 @@ export const _API_ROUTES = { excludeErrors?: boolean filterOptions: DocumentLogFilterOptions }) => - addParameters(`${documentRoot}/logs/pagination`, { - page, - pageSize, - excludeErrors, - ...filterOptions, + generateDocumentLogsApiRouteWithParams({ + path: `${documentRoot}/logs/pagination`, + params: { + page, + pageSize, + excludeErrors, + filterOptions, + }, }), - aggregations: (filterOptions: DocumentLogFilterOptions) => - addParameters( - `${documentRoot}/logs/aggregations`, - filterOptions, - ), + generateDocumentLogsApiRouteWithParams({ + path: `${documentRoot}/logs/aggregations`, + params: { + filterOptions, + }, + }), dailyCount: ({ filterOptions, days, @@ -126,9 +131,12 @@ export const _API_ROUTES = { filterOptions: DocumentLogFilterOptions days?: number }) => - addParameters(`${documentRoot}/logs/daily-count`, { - days, - ...filterOptions, + generateDocumentLogsApiRouteWithParams({ + path: `${documentRoot}/logs/daily-count`, + params: { + days, + filterOptions, + }, }), detail: (documentLogUuid: string) => { return { @@ -139,13 +147,13 @@ export const _API_ROUTES = { excludeErrors?: boolean filterOptions: DocumentLogFilterOptions }) => - addParameters( - `${documentRoot}/logs/${documentLogUuid}/position`, - { + generateDocumentLogsApiRouteWithParams({ + path: `${documentRoot}/logs/${documentLogUuid}/position`, + params: { excludeErrors, - ...filterOptions, + filterOptions, }, - ), + }), } }, }, diff --git a/packages/core/package.json b/packages/core/package.json index 083c42b5b..071835d0c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -147,6 +147,7 @@ "zod": "^3.23.8" }, "dependencies": { - "@latitude-data/promptl": "^0.3.2" + "@latitude-data/promptl": "^0.3.2", + "date-fns": "^3.6.0" } } diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 986e18dd9..48d6418ae 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -451,6 +451,7 @@ export enum SpanKind { export type DocumentLogFilterOptions = { commitIds: number[] logSources: LogSources[] + createdAt: { from: Date; to?: Date } | undefined } export const RELATIVE_DATES = { @@ -469,3 +470,4 @@ export const RELATIVE_DATES = { } as const export type RelativeDate = keyof typeof RELATIVE_DATES +export const LOG_FILTERS_ENCODED_PARAMS = ['customIdentifier'] diff --git a/packages/core/src/lib/pagination/buildPaginatedUrl.ts b/packages/core/src/lib/pagination/buildPaginatedUrl.ts index 376b302d0..83b60ddc3 100644 --- a/packages/core/src/lib/pagination/buildPaginatedUrl.ts +++ b/packages/core/src/lib/pagination/buildPaginatedUrl.ts @@ -1,14 +1,63 @@ export type QueryParams = { [key: string]: string | string[] | undefined } -export function parseSearchParams( - searchParams: QueryParams | string | undefined, -) { - if (typeof searchParams === 'string') { - const searchParamsInstance = new URLSearchParams(searchParams) - return Object.fromEntries(searchParamsInstance) +export function paramsToString({ + params, + paramsToEncode = [], +}: { + params: Record | QueryParams | undefined + paramsToEncode?: string[] +}) { + if (!params) return '' + + return Object.entries(params) + .map(([key, value]) => { + if (!value) return undefined + if (Array.isArray(value) && !value.length) return undefined + + if (paramsToEncode.includes(key)) { + return `${key}=${encodeURIComponent(value?.toString() ?? '')}` + } + return `${key}=${value}` + }) + .filter(Boolean) + .join('&') +} + +function parseStringQueryParams(queryParams: string): QueryParams { + const parts = queryParams.split('&') + return parts.reduce((acc, part) => { + const [key, value] = part.split('=') + if (!key) return acc + return { ...acc, [key]: value } + }, {}) +} + +function parseEncodedQueryParams(queryParams: string): QueryParams { + const searchParams = new URLSearchParams(queryParams) + return Object.fromEntries(searchParams) +} + +export function parseSearchParams({ + queryParams, + encodeQueryParams, + pageParams, +}: { + queryParams: QueryParams | string | undefined + encodeQueryParams: boolean + pageParams?: { page: string; pageSize: string } +}) { + if (typeof queryParams === 'string') { + const parsed = encodeQueryParams + ? parseEncodedQueryParams(queryParams) + : parseStringQueryParams(queryParams) + return parsed ? { ...parsed, ...pageParams } : (pageParams ?? {}) + } + + if (typeof queryParams === 'object') { + return queryParams ? { ...queryParams, ...pageParams } : pageParams } - return searchParams ?? {} + return pageParams ?? {} } export function buildPaginatedUrl({ @@ -16,17 +65,24 @@ export function buildPaginatedUrl({ page, pageSize, queryParams, + encodeQueryParams = true, + paramsToEncode = [], }: { baseUrl: string page: number pageSize: number + encodeQueryParams?: boolean queryParams?: QueryParams | string + paramsToEncode?: string[] }) { - const queryString = new URLSearchParams({ - ...parseSearchParams(queryParams), - page: String(page), - pageSize: String(pageSize), - }).toString() + const params = parseSearchParams({ + queryParams, + encodeQueryParams, + pageParams: { + page: String(page), + pageSize: String(pageSize), + }, + }) - return `${baseUrl}?${queryString}` + return `${baseUrl}?${paramsToString({ params, paramsToEncode })}` } diff --git a/packages/core/src/lib/pagination/buildPagination.ts b/packages/core/src/lib/pagination/buildPagination.ts index 51a703d4c..c9a55a955 100644 --- a/packages/core/src/lib/pagination/buildPagination.ts +++ b/packages/core/src/lib/pagination/buildPagination.ts @@ -23,7 +23,10 @@ export function getPaginationParamsWithDefaults({ defaultPaginate?: Exclude searchParams?: QueryParams | string }) { - const params = parseSearchParams(searchParams) + const params = parseSearchParams({ + queryParams: searchParams, + encodeQueryParams: false, + }) return { page: parsePage(params?.page), pageSize: params?.pageSize @@ -38,10 +41,14 @@ export function buildPagination({ queryParams, page, pageSize, + encodeQueryParams = true, + paramsToEncode = [], }: { baseUrl: string count: number queryParams?: QueryParams | string | undefined + encodeQueryParams?: boolean + paramsToEncode?: string[] page: number pageSize: number }) { @@ -61,6 +68,8 @@ export function buildPagination({ page: page - 1, pageSize, queryParams, + encodeQueryParams, + paramsToEncode, }), } : undefined, @@ -73,6 +82,8 @@ export function buildPagination({ page: page + 1, pageSize, queryParams, + encodeQueryParams, + paramsToEncode, }), } : undefined, diff --git a/packages/core/src/lib/pagination/paginate.ts b/packages/core/src/lib/pagination/paginate.ts index 942159c1c..8519e6399 100644 --- a/packages/core/src/lib/pagination/paginate.ts +++ b/packages/core/src/lib/pagination/paginate.ts @@ -59,6 +59,7 @@ export async function paginateQuery({ baseUrl: pageUrl?.base ?? '', count, queryParams: searchParams, + encodeQueryParams: false, page, pageSize, }) diff --git a/packages/core/src/services/documentLogs/computeDocumentLogsAggregations.test.ts b/packages/core/src/services/documentLogs/computeDocumentLogsAggregations.test.ts index 085881271..109dd11e1 100644 --- a/packages/core/src/services/documentLogs/computeDocumentLogsAggregations.test.ts +++ b/packages/core/src/services/documentLogs/computeDocumentLogsAggregations.test.ts @@ -87,6 +87,7 @@ describe('computeDocumentLogsAggregations', () => { filterOptions: { commitIds: [commit.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) diff --git a/packages/core/src/services/documentLogs/computeDocumentLogsDailyCount.test.ts b/packages/core/src/services/documentLogs/computeDocumentLogsDailyCount.test.ts index 03f0ff201..9f23fc5f3 100644 --- a/packages/core/src/services/documentLogs/computeDocumentLogsDailyCount.test.ts +++ b/packages/core/src/services/documentLogs/computeDocumentLogsDailyCount.test.ts @@ -97,6 +97,7 @@ describe('computeDocumentLogsDailyCount', () => { filterOptions: { commitIds: [commit.id], logSources: LOG_SOURCES, + createdAt: undefined, }, days: 3, }) @@ -139,6 +140,7 @@ describe('computeDocumentLogsDailyCount', () => { filterOptions: { commitIds: [commit.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -166,6 +168,7 @@ describe('computeDocumentLogsDailyCount', () => { filterOptions: { commitIds: [commit.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -194,6 +197,7 @@ describe('computeDocumentLogsDailyCount', () => { filterOptions: { commitIds: [commit.id], logSources: LOG_SOURCES, + createdAt: undefined, }, days: 30, }) diff --git a/packages/core/src/services/documentLogs/computeDocumentLogsWithMetadata.test.ts b/packages/core/src/services/documentLogs/computeDocumentLogsWithMetadata.test.ts index 021c08dde..28b1b126c 100644 --- a/packages/core/src/services/documentLogs/computeDocumentLogsWithMetadata.test.ts +++ b/packages/core/src/services/documentLogs/computeDocumentLogsWithMetadata.test.ts @@ -89,6 +89,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit.id, commit2.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -116,6 +117,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit.id, commit2.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -131,6 +133,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit.id, commit2.id], logSources: LOG_SOURCES, + createdAt: undefined, }, page: '1', pageSize: '1', @@ -145,6 +148,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit.id, commit2.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -209,6 +213,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit1.id, commit2.id, draft.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -272,6 +277,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit1.id, draft1.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -307,6 +313,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) @@ -343,6 +350,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit.id], logSources: [], + createdAt: undefined, }, }) @@ -382,6 +390,7 @@ describe('getDocumentLogsWithMetadata', () => { filterOptions: { commitIds: [commit0.id, commit1.id], logSources: LOG_SOURCES, + createdAt: undefined, }, }) diff --git a/packages/core/src/services/documentLogs/index.ts b/packages/core/src/services/documentLogs/index.ts index 29ef05289..9ecc770f1 100644 --- a/packages/core/src/services/documentLogs/index.ts +++ b/packages/core/src/services/documentLogs/index.ts @@ -10,3 +10,4 @@ export * from './evaluate' export * from './computeDocumentLogsAggregations' export * from './fetchDocumentLogsWithEvaluationResults' export * from './generateCsvFromDocumentLogs' +export * from './logsFilterUtils' diff --git a/packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.test.ts b/packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.test.ts new file mode 100644 index 000000000..027bc566a --- /dev/null +++ b/packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.test.ts @@ -0,0 +1,148 @@ +import { describe, it, expect } from 'vitest' +import { formatISO } from 'date-fns' +import { generateDocumentLogsApiRouteWithParams } from './generateDocumentLogsApiRouteWithParams' +import { LogSources } from '../../../constants' + +const mockPath = '/api/logs' + +describe('generateDocumentLogsApiRouteWithParams', () => { + it('should generate a URL with no params', () => { + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: {}, + }) + expect(result).toBe(mockPath) + }) + + it('should handle page and pageSize', () => { + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { page: 1, pageSize: 20 }, + }) + expect(result).toBe(`${mockPath}?page=1&pageSize=20`) + }) + + it('should handle excludeErrors and days', () => { + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { excludeErrors: true, days: 7 }, + }) + expect(result).toBe(`${mockPath}?excludeErrors=true&days=7`) + }) + + it('should handle filterOptions with createdAt (only from date)', () => { + const fromDate = new Date('2024-01-01') + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { + filterOptions: { + createdAt: { from: fromDate, to: undefined }, + commitIds: [], + logSources: [], + }, + }, + }) + expect(result).toBe(`${mockPath}?createdAt=${formatISO(fromDate)}`) + }) + + it('should handle filterOptions with createdAt (from and to date)', () => { + const fromDate = new Date('2024-01-01') + const toDate = new Date('2024-01-10') + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { + filterOptions: { + createdAt: { from: fromDate, to: toDate }, + commitIds: [], + logSources: [], + }, + }, + }) + expect(result).toBe( + `${mockPath}?createdAt=${formatISO(fromDate)},${formatISO(toDate)}`, + ) + }) + + it('should handle filterOptions with empty commitIds and logSources', () => { + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { + filterOptions: { + createdAt: undefined, + commitIds: [], + logSources: [], + }, + }, + }) + expect(result).toBe(`${mockPath}`) + }) + + it('should handle filterOptions with non-empty commitIds and logSources', () => { + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { + filterOptions: { + createdAt: undefined, + commitIds: [123, 456], + logSources: [LogSources.API, LogSources.User], + }, + }, + }) + expect(result).toBe(`${mockPath}?commitIds=123,456&logSources=api,user`) + }) + + it('should handle mixed parameters', () => { + const fromDate = new Date('2024-01-01') + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { + page: 1, + pageSize: 50, + filterOptions: { + createdAt: { from: fromDate, to: undefined }, + commitIds: [789], + logSources: [LogSources.API], + }, + excludeErrors: true, + days: 30, + }, + }) + expect(result).toBe( + `${mockPath}?page=1&pageSize=50&createdAt=${formatISO(fromDate)}&commitIds=789&logSources=api&excludeErrors=true&days=30`, + ) + }) + + it('should ignore undefined or empty values', () => { + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { + page: undefined, + pageSize: 0, + filterOptions: { + createdAt: undefined, + commitIds: [], + logSources: [], + }, + }, + }) + expect(result).toBe(mockPath) + }) + + it('should handle filterOptions with createdAt as empty string', () => { + const result = generateDocumentLogsApiRouteWithParams({ + path: mockPath, + params: { + filterOptions: { + createdAt: { + // @ts-ignore - from and to are required + from: undefined, + to: undefined, + }, + commitIds: [], + logSources: [], + }, + }, + }) + expect(result).toBe(mockPath) + }) +}) diff --git a/packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.ts b/packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.ts new file mode 100644 index 000000000..bfac853e4 --- /dev/null +++ b/packages/core/src/services/documentLogs/logsFilterUtils/generateDocumentLogsApiRouteWithParams.ts @@ -0,0 +1,77 @@ +import { formatISO } from 'date-fns' +import { + DocumentLogFilterOptions, + LOG_FILTERS_ENCODED_PARAMS, +} from '../../../constants' + +function addToAcc(acc: string[], key: string, value: unknown) { + if (!value) return acc + if (Array.isArray(value) && !value.length) return acc + + acc.push(`${key}=${value}`) + return acc +} + +function processFilterOptions(filterOptions: DocumentLogFilterOptions) { + return Object.keys(filterOptions).reduce((acc, key) => { + switch (key) { + case 'createdAt': { + const createdAt = filterOptions.createdAt + if (!createdAt) return acc + const { from, to } = createdAt + const fromStr = from ? formatISO(from) : undefined + const toStr = to ? formatISO(to) : undefined + if (!fromStr) return acc + + const dateParts = [fromStr] + + if (toStr) dateParts.push(toStr) + + acc.push(`createdAt=${dateParts.join(',')}`) + return acc + } + default: { + return addToAcc( + acc, + key, + filterOptions[key as keyof DocumentLogFilterOptions], + ) + } + } + }, [] as string[]) +} + +export function generateDocumentLogsApiRouteWithParams({ + path, + params, + paramsToEncode: _pe = LOG_FILTERS_ENCODED_PARAMS, +}: { + path: string + paramsToEncode?: string[] + params: { + filterOptions?: DocumentLogFilterOptions + page?: number + pageSize?: number + excludeErrors?: boolean + days?: number | undefined + } +}) { + const parts = Object.keys(params).reduce((acc, key) => { + switch (key) { + case 'filterOptions': { + const filterParts = processFilterOptions( + params[key] as DocumentLogFilterOptions, + ) + acc.push(...filterParts) + return acc + } + default: { + return addToAcc(acc, key, params[key as keyof typeof params]) + } + } + }, [] as string[]) + + if (!parts.length) return path + + return `${path}?${parts.join('&')}` +} diff --git a/packages/core/src/services/documentLogs/logsFilterUtils/index.ts b/packages/core/src/services/documentLogs/logsFilterUtils/index.ts new file mode 100644 index 000000000..82b1a2c40 --- /dev/null +++ b/packages/core/src/services/documentLogs/logsFilterUtils/index.ts @@ -0,0 +1,3 @@ +export * from './parseLogFilterParams' +export * from './parseApiLogFilterParams' +export * from './generateDocumentLogsApiRouteWithParams' diff --git a/packages/core/src/services/documentLogs/logsFilterUtils/parseApiLogFilterParams.ts b/packages/core/src/services/documentLogs/logsFilterUtils/parseApiLogFilterParams.ts new file mode 100644 index 000000000..e48c15bd5 --- /dev/null +++ b/packages/core/src/services/documentLogs/logsFilterUtils/parseApiLogFilterParams.ts @@ -0,0 +1,48 @@ +import { DEFAULT_PAGINATION_SIZE, LogSources } from '../../../constants' +import { parseSafeCreatedAtRange } from './parseLogFilterParams' + +function parsePage(page: string | null): string { + if (!page) return '1' + + const parsed = parseInt(page, 10) + if (isNaN(parsed)) return '1' + + return parsed < 1 ? '1' : parsed.toString() +} + +function parseCommitIds(commitIds: string | undefined) { + return commitIds?.split(',')?.map?.(Number) ?? [] +} + +function parseLogSources(logSources: string | undefined) { + return (logSources?.split?.(',') as LogSources[]) ?? [] +} + +export function parseApiDocumentLogParams({ + searchParams, +}: { + searchParams: URLSearchParams +}) { + const params = Object.fromEntries(searchParams.entries()) + const commitIds = parseCommitIds(params.commitIds) + const logSources = parseLogSources(params.logSources) + const excludeErrors = searchParams.get('excludeErrors') === 'true' + + const filterOptions = { + commitIds, + logSources, + createdAt: parseSafeCreatedAtRange(params.createdAt), + } + + const isEmptyResponse = + filterOptions.commitIds.length === 0 || + filterOptions.logSources.length === 0 + + return { + excludeErrors, + isEmptyResponse, + filterOptions, + page: parsePage(searchParams.get('page')), + pageSize: searchParams.get('pageSize') ?? String(DEFAULT_PAGINATION_SIZE), + } +} diff --git a/packages/core/src/services/documentLogs/logsFilterUtils/parseLogFilterParams.ts b/packages/core/src/services/documentLogs/logsFilterUtils/parseLogFilterParams.ts new file mode 100644 index 000000000..d6bde5dd0 --- /dev/null +++ b/packages/core/src/services/documentLogs/logsFilterUtils/parseLogFilterParams.ts @@ -0,0 +1,50 @@ +import { Commit, LOG_SOURCES, LogSources } from '../../../browser' +import { QueryParams } from '../../../lib/pagination/buildPaginatedUrl' + +function parseLogSourcesSafe(origins: string[] | string | undefined) { + const logSources = (origins?.toString()?.split(',') ?? + LOG_SOURCES) as LogSources[] + return logSources.filter((s) => LOG_SOURCES.includes(s as LogSources)) +} + +export function parseSafeCreatedAtRange( + createdAt: string[] | string | undefined, +) { + const [from, to] = createdAt?.toString()?.split(',') ?? [] + if (!from || !to) return undefined + + return { from: new Date(from), to: new Date(to) } +} + +export function parseLogFiltersParams({ + params, + currentCommit, + commits, +}: { + params: QueryParams + currentCommit: Commit + commits: Commit[] +}) { + const originalSelectedCommitsIds = [ + ...commits.filter((c) => !!c.mergedAt).map((c) => c.id), + ...(!currentCommit.mergedAt ? [currentCommit.id] : []), + ] + const { versions, origins, createdAt: createdAtParam } = params + const commitIds = + versions?.toString()?.split(',')?.map(Number) ?? originalSelectedCommitsIds + const logSources = parseLogSourcesSafe(origins) + const createdAt = parseSafeCreatedAtRange(createdAtParam) + + return { + filterOptions: { + commitIds, + logSources, + createdAt, + }, + originalSelectedCommitsIds, + redirectUrlParams: [ + versions ? `versions=${versions}` : undefined, + origins ? `origins=${origins}` : undefined, + ], + } +} diff --git a/packages/web-ui/src/ds/atoms/Checkbox/Primitive/index.tsx b/packages/web-ui/src/ds/atoms/Checkbox/Primitive/index.tsx index 7e76d0f52..818671bae 100644 --- a/packages/web-ui/src/ds/atoms/Checkbox/Primitive/index.tsx +++ b/packages/web-ui/src/ds/atoms/Checkbox/Primitive/index.tsx @@ -43,7 +43,7 @@ const CheckboxAtom = forwardRef< className={cn('flex items-center justify-center text-current')} > {props.checked === 'indeterminate' && ( - + )} {props.checked === true && } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46b3e1cca..397c5233a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -350,7 +350,7 @@ importers: version: 0.1.65 '@sentry/nextjs': specifier: ^8.34.0 - version: 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.95.0(esbuild@0.19.12)) + version: 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.95.0) '@sentry/utils': specifier: ^8.30.0 version: 8.35.0 @@ -674,6 +674,9 @@ importers: '@latitude-data/promptl': specifier: ^0.3.2 version: 0.3.2 + date-fns: + specifier: ^3.6.0 + version: 3.6.0 devDependencies: '@ai-sdk/anthropic': specifier: ^0.0.48 @@ -2413,8 +2416,8 @@ packages: '@floating-ui/react-dom@2.1.2': resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' + react: '>=16.8.0 || >=18.x' + react-dom: '>=16.8.0 || >=18.x' '@floating-ui/utils@0.2.8': resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} @@ -2719,8 +2722,8 @@ packages: resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==} peerDependencies: monaco-editor: '>= 0.25.0 < 1' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=18.x + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=18.x '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} @@ -3615,8 +3618,8 @@ packages: peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x peerDependenciesMeta: '@types/react': optional: true @@ -3671,7 +3674,7 @@ packages: resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} peerDependencies: '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x peerDependenciesMeta: '@types/react': optional: true @@ -3817,8 +3820,8 @@ packages: peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x peerDependenciesMeta: '@types/react': optional: true @@ -3830,8 +3833,8 @@ packages: peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x peerDependenciesMeta: '@types/react': optional: true @@ -4050,7 +4053,7 @@ packages: resolution: {integrity: sha512-RcBoffx2IZG6quLBXo5sj3fF47rKmmkiMhG1ZBua4nFjHYlmW8j1uUMyO5HNglxIF9E52NYq4sF7XeZRp9jYjg==} engines: {node: '>=18.0.0'} peerDependencies: - react: ^18.0 || ^19.0 || ^19.0.0-rc + react: ^18.0 || ^19.0 || ^19.0.0-rc || >=18.x '@react-email/container@0.0.14': resolution: {integrity: sha512-NgoaJJd9tTtsrveL86Ocr/AYLkGyN3prdXKd/zm5fQpfDhy/NXezyT3iF6VlwAOEUIu64ErHpAJd+P6ygR+vjg==} @@ -5062,7 +5065,7 @@ packages: engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 + eslint: ^8.56.0 || >=8.x typescript: '*' peerDependenciesMeta: typescript: @@ -5163,7 +5166,7 @@ packages: resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || >=8.x '@typescript-eslint/utils@6.21.0': resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} @@ -5197,7 +5200,7 @@ packages: engines: {node: '>=16'} peerDependencies: '@next/eslint-plugin-next': '>=12.3.0 <15' - eslint: '>=8.48.0 <9' + eslint: '>=8.48.0 <9 || >=8.x' prettier: '>=3.0.0 <4' typescript: '>=4.8.0 <6' peerDependenciesMeta: @@ -15453,7 +15456,7 @@ snapshots: '@sentry/types': 8.35.0 '@sentry/utils': 8.35.0 - '@sentry/nextjs@8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.95.0(esbuild@0.19.12))': + '@sentry/nextjs@8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.0.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923)(webpack@5.95.0)': dependencies: '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 @@ -15466,14 +15469,14 @@ snapshots: '@sentry/types': 8.35.0 '@sentry/utils': 8.35.0 '@sentry/vercel-edge': 8.35.0 - '@sentry/webpack-plugin': 2.22.3(encoding@0.1.13)(webpack@5.95.0(esbuild@0.19.12)) + '@sentry/webpack-plugin': 2.22.3(encoding@0.1.13)(webpack@5.95.0) chalk: 3.0.0 next: 15.0.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0-rc-5d19e1c8-20240923(react@19.0.0-rc-5d19e1c8-20240923))(react@19.0.0-rc-5d19e1c8-20240923) resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 optionalDependencies: - webpack: 5.95.0(esbuild@0.19.12) + webpack: 5.95.0 transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/core' @@ -15555,12 +15558,12 @@ snapshots: '@sentry/types': 8.35.0 '@sentry/utils': 8.35.0 - '@sentry/webpack-plugin@2.22.3(encoding@0.1.13)(webpack@5.95.0(esbuild@0.19.12))': + '@sentry/webpack-plugin@2.22.3(encoding@0.1.13)(webpack@5.95.0)': dependencies: '@sentry/bundler-plugin-core': 2.22.3(encoding@0.1.13) unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.95.0(esbuild@0.19.12) + webpack: 5.95.0 transitivePeerDependencies: - encoding - supports-color @@ -23493,16 +23496,14 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - terser-webpack-plugin@5.3.10(esbuild@0.19.12)(webpack@5.95.0(esbuild@0.19.12)): + terser-webpack-plugin@5.3.10(webpack@5.95.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.36.0 - webpack: 5.95.0(esbuild@0.19.12) - optionalDependencies: - esbuild: 0.19.12 + webpack: 5.95.0 terser@5.36.0: dependencies: @@ -24477,7 +24478,7 @@ snapshots: webpack-virtual-modules@0.5.0: {} - webpack@5.95.0(esbuild@0.19.12): + webpack@5.95.0: dependencies: '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.14.1 @@ -24499,7 +24500,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.19.12)(webpack@5.95.0(esbuild@0.19.12)) + terser-webpack-plugin: 5.3.10(webpack@5.95.0) watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: