mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-10 23:48:26 +00:00
Feat: Improve recall efficiency (#38)
* Feat: Improve recall efficiency Feat: add extension search API * Feat: add summary to extension
This commit is contained in:
parent
2fc3d20cf1
commit
026e2f2cbe
139
apps/webapp/app/routes/api.v1.extension-search.tsx
Normal file
139
apps/webapp/app/routes/api.v1.extension-search.tsx
Normal file
@ -0,0 +1,139 @@
|
||||
import { z } from "zod";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
import { SearchService } from "~/services/search.server";
|
||||
import { makeModelCall } from "~/lib/model.server";
|
||||
import { json } from "@remix-run/node";
|
||||
import type { CoreMessage } from "ai";
|
||||
|
||||
export const ExtensionSearchBodyRequest = z.object({
|
||||
input: z.string().min(1, "Input text is required"),
|
||||
limit: z.number().optional().default(20),
|
||||
maxBfsDepth: z.number().optional(),
|
||||
includeInvalidated: z.boolean().optional(),
|
||||
entityTypes: z.array(z.string()).optional(),
|
||||
scoreThreshold: z.number().optional(),
|
||||
minResults: z.number().optional(),
|
||||
});
|
||||
|
||||
const searchService = new SearchService();
|
||||
|
||||
/**
|
||||
* Generate multiple search queries from user input using LLM
|
||||
*/
|
||||
async function generateSearchQueries(userInput: string): Promise<string[]> {
|
||||
const messages: CoreMessage[] = [
|
||||
{
|
||||
role: "system",
|
||||
content: `You are my personal memory assistant. I'm writing something and need you to help me recall relevant information from my past conversations, notes, and experiences that might be useful for what I'm currently working on.
|
||||
|
||||
Based on what I'm typing, think about what information from my memory would be most helpful:
|
||||
- What have I discussed before that relates to this topic?
|
||||
- What context, decisions, or insights might I need to remember?
|
||||
- What related work, people, or concepts should I be aware of?
|
||||
- What problems or solutions have I encountered that are similar?
|
||||
- What background information would help me with this task?
|
||||
|
||||
Generate 3-5 specific search queries that will help me find the most relevant memories and context for my current work. Think like you're helping me remember things I might have forgotten or overlooked.
|
||||
|
||||
Return the JSON array of strings wrapped in <output></output> tags. Each string should be a search query.
|
||||
|
||||
Format: <output>["query1", "query2", "query3"]</output>
|
||||
|
||||
Example input: "working on the user authentication feature"
|
||||
Example output: ["user authentication implementation", "login flow discussion", "authentication security concerns", "user session management", "auth token handling"]`,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: userInput,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
const response = await makeModelCall(
|
||||
false,
|
||||
messages,
|
||||
() => {}, // onFinish callback
|
||||
{ temperature: 0.3 }
|
||||
);
|
||||
|
||||
// Extract content from <output> tags and parse JSON
|
||||
const outputMatch = (response as string).match(/<output>(.*?)<\/output>/s);
|
||||
if (!outputMatch) {
|
||||
throw new Error("No output tags found in LLM response");
|
||||
}
|
||||
|
||||
const queries = JSON.parse(outputMatch[1].trim());
|
||||
|
||||
// Validate that we got an array of strings
|
||||
if (!Array.isArray(queries) || !queries.every(q => typeof q === 'string')) {
|
||||
throw new Error("Invalid response format from LLM");
|
||||
}
|
||||
|
||||
return queries.slice(0, 5); // Limit to max 5 queries
|
||||
} catch (error) {
|
||||
console.error("Error generating search queries:", error);
|
||||
// Fallback: use the original input as a single query
|
||||
return [userInput];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deduplicate facts and episodes from multiple search results
|
||||
*/
|
||||
function deduplicateResults(results: Array<{ episodes: string[]; facts: string[] }>) {
|
||||
const uniqueFacts = new Set<string>();
|
||||
const uniqueEpisodes = new Set<string>();
|
||||
|
||||
for (const result of results) {
|
||||
result.facts.forEach(fact => uniqueFacts.add(fact));
|
||||
result.episodes.forEach(episode => uniqueEpisodes.add(episode));
|
||||
}
|
||||
|
||||
return {
|
||||
facts: Array.from(uniqueFacts),
|
||||
episodes: Array.from(uniqueEpisodes),
|
||||
};
|
||||
}
|
||||
|
||||
const { action, loader } = createActionApiRoute(
|
||||
{
|
||||
body: ExtensionSearchBodyRequest,
|
||||
allowJWT: true,
|
||||
authorization: {
|
||||
action: "search",
|
||||
},
|
||||
corsStrategy: "all",
|
||||
},
|
||||
async ({ body, authentication }) => {
|
||||
// Generate multiple search queries from user input
|
||||
const searchQueries = await generateSearchQueries(body.input);
|
||||
|
||||
// Execute all search queries in parallel
|
||||
const searchResults = await Promise.all(
|
||||
searchQueries.map(query =>
|
||||
searchService.search(query, authentication.userId, {
|
||||
limit: Math.ceil(body.limit / searchQueries.length), // Distribute limit across queries
|
||||
maxBfsDepth: body.maxBfsDepth,
|
||||
includeInvalidated: body.includeInvalidated,
|
||||
entityTypes: body.entityTypes,
|
||||
scoreThreshold: body.scoreThreshold,
|
||||
minResults: body.minResults,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Deduplicate and combine results
|
||||
const combinedResults = deduplicateResults(searchResults);
|
||||
|
||||
// Limit final results if they exceed the requested limit
|
||||
const finalResults = {
|
||||
facts: combinedResults.facts.slice(0, body.limit),
|
||||
episodes: combinedResults.episodes.slice(0, body.limit),
|
||||
queries_used: searchQueries, // Include the generated queries for debugging
|
||||
};
|
||||
|
||||
return json(finalResults);
|
||||
},
|
||||
);
|
||||
|
||||
export { action, loader };
|
||||
216
apps/webapp/app/routes/api.v1.extension-summary.tsx
Normal file
216
apps/webapp/app/routes/api.v1.extension-summary.tsx
Normal file
@ -0,0 +1,216 @@
|
||||
import { z } from "zod";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
import { makeModelCall } from "~/lib/model.server";
|
||||
import { json } from "@remix-run/node";
|
||||
import type { CoreMessage } from "ai";
|
||||
import * as cheerio from 'cheerio';
|
||||
|
||||
export const ExtensionSummaryBodyRequest = z.object({
|
||||
html: z.string().min(1, "HTML content is required"),
|
||||
url: z.string().url("Valid URL is required"),
|
||||
title: z.string().optional(),
|
||||
});
|
||||
|
||||
export type PageType = "text" | "video";
|
||||
|
||||
interface ContentExtractionResult {
|
||||
pageType: PageType;
|
||||
title: string;
|
||||
content: string;
|
||||
metadata: {
|
||||
url: string;
|
||||
wordCount: number;
|
||||
};
|
||||
supported: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if page contains video content
|
||||
*/
|
||||
function isVideoPage(url: string, $: cheerio.CheerioAPI): boolean {
|
||||
const hostname = new URL(url).hostname.toLowerCase();
|
||||
|
||||
// Known video platforms
|
||||
if (hostname.includes('youtube.com') || hostname.includes('youtu.be') ||
|
||||
hostname.includes('vimeo.com') || hostname.includes('twitch.tv') ||
|
||||
hostname.includes('tiktok.com')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generic video content detection
|
||||
const videoElements = $('video').length;
|
||||
const videoPlayers = $('.video-player, [class*="video-player"], [data-testid*="video"]').length;
|
||||
|
||||
// If there are multiple video indicators, likely a video-focused page
|
||||
return videoElements > 0 || videoPlayers > 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all text content from any webpage
|
||||
*/
|
||||
function extractTextContent($: cheerio.CheerioAPI, url: string): ContentExtractionResult {
|
||||
// Extract title from multiple possible locations
|
||||
const title = $('title').text() ||
|
||||
$('meta[property="og:title"]').attr('content') ||
|
||||
$('meta[name="title"]').attr('content') ||
|
||||
$('h1').first().text() ||
|
||||
'Untitled Page';
|
||||
|
||||
// Check if this is primarily a video page
|
||||
const isVideo = isVideoPage(url, $);
|
||||
const pageType: PageType = isVideo ? "video" : "text";
|
||||
|
||||
let content = '';
|
||||
|
||||
if (isVideo) {
|
||||
// For video pages, try to get description/transcript text
|
||||
content = $('#description, .video-description, .description').text() ||
|
||||
$('meta[name="description"]').attr('content') ||
|
||||
$('[class*="transcript"], [class*="caption"]').text() ||
|
||||
'Video content detected - text summarization not available';
|
||||
} else {
|
||||
// Simple universal text extraction
|
||||
// Remove non-content elements
|
||||
$('script, style, noscript, nav, header, footer').remove();
|
||||
|
||||
// Get all text content
|
||||
const allText = $('body').text();
|
||||
|
||||
// Split into sentences and filter for meaningful content
|
||||
const sentences = allText
|
||||
.split(/[.!?]+/)
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 20) // Keep sentences with substance
|
||||
.filter(s => !/^(click|menu|button|nav|home|search|login|signup|subscribe)$/i.test(s.toLowerCase())) // Remove UI text
|
||||
.filter(s => s.split(' ').length > 3); // Keep sentences with multiple words
|
||||
|
||||
content = sentences.join('. ').slice(0, 10000);
|
||||
}
|
||||
|
||||
// Clean up whitespace and normalize text
|
||||
content = content.replace(/\s+/g, ' ').trim();
|
||||
|
||||
const wordCount = content.split(/\s+/).filter(word => word.length > 0).length;
|
||||
const supported = !isVideo && content.length > 50;
|
||||
|
||||
return {
|
||||
pageType,
|
||||
title: title.trim(),
|
||||
content: content.slice(0, 10000), // Limit content size for processing
|
||||
metadata: {
|
||||
url,
|
||||
wordCount,
|
||||
},
|
||||
supported,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate summary using LLM
|
||||
*/
|
||||
async function generateSummary(title: string, content: string): Promise<string> {
|
||||
const messages: CoreMessage[] = [
|
||||
{
|
||||
role: "system",
|
||||
content: `You are a helpful assistant that creates concise summaries of web content.
|
||||
|
||||
Create a clear, informative summary that captures the key points and main ideas from the provided content. The summary should:
|
||||
- Focus on the most important information and key takeaways
|
||||
- Be concise but comprehensive
|
||||
- Maintain the original context and meaning
|
||||
- Be useful for someone who wants to quickly understand the content
|
||||
|
||||
Extract the essential information while preserving important details, facts, or insights.`,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `Title: ${title}
|
||||
Content: ${content}
|
||||
|
||||
Please provide a concise summary of this content.`,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
const response = await makeModelCall(
|
||||
false,
|
||||
messages,
|
||||
() => {}, // onFinish callback
|
||||
{ temperature: 0.3 }
|
||||
);
|
||||
|
||||
return response as string;
|
||||
} catch (error) {
|
||||
console.error("Error generating summary:", error);
|
||||
return "Unable to generate summary at this time.";
|
||||
}
|
||||
}
|
||||
|
||||
const { action, loader } = createActionApiRoute(
|
||||
{
|
||||
body: ExtensionSummaryBodyRequest,
|
||||
allowJWT: true,
|
||||
authorization: {
|
||||
action: "search",
|
||||
},
|
||||
corsStrategy: "all",
|
||||
},
|
||||
async ({ body }) => {
|
||||
try {
|
||||
const $ = cheerio.load(body.html);
|
||||
|
||||
// Extract content from any webpage
|
||||
const extraction = extractTextContent($, body.url);
|
||||
|
||||
// Override title if provided
|
||||
if (body.title) {
|
||||
extraction.title = body.title;
|
||||
}
|
||||
|
||||
let summary = '';
|
||||
|
||||
if (extraction.supported && extraction.content.length > 0) {
|
||||
// Generate summary for text content
|
||||
summary = await generateSummary(extraction.title, extraction.content);
|
||||
} else {
|
||||
// Handle unsupported content types
|
||||
if (extraction.pageType === "video") {
|
||||
summary = "Video content detected. Text summarization not available for video-focused pages.";
|
||||
} else {
|
||||
summary = "Unable to extract sufficient text content for summarization.";
|
||||
}
|
||||
}
|
||||
|
||||
const response = {
|
||||
success: true,
|
||||
pageType: extraction.pageType,
|
||||
title: extraction.title,
|
||||
summary,
|
||||
content: extraction.content.slice(0, 1000), // Return first 1000 chars of content
|
||||
supported: extraction.supported,
|
||||
metadata: extraction.metadata,
|
||||
};
|
||||
|
||||
return json(response);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error processing extension summary request:", error);
|
||||
|
||||
return json({
|
||||
success: false,
|
||||
error: "Failed to process page content",
|
||||
pageType: "text" as PageType,
|
||||
title: body.title || "Error",
|
||||
summary: "Unable to process this page content.",
|
||||
content: "",
|
||||
supported: false,
|
||||
metadata: {
|
||||
url: body.url,
|
||||
wordCount: 0,
|
||||
},
|
||||
}, { status: 500 });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export { action, loader };
|
||||
@ -205,24 +205,24 @@ export async function updateStatementsWithNewEntity(
|
||||
const queries = [
|
||||
// Update statements where old entity is the subject
|
||||
`
|
||||
MATCH (oldEntity:Entity {uuid: $oldEntityUUID})-[:SUBJECT]->(statement:Statement)
|
||||
MATCH (oldEntity:Entity {uuid: $oldEntityUUID})-[r:SUBJECT]->(statement:Statement)
|
||||
MATCH (newEntity:Entity {uuid: $newEntityUUID})
|
||||
DELETE oldEntity-[:SUBJECT]->statement
|
||||
CREATE newEntity-[:SUBJECT]->statement
|
||||
DELETE r
|
||||
CREATE (newEntity)-[:SUBJECT]->(statement)
|
||||
`,
|
||||
// Update statements where old entity is the predicate
|
||||
`
|
||||
MATCH (oldEntity:Entity {uuid: $oldEntityUUID})-[:PREDICATE]->(statement:Statement)
|
||||
MATCH (oldEntity:Entity {uuid: $oldEntityUUID})-[r:PREDICATE]->(statement:Statement)
|
||||
MATCH (newEntity:Entity {uuid: $newEntityUUID})
|
||||
DELETE oldEntity-[:PREDICATE]->statement
|
||||
CREATE newEntity-[:PREDICATE]->statement
|
||||
DELETE r
|
||||
CREATE (newEntity)-[:PREDICATE]->(statement)
|
||||
`,
|
||||
// Update statements where old entity is the object
|
||||
`
|
||||
MATCH (oldEntity:Entity {uuid: $oldEntityUUID})-[:OBJECT]->(statement:Statement)
|
||||
MATCH (oldEntity:Entity {uuid: $oldEntityUUID})-[r:OBJECT]->(statement:Statement)
|
||||
MATCH (newEntity:Entity {uuid: $newEntityUUID})
|
||||
DELETE oldEntity-[:OBJECT]->statement
|
||||
CREATE newEntity-[:OBJECT]->statement
|
||||
DELETE r
|
||||
CREATE (newEntity)-[:OBJECT]->(statement)
|
||||
`,
|
||||
];
|
||||
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import type { EpisodicNode, StatementNode } from "@core/types";
|
||||
import { logger } from "./logger.service";
|
||||
import { applyCrossEncoderReranking, applyWeightedRRF } from "./search/rerank";
|
||||
import {
|
||||
applyCrossEncoderReranking,
|
||||
applyMultiFactorReranking,
|
||||
applyMultiFactorMMRReranking,
|
||||
applyWeightedRRF,
|
||||
} from "./search/rerank";
|
||||
import {
|
||||
getEpisodesByStatements,
|
||||
performBfsSearch,
|
||||
@ -118,6 +123,12 @@ export class SearchService {
|
||||
score = (result as any).crossEncoderScore;
|
||||
} else if ((result as any).finalScore !== undefined) {
|
||||
score = (result as any).finalScore;
|
||||
} else if ((result as any).multifactorScore !== undefined) {
|
||||
score = (result as any).multifactorScore;
|
||||
} else if ((result as any).combinedScore !== undefined) {
|
||||
score = (result as any).combinedScore;
|
||||
} else if ((result as any).mmrScore !== undefined) {
|
||||
score = (result as any).mmrScore;
|
||||
}
|
||||
|
||||
return { result, score };
|
||||
@ -141,19 +152,27 @@ export class SearchService {
|
||||
|
||||
let threshold = 0;
|
||||
if (isRRF || scoreRange < 0.01) {
|
||||
// For RRF or other compressed score ranges, use a percentile-based approach
|
||||
// Keep top 70% (or whatever is specified in options) of results
|
||||
const keepPercentage = 1 - (options.scoreThreshold || 0.3);
|
||||
const keepCount = Math.max(
|
||||
1,
|
||||
Math.ceil(scoredResults.length * keepPercentage),
|
||||
);
|
||||
// For RRF scores, use a more lenient adaptive approach
|
||||
// Calculate median score and use a dynamic threshold based on score distribution
|
||||
const sortedScores = [...scores].sort((a, b) => b - a);
|
||||
const medianIndex = Math.floor(sortedScores.length / 2);
|
||||
const medianScore = sortedScores[medianIndex];
|
||||
|
||||
// Set threshold to the score of the last item we want to keep
|
||||
threshold =
|
||||
keepCount < scoredResults.length
|
||||
? scoredResults[keepCount - 1].score
|
||||
: 0;
|
||||
// Use the smaller of: 20% of max score or 50% of median score
|
||||
// This is more lenient for broad queries while still filtering noise
|
||||
const maxBasedThreshold = maxScore * 0.2;
|
||||
const medianBasedThreshold = medianScore * 0.5;
|
||||
threshold = Math.min(maxBasedThreshold, medianBasedThreshold);
|
||||
|
||||
// Ensure we keep at least minResults if available
|
||||
const minResultsCount = Math.min(
|
||||
options.minResults,
|
||||
scoredResults.length,
|
||||
);
|
||||
if (scoredResults.length >= minResultsCount) {
|
||||
const minResultsThreshold = scoredResults[minResultsCount - 1].score;
|
||||
threshold = Math.min(threshold, minResultsThreshold);
|
||||
}
|
||||
} else {
|
||||
// For normal score distributions, use the relative threshold approach
|
||||
const relativeThreshold = options.scoreThreshold || 0.3;
|
||||
@ -216,8 +235,11 @@ export class SearchService {
|
||||
return applyCrossEncoderReranking(query, results);
|
||||
}
|
||||
|
||||
// Otherwise use weighted RRF for multiple sources
|
||||
return applyWeightedRRF(results);
|
||||
// Otherwise use combined MultiFactorReranking + MMR for multiple sources
|
||||
return applyMultiFactorMMRReranking(results, {
|
||||
lambda: 0.7, // Balance relevance (0.7) vs diversity (0.3)
|
||||
maxResults: options.limit > 0 ? options.limit * 2 : 100, // Get more results for filtering
|
||||
});
|
||||
}
|
||||
|
||||
private async logRecallAsync(
|
||||
|
||||
@ -4,6 +4,21 @@ import { type CoreMessage } from "ai";
|
||||
import { makeModelCall } from "~/lib/model.server";
|
||||
import { logger } from "../logger.service";
|
||||
|
||||
// Utility function to safely convert BigInt values to Number
|
||||
function safeNumber(value: any): number {
|
||||
if (typeof value === 'bigint') {
|
||||
return Number(value);
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const parsed = parseFloat(value);
|
||||
return isNaN(parsed) ? 0 : parsed;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply Weighted Reciprocal Rank Fusion to combine results
|
||||
*/
|
||||
@ -59,6 +74,134 @@ export function applyWeightedRRF(results: {
|
||||
return sortedResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply MMR (Maximal Marginal Relevance) reranking to reduce redundancy while maintaining relevance
|
||||
* MMR balances relevance and diversity to prevent redundant fact statements in results
|
||||
*/
|
||||
export function applyMMRReranking(
|
||||
statements: StatementNode[],
|
||||
lambda: number = 0.7, // Balance between relevance (1.0) and diversity (0.0)
|
||||
maxResults: number = 50
|
||||
): StatementNode[] {
|
||||
if (statements.length === 0) return [];
|
||||
|
||||
// Extract relevance scores and embeddings
|
||||
const candidates = statements.map((statement) => {
|
||||
let relevanceScore = 0;
|
||||
|
||||
// Use existing scores from MultiFactorReranking or other sources
|
||||
if ((statement as any).multifactorScore !== undefined) {
|
||||
relevanceScore = safeNumber((statement as any).multifactorScore);
|
||||
} else if ((statement as any).rrfScore !== undefined) {
|
||||
relevanceScore = safeNumber((statement as any).rrfScore);
|
||||
} else if ((statement as any).crossEncoderScore !== undefined) {
|
||||
relevanceScore = safeNumber((statement as any).crossEncoderScore);
|
||||
} else if ((statement as any).finalScore !== undefined) {
|
||||
relevanceScore = safeNumber((statement as any).finalScore);
|
||||
}
|
||||
|
||||
return {
|
||||
statement,
|
||||
relevanceScore,
|
||||
embedding: statement.factEmbedding || [],
|
||||
selected: false
|
||||
};
|
||||
});
|
||||
|
||||
// Sort by relevance score (descending)
|
||||
candidates.sort((a, b) => b.relevanceScore - a.relevanceScore);
|
||||
|
||||
const selectedCandidates: typeof candidates = [];
|
||||
const remainingCandidates = [...candidates];
|
||||
|
||||
// Pre-filter candidates with no embeddings for faster processing
|
||||
const candidatesWithEmbeddings = remainingCandidates.filter(c => c.embedding.length > 0);
|
||||
const candidatesWithoutEmbeddings = remainingCandidates.filter(c => c.embedding.length === 0);
|
||||
|
||||
// MMR Selection Algorithm with optimizations
|
||||
while (selectedCandidates.length < maxResults && remainingCandidates.length > 0) {
|
||||
let bestCandidate = null;
|
||||
let bestScore = -Infinity;
|
||||
let bestIndex = -1;
|
||||
|
||||
// Early termination: if we have enough high-relevance items, stop diversity checking
|
||||
const relevanceThreshold = selectedCandidates.length > 0 ?
|
||||
selectedCandidates[selectedCandidates.length - 1].relevanceScore * 0.5 : 0;
|
||||
|
||||
for (let i = 0; i < remainingCandidates.length; i++) {
|
||||
const candidate = remainingCandidates[i];
|
||||
|
||||
// Skip similarity calculation for very low relevance items
|
||||
if (candidate.relevanceScore < relevanceThreshold && selectedCandidates.length > 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let maxSimilarityToSelected = 0;
|
||||
|
||||
// Only calculate similarity if candidate has embedding and we have selected items
|
||||
if (selectedCandidates.length > 0 && candidate.embedding.length > 0) {
|
||||
// Optimization: only check similarity with most recent selected items (last 5)
|
||||
const recentSelected = selectedCandidates.slice(-Math.min(5, selectedCandidates.length));
|
||||
|
||||
for (const selected of recentSelected) {
|
||||
if (selected.embedding.length > 0) {
|
||||
const similarity = cosineSimilarity(candidate.embedding, selected.embedding);
|
||||
maxSimilarityToSelected = Math.max(maxSimilarityToSelected, similarity);
|
||||
|
||||
// Early exit: if similarity is very high, no need to check more
|
||||
if (similarity > 0.95) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MMR Score: λ * relevance - (1-λ) * max_similarity_to_selected
|
||||
const mmrScore = lambda * candidate.relevanceScore - (1 - lambda) * maxSimilarityToSelected;
|
||||
|
||||
if (mmrScore > bestScore) {
|
||||
bestScore = mmrScore;
|
||||
bestCandidate = candidate;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestCandidate && bestIndex !== -1) {
|
||||
selectedCandidates.push(bestCandidate);
|
||||
remainingCandidates.splice(bestIndex, 1);
|
||||
} else {
|
||||
// No more candidates to select
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return selected statements with MMR scores
|
||||
return selectedCandidates.map((item, index) => ({
|
||||
...item.statement,
|
||||
mmrScore: item.relevanceScore, // Keep original relevance score
|
||||
mmrRank: index + 1
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate cosine similarity between two vectors
|
||||
*/
|
||||
function cosineSimilarity(a: number[], b: number[]): number {
|
||||
if (a.length !== b.length || a.length === 0) return 0;
|
||||
|
||||
let dotProduct = 0;
|
||||
let normA = 0;
|
||||
let normB = 0;
|
||||
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
dotProduct += a[i] * b[i];
|
||||
normA += a[i] * a[i];
|
||||
normB += b[i] * b[i];
|
||||
}
|
||||
|
||||
if (normA === 0 || normB === 0) return 0;
|
||||
|
||||
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply Cross-Encoder reranking to results
|
||||
* This is particularly useful when results come from a single source
|
||||
@ -115,3 +258,154 @@ export async function applyCrossEncoderReranking(
|
||||
|
||||
return finalStatements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply combined MultiFactorReranking + MMR for optimal relevance and diversity
|
||||
* First applies MultiFactorReranking for authority/popularity/temporal scoring,
|
||||
* then applies MMR to reduce redundancy while maintaining relevance
|
||||
*/
|
||||
export function applyMultiFactorMMRReranking(results: {
|
||||
bm25: StatementNode[];
|
||||
vector: StatementNode[];
|
||||
bfs: StatementNode[];
|
||||
}, options?: {
|
||||
lambda?: number; // MMR balance parameter (default: 0.7)
|
||||
maxResults?: number; // Maximum results to return (default: 50)
|
||||
}): StatementNode[] {
|
||||
const { lambda = 0.7, maxResults = 50 } = options || {};
|
||||
|
||||
// Step 1: Apply MultiFactorReranking to get relevance/authority/popularity scores
|
||||
const multiFactorResults = applyMultiFactorReranking(results);
|
||||
|
||||
// Step 2: Apply MMR to reduce redundancy while maintaining relevance
|
||||
const mmrResults = applyMMRReranking(multiFactorResults, lambda, maxResults);
|
||||
|
||||
// Add combined score for debugging
|
||||
return mmrResults.map((statement) => ({
|
||||
...statement,
|
||||
combinedScore: safeNumber((statement as any).mmrScore), // MMR preserves MultiFactorScore
|
||||
rerankerUsed: 'multifactor+mmr'
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply Multi-Factor Reranking combining semantic, structural, temporal, and provenance signals
|
||||
*/
|
||||
export function applyMultiFactorReranking(results: {
|
||||
bm25: StatementNode[];
|
||||
vector: StatementNode[];
|
||||
bfs: StatementNode[];
|
||||
}): StatementNode[] {
|
||||
// Map to store combined scores and metadata
|
||||
const scores: Record<
|
||||
string,
|
||||
{
|
||||
score: number;
|
||||
statement: StatementNode;
|
||||
signals: { bm25: number; vector: number; bfs: number };
|
||||
}
|
||||
> = {};
|
||||
|
||||
// Extract original scores when available (handle BigInt)
|
||||
const getOriginalScore = (statement: any) => {
|
||||
const rawScore = statement.similarity || statement.score || statement.bm25Score || 0;
|
||||
return safeNumber(rawScore);
|
||||
};
|
||||
|
||||
// Process BM25 results - preserve original BM25 scores
|
||||
results.bm25.forEach((statement, rank) => {
|
||||
const uuid = statement.uuid;
|
||||
const originalScore = getOriginalScore(statement);
|
||||
const normalizedScore = Math.max(originalScore, 1 / (rank + 1)); // Rank fallback
|
||||
|
||||
scores[uuid] = scores[uuid] || {
|
||||
score: 0,
|
||||
statement,
|
||||
signals: { bm25: 0, vector: 0, bfs: 0 },
|
||||
};
|
||||
scores[uuid].signals.bm25 = normalizedScore;
|
||||
});
|
||||
|
||||
// Process vector similarity results - preserve semantic scores
|
||||
results.vector.forEach((statement, rank) => {
|
||||
const uuid = statement.uuid;
|
||||
const originalScore = getOriginalScore(statement);
|
||||
const normalizedScore = Math.max(originalScore, 1 / (rank + 1));
|
||||
|
||||
scores[uuid] = scores[uuid] || {
|
||||
score: 0,
|
||||
statement,
|
||||
signals: { bm25: 0, vector: 0, bfs: 0 },
|
||||
};
|
||||
scores[uuid].signals.vector = normalizedScore;
|
||||
});
|
||||
|
||||
// Process BFS traversal results - structural relevance
|
||||
results.bfs.forEach((statement, rank) => {
|
||||
const uuid = statement.uuid;
|
||||
const originalScore = getOriginalScore(statement);
|
||||
const normalizedScore = Math.max(originalScore, 1 / (rank + 1));
|
||||
|
||||
scores[uuid] = scores[uuid] || {
|
||||
score: 0,
|
||||
statement,
|
||||
signals: { bm25: 0, vector: 0, bfs: 0 },
|
||||
};
|
||||
scores[uuid].signals.bfs = normalizedScore;
|
||||
});
|
||||
|
||||
// Calculate final scores using adaptive weights
|
||||
Object.values(scores).forEach((item) => {
|
||||
const { bm25, vector, bfs } = item.signals;
|
||||
|
||||
// Adaptive weights based on signal strength
|
||||
const totalSignals =
|
||||
(bm25 > 0 ? 1 : 0) + (vector > 0 ? 1 : 0) + (bfs > 0 ? 1 : 0);
|
||||
|
||||
// Multi-signal bonus: statements appearing in multiple sources get higher weights
|
||||
const multiSignalBonus = totalSignals > 1 ? 1.2 : 1.0;
|
||||
|
||||
// Dynamic weights: stronger for queries that benefit from each signal type
|
||||
const weights = {
|
||||
bm25: bm25 > 0 ? 1.0 : 0, // Keyword matching
|
||||
vector: vector > 0 ? 0.9 : 0, // Semantic similarity
|
||||
bfs: bfs > 0 ? 0.6 : 0, // Graph connectivity
|
||||
};
|
||||
|
||||
// Temporal recency bonus (newer statements get slight boost)
|
||||
const createdAt = new Date(item.statement.createdAt).getTime();
|
||||
const now = Date.now();
|
||||
const daysSince = (now - createdAt) / (1000 * 60 * 60 * 24);
|
||||
const recencyBonus = Math.max(0.9, 1.0 - (daysSince / 365) * 0.1); // Max 10% decay over 1 year
|
||||
|
||||
// Popularity bonus based on recall count (log-scaled to prevent dominance)
|
||||
const recallCount = safeNumber(item.statement.recallCount);
|
||||
const popularityBonus = 1.0 + (Math.log(1 + recallCount) * 0.15); // Up to ~30% boost for frequently recalled facts
|
||||
|
||||
// Provenance authority bonus based on multiple source episodes
|
||||
const provenanceCount = Math.max(1, safeNumber(item.statement.provenanceCount));
|
||||
const authorityBonus = 1.0 + (Math.log(provenanceCount) * 0.2); // Up to ~35% boost for multi-source facts
|
||||
|
||||
// Final weighted score with all bonuses
|
||||
item.score =
|
||||
(weights.bm25 * bm25 + weights.vector * vector + weights.bfs * bfs) *
|
||||
multiSignalBonus *
|
||||
recencyBonus *
|
||||
popularityBonus *
|
||||
authorityBonus;
|
||||
});
|
||||
|
||||
// Convert to array and sort by final score
|
||||
const sortedResults = Object.values(scores)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.map((item) => {
|
||||
// Add the reranking score and signal breakdown for debugging
|
||||
return {
|
||||
...item.statement,
|
||||
multifactorScore: item.score,
|
||||
signals: item.signals,
|
||||
};
|
||||
});
|
||||
|
||||
return sortedResults;
|
||||
}
|
||||
|
||||
@ -31,14 +31,16 @@ export async function performBM25Search(
|
||||
`;
|
||||
}
|
||||
|
||||
// Use Neo4j's built-in fulltext search capabilities
|
||||
// Use Neo4j's built-in fulltext search capabilities with provenance count
|
||||
const cypher = `
|
||||
CALL db.index.fulltext.queryNodes("statement_fact_index", $query)
|
||||
YIELD node AS s, score
|
||||
WHERE
|
||||
(s.userId = $userId)
|
||||
${timeframeCondition}
|
||||
RETURN s, score
|
||||
OPTIONAL MATCH (episode:Episode)-[:HAS_PROVENANCE]->(s)
|
||||
WITH s, score, count(episode) as provenanceCount
|
||||
RETURN s, score, provenanceCount
|
||||
ORDER BY score DESC
|
||||
`;
|
||||
|
||||
@ -50,7 +52,15 @@ export async function performBM25Search(
|
||||
};
|
||||
|
||||
const records = await runQuery(cypher, params);
|
||||
return records.map((record) => record.get("s").properties as StatementNode);
|
||||
return records.map((record) => {
|
||||
const statement = record.get("s").properties as StatementNode;
|
||||
const provenanceCountValue = record.get("provenanceCount");
|
||||
statement.provenanceCount =
|
||||
typeof provenanceCountValue === "bigint"
|
||||
? Number(provenanceCountValue)
|
||||
: (provenanceCountValue?.toNumber?.() ?? provenanceCountValue ?? 0);
|
||||
return statement;
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("BM25 search error:", { error });
|
||||
return [];
|
||||
@ -101,7 +111,7 @@ export async function performVectorSearch(
|
||||
`;
|
||||
}
|
||||
|
||||
// 1. Search for similar statements using Neo4j vector search
|
||||
// 1. Search for similar statements using Neo4j vector search with provenance count
|
||||
const cypher = `
|
||||
MATCH (s:Statement)
|
||||
WHERE
|
||||
@ -109,7 +119,9 @@ export async function performVectorSearch(
|
||||
${timeframeCondition}
|
||||
WITH s, vector.similarity.cosine(s.factEmbedding, $embedding) AS score
|
||||
WHERE score > 0.7
|
||||
RETURN s, score
|
||||
OPTIONAL MATCH (episode:Episode)-[:HAS_PROVENANCE]->(s)
|
||||
WITH s, score, count(episode) as provenanceCount
|
||||
RETURN s, score, provenanceCount
|
||||
ORDER BY score DESC
|
||||
`;
|
||||
|
||||
@ -121,7 +133,15 @@ export async function performVectorSearch(
|
||||
};
|
||||
|
||||
const records = await runQuery(cypher, params);
|
||||
return records.map((record) => record.get("s").properties as StatementNode);
|
||||
return records.map((record) => {
|
||||
const statement = record.get("s").properties as StatementNode;
|
||||
const provenanceCountValue = record.get("provenanceCount");
|
||||
statement.provenanceCount =
|
||||
typeof provenanceCountValue === "bigint"
|
||||
? Number(provenanceCountValue)
|
||||
: (provenanceCountValue?.toNumber?.() ?? provenanceCountValue ?? 0);
|
||||
return statement;
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Vector search error:", { error });
|
||||
return [];
|
||||
|
||||
@ -79,6 +79,7 @@
|
||||
"ai": "4.3.14",
|
||||
"axios": "^1.10.0",
|
||||
"bullmq": "^5.53.2",
|
||||
"cheerio": "^1.1.2",
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "0.14.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
||||
@ -54,6 +54,7 @@ export interface StatementNode {
|
||||
userId: string;
|
||||
space?: string;
|
||||
recallCount?: number;
|
||||
provenanceCount?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
303
pnpm-lock.yaml
generated
303
pnpm-lock.yaml
generated
@ -475,6 +475,9 @@ importers:
|
||||
bullmq:
|
||||
specifier: ^5.53.2
|
||||
version: 5.53.2
|
||||
cheerio:
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2
|
||||
class-transformer:
|
||||
specifier: 0.5.1
|
||||
version: 0.5.1
|
||||
@ -637,7 +640,7 @@ importers:
|
||||
devDependencies:
|
||||
'@remix-run/dev':
|
||||
specifier: 2.16.7
|
||||
version: 2.16.7(@remix-run/react@2.16.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.7(typescript@5.8.3))(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(typescript@5.8.3)(vite@6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))(yaml@2.8.0)
|
||||
version: 2.16.7(@remix-run/react@2.16.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.7(typescript@5.8.3))(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))(yaml@2.8.0)
|
||||
'@remix-run/eslint-config':
|
||||
specifier: 2.16.7
|
||||
version: 2.16.7(eslint@8.57.1)(react@18.3.1)(typescript@5.8.3)
|
||||
@ -652,7 +655,7 @@ importers:
|
||||
version: 0.5.16(tailwindcss@4.1.7)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.9(vite@6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))
|
||||
version: 4.1.9(vite@6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))
|
||||
'@trigger.dev/build':
|
||||
specifier: ^4.0.0-v4-beta.22
|
||||
version: 4.0.0-v4-beta.22(typescript@5.8.3)
|
||||
@ -736,7 +739,7 @@ importers:
|
||||
version: 3.5.3
|
||||
prettier-plugin-tailwindcss:
|
||||
specifier: ^0.6.11
|
||||
version: 0.6.12(@ianvs/prettier-plugin-sort-imports@4.1.1(@vue/compiler-sfc@3.3.4)(prettier@3.5.3))(prettier@3.5.3)
|
||||
version: 0.6.12(@ianvs/prettier-plugin-sort-imports@4.1.1(prettier@3.5.3))(prettier@3.5.3)
|
||||
tailwind-scrollbar:
|
||||
specifier: ^4.0.2
|
||||
version: 4.0.2(react@18.3.1)(tailwindcss@4.1.7)
|
||||
@ -748,10 +751,10 @@ importers:
|
||||
version: 5.8.3
|
||||
vite:
|
||||
specifier: ^6.0.0
|
||||
version: 6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
version: 6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^4.2.1
|
||||
version: 4.3.2(typescript@5.8.3)(vite@6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))
|
||||
version: 4.3.2(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))
|
||||
|
||||
packages/database:
|
||||
dependencies:
|
||||
@ -820,7 +823,7 @@ importers:
|
||||
version: 20.19.7
|
||||
tsup:
|
||||
specifier: ^8.0.1
|
||||
version: 8.5.0(@swc/core@1.3.101(@swc/helpers@0.5.17))(jiti@2.4.2)(postcss@8.5.5)(tsx@4.17.0)(typescript@5.8.3)(yaml@2.8.0)
|
||||
version: 8.5.0(@swc/core@1.3.101)(jiti@2.4.2)(postcss@8.5.5)(tsx@4.17.0)(typescript@5.8.3)(yaml@2.8.0)
|
||||
typescript:
|
||||
specifier: ^5.0.0
|
||||
version: 5.8.3
|
||||
@ -857,7 +860,7 @@ importers:
|
||||
version: 6.0.1
|
||||
tsup:
|
||||
specifier: ^8.0.1
|
||||
version: 8.5.0(@swc/core@1.3.101(@swc/helpers@0.5.17))(jiti@2.4.2)(postcss@8.5.5)(tsx@4.17.0)(typescript@5.8.3)(yaml@2.8.0)
|
||||
version: 8.5.0(@swc/core@1.3.101)(jiti@2.4.2)(postcss@8.5.5)(tsx@4.17.0)(typescript@5.8.3)(yaml@2.8.0)
|
||||
typescript:
|
||||
specifier: ^5.3.0
|
||||
version: 5.8.3
|
||||
@ -5284,24 +5287,6 @@ packages:
|
||||
'@vanilla-extract/private@1.0.8':
|
||||
resolution: {integrity: sha512-oRAbUlq1SyTWCo7dQnTVm+xgJMqNl8K1dEempQHXzQvUuyEfBabMt0wNGf+VCHzvKbx/Bzr9p/2wy8WA9+2z2g==}
|
||||
|
||||
'@vue/compiler-core@3.3.4':
|
||||
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
|
||||
|
||||
'@vue/compiler-dom@3.3.4':
|
||||
resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
|
||||
|
||||
'@vue/compiler-sfc@3.3.4':
|
||||
resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
|
||||
|
||||
'@vue/compiler-ssr@3.3.4':
|
||||
resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
|
||||
|
||||
'@vue/reactivity-transform@3.3.4':
|
||||
resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
|
||||
|
||||
'@vue/shared@3.3.4':
|
||||
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
|
||||
|
||||
'@web3-storage/multipart-parser@1.0.0':
|
||||
resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==}
|
||||
|
||||
@ -5638,6 +5623,9 @@ packages:
|
||||
resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
boolbase@1.0.0:
|
||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||
|
||||
bowser@2.11.0:
|
||||
resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
|
||||
|
||||
@ -5773,6 +5761,13 @@ packages:
|
||||
chardet@0.7.0:
|
||||
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||
|
||||
cheerio-select@2.1.0:
|
||||
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
|
||||
|
||||
cheerio@1.1.2:
|
||||
resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
@ -6087,6 +6082,9 @@ packages:
|
||||
webpack:
|
||||
optional: true
|
||||
|
||||
css-select@5.2.2:
|
||||
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
|
||||
|
||||
css-styled@1.0.8:
|
||||
resolution: {integrity: sha512-tCpP7kLRI8dI95rCh3Syl7I+v7PP+2JYOzWkl0bUEoSbJM+u8ITbutjlQVf0NC2/g4ULROJPi16sfwDIO8/84g==}
|
||||
|
||||
@ -6542,6 +6540,9 @@ packages:
|
||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
encoding-sniffer@0.2.1:
|
||||
resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==}
|
||||
|
||||
encoding@0.1.13:
|
||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||
|
||||
@ -6571,6 +6572,10 @@ packages:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
entities@6.0.1:
|
||||
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
env-paths@2.2.1:
|
||||
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
||||
engines: {node: '>=6'}
|
||||
@ -6894,9 +6899,6 @@ packages:
|
||||
estree-util-visit@1.2.1:
|
||||
resolution: {integrity: sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
@ -7412,6 +7414,9 @@ packages:
|
||||
html-url-attributes@3.0.1:
|
||||
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
|
||||
|
||||
htmlparser2@10.0.0:
|
||||
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
|
||||
|
||||
htmlparser2@8.0.2:
|
||||
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
||||
|
||||
@ -8790,6 +8795,9 @@ packages:
|
||||
resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
num2fraction@1.2.2:
|
||||
resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==}
|
||||
|
||||
@ -9026,6 +9034,15 @@ packages:
|
||||
resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
parse5-htmlparser2-tree-adapter@7.1.0:
|
||||
resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==}
|
||||
|
||||
parse5-parser-stream@7.1.2:
|
||||
resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==}
|
||||
|
||||
parse5@7.3.0:
|
||||
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||
|
||||
parseley@0.12.1:
|
||||
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
|
||||
|
||||
@ -10291,6 +10308,7 @@ packages:
|
||||
source-map@0.8.0-beta.0:
|
||||
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
|
||||
engines: {node: '>= 8'}
|
||||
deprecated: The work that was done in this beta branch won't be included in future versions
|
||||
|
||||
space-separated-tokens@2.0.2:
|
||||
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
|
||||
@ -10908,6 +10926,10 @@ packages:
|
||||
resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
undici@7.13.0:
|
||||
resolution: {integrity: sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
unicorn-magic@0.1.0:
|
||||
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
|
||||
engines: {node: '>=18'}
|
||||
@ -11209,6 +11231,14 @@ packages:
|
||||
webpack-cli:
|
||||
optional: true
|
||||
|
||||
whatwg-encoding@3.1.1:
|
||||
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
whatwg-mimetype@4.0.0:
|
||||
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
|
||||
@ -12778,7 +12808,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
'@floating-ui/react-dom@2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
@ -12852,7 +12882,7 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/object-schema@2.0.3': {}
|
||||
|
||||
'@ianvs/prettier-plugin-sort-imports@4.1.1(@vue/compiler-sfc@3.3.4)(prettier@3.5.3)':
|
||||
'@ianvs/prettier-plugin-sort-imports@4.1.1(prettier@3.5.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/generator': 7.27.5
|
||||
@ -12861,8 +12891,6 @@ snapshots:
|
||||
'@babel/types': 7.27.6
|
||||
prettier: 3.5.3
|
||||
semver: 7.7.2
|
||||
optionalDependencies:
|
||||
'@vue/compiler-sfc': 3.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
optional: true
|
||||
@ -13441,7 +13469,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13495,7 +13523,7 @@ snapshots:
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13523,7 +13551,7 @@ snapshots:
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-slot': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13656,7 +13684,7 @@ snapshots:
|
||||
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13721,7 +13749,7 @@ snapshots:
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13813,7 +13841,7 @@ snapshots:
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
aria-hidden: 1.2.6
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-remove-scroll: 2.5.7(@types/react@18.2.47)(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
@ -13855,7 +13883,7 @@ snapshots:
|
||||
'@radix-ui/react-use-size': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/rect': 1.1.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13890,7 +13918,7 @@ snapshots:
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13918,7 +13946,7 @@ snapshots:
|
||||
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13944,7 +13972,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@radix-ui/react-slot': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -13970,7 +13998,7 @@ snapshots:
|
||||
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -14155,7 +14183,7 @@ snapshots:
|
||||
'@radix-ui/react-toggle': 1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -14166,7 +14194,7 @@ snapshots:
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -14186,7 +14214,7 @@ snapshots:
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.47)(react@18.2.0)
|
||||
'@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -14338,7 +14366,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.2.47
|
||||
'@types/react-dom': 18.2.18
|
||||
@ -14459,7 +14487,7 @@ snapshots:
|
||||
html-to-text: 9.0.5
|
||||
js-beautify: 1.15.4
|
||||
react: 18.3.1
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-promise-suspense: 0.3.4
|
||||
|
||||
'@react-email/row@0.0.7(react@18.3.1)':
|
||||
@ -14493,7 +14521,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
'@remix-run/dev@2.16.7(@remix-run/react@2.16.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.7(typescript@5.8.3))(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(typescript@5.8.3)(vite@6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))(yaml@2.8.0)':
|
||||
'@remix-run/dev@2.16.7(@remix-run/react@2.16.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3))(@remix-run/serve@2.16.7(typescript@5.8.3))(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))(yaml@2.8.0)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/generator': 7.27.5
|
||||
@ -14510,7 +14538,7 @@ snapshots:
|
||||
'@remix-run/router': 1.23.0
|
||||
'@remix-run/server-runtime': 2.16.7(typescript@5.8.3)
|
||||
'@types/mdx': 2.0.13
|
||||
'@vanilla-extract/integration': 6.5.0(@types/node@20.19.7)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
'@vanilla-extract/integration': 6.5.0(@types/node@22.16.0)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
arg: 5.0.2
|
||||
cacache: 17.1.4
|
||||
chalk: 4.1.2
|
||||
@ -14550,12 +14578,12 @@ snapshots:
|
||||
tar-fs: 2.1.3
|
||||
tsconfig-paths: 4.2.0
|
||||
valibot: 0.41.0(typescript@5.8.3)
|
||||
vite-node: 3.2.3(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
vite-node: 3.2.3(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
ws: 7.5.10
|
||||
optionalDependencies:
|
||||
'@remix-run/serve': 2.16.7(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
vite: 6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
@ -15294,12 +15322,12 @@ snapshots:
|
||||
postcss-selector-parser: 6.0.10
|
||||
tailwindcss: 4.1.7
|
||||
|
||||
'@tailwindcss/vite@4.1.9(vite@6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))':
|
||||
'@tailwindcss/vite@4.1.9(vite@6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@tailwindcss/node': 4.1.9
|
||||
'@tailwindcss/oxide': 4.1.9
|
||||
tailwindcss: 4.1.9
|
||||
vite: 6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
|
||||
'@tanstack/react-table@8.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
@ -16268,7 +16296,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- babel-plugin-macros
|
||||
|
||||
'@vanilla-extract/integration@6.5.0(@types/node@20.19.7)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)':
|
||||
'@vanilla-extract/integration@6.5.0(@types/node@22.16.0)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)':
|
||||
dependencies:
|
||||
'@babel/core': 7.27.4
|
||||
'@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.4)
|
||||
@ -16281,8 +16309,8 @@ snapshots:
|
||||
lodash: 4.17.21
|
||||
mlly: 1.7.4
|
||||
outdent: 0.8.0
|
||||
vite: 5.4.19(@types/node@20.19.7)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
vite-node: 1.6.1(@types/node@20.19.7)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
vite: 5.4.19(@types/node@22.16.0)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
vite-node: 1.6.1(@types/node@22.16.0)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- babel-plugin-macros
|
||||
@ -16297,52 +16325,6 @@ snapshots:
|
||||
|
||||
'@vanilla-extract/private@1.0.8': {}
|
||||
|
||||
'@vue/compiler-core@3.3.4':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.5
|
||||
'@vue/shared': 3.3.4
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
optional: true
|
||||
|
||||
'@vue/compiler-dom@3.3.4':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.3.4
|
||||
'@vue/shared': 3.3.4
|
||||
optional: true
|
||||
|
||||
'@vue/compiler-sfc@3.3.4':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.5
|
||||
'@vue/compiler-core': 3.3.4
|
||||
'@vue/compiler-dom': 3.3.4
|
||||
'@vue/compiler-ssr': 3.3.4
|
||||
'@vue/reactivity-transform': 3.3.4
|
||||
'@vue/shared': 3.3.4
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.17
|
||||
postcss: 8.5.5
|
||||
source-map-js: 1.2.1
|
||||
optional: true
|
||||
|
||||
'@vue/compiler-ssr@3.3.4':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.3.4
|
||||
'@vue/shared': 3.3.4
|
||||
optional: true
|
||||
|
||||
'@vue/reactivity-transform@3.3.4':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.5
|
||||
'@vue/compiler-core': 3.3.4
|
||||
'@vue/shared': 3.3.4
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.17
|
||||
optional: true
|
||||
|
||||
'@vue/shared@3.3.4':
|
||||
optional: true
|
||||
|
||||
'@web3-storage/multipart-parser@1.0.0': {}
|
||||
|
||||
'@webassemblyjs/ast@1.14.1':
|
||||
@ -16750,6 +16732,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
boolbase@1.0.0: {}
|
||||
|
||||
bowser@2.11.0: {}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
@ -16913,6 +16897,29 @@ snapshots:
|
||||
|
||||
chardet@0.7.0: {}
|
||||
|
||||
cheerio-select@2.1.0:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
css-select: 5.2.2
|
||||
css-what: 6.1.0
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.2.2
|
||||
|
||||
cheerio@1.1.2:
|
||||
dependencies:
|
||||
cheerio-select: 2.1.0
|
||||
dom-serializer: 2.0.0
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.2.2
|
||||
encoding-sniffer: 0.2.1
|
||||
htmlparser2: 10.0.0
|
||||
parse5: 7.3.0
|
||||
parse5-htmlparser2-tree-adapter: 7.1.0
|
||||
parse5-parser-stream: 7.1.2
|
||||
undici: 7.13.0
|
||||
whatwg-mimetype: 4.0.0
|
||||
|
||||
chokidar@3.5.3:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
@ -17232,6 +17239,14 @@ snapshots:
|
||||
optionalDependencies:
|
||||
webpack: 5.99.9(esbuild@0.25.5)
|
||||
|
||||
css-select@5.2.2:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
css-what: 6.1.0
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.2.2
|
||||
nth-check: 2.1.1
|
||||
|
||||
css-styled@1.0.8:
|
||||
dependencies:
|
||||
'@daybrush/utils': 1.13.0
|
||||
@ -17672,6 +17687,11 @@ snapshots:
|
||||
|
||||
encodeurl@2.0.0: {}
|
||||
|
||||
encoding-sniffer@0.2.1:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
whatwg-encoding: 3.1.1
|
||||
|
||||
encoding@0.1.13:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
@ -17723,6 +17743,8 @@ snapshots:
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
entities@6.0.1: {}
|
||||
|
||||
env-paths@2.2.1: {}
|
||||
|
||||
environment@1.1.0: {}
|
||||
@ -18339,9 +18361,6 @@ snapshots:
|
||||
'@types/estree-jsx': 1.0.5
|
||||
'@types/unist': 2.0.11
|
||||
|
||||
estree-walker@2.0.2:
|
||||
optional: true
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@ -18650,7 +18669,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@emotion/is-prop-valid': 0.8.8
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
framework-utils@1.1.0: {}
|
||||
|
||||
@ -19002,6 +19021,13 @@ snapshots:
|
||||
|
||||
html-url-attributes@3.0.1: {}
|
||||
|
||||
htmlparser2@10.0.0:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.2.2
|
||||
entities: 6.0.1
|
||||
|
||||
htmlparser2@8.0.2:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
@ -20491,7 +20517,7 @@ snapshots:
|
||||
graceful-fs: 4.2.11
|
||||
postcss: 8.4.31
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
styled-jsx: 5.1.1(@babel/core@7.24.5)(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 14.1.4
|
||||
@ -20650,6 +20676,10 @@ snapshots:
|
||||
path-key: 4.0.0
|
||||
unicorn-magic: 0.3.0
|
||||
|
||||
nth-check@2.1.1:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
num2fraction@1.2.2: {}
|
||||
|
||||
nypm@0.5.4:
|
||||
@ -20898,6 +20928,19 @@ snapshots:
|
||||
parse-node-version@1.0.1:
|
||||
optional: true
|
||||
|
||||
parse5-htmlparser2-tree-adapter@7.1.0:
|
||||
dependencies:
|
||||
domhandler: 5.0.3
|
||||
parse5: 7.3.0
|
||||
|
||||
parse5-parser-stream@7.1.2:
|
||||
dependencies:
|
||||
parse5: 7.3.0
|
||||
|
||||
parse5@7.3.0:
|
||||
dependencies:
|
||||
entities: 6.0.1
|
||||
|
||||
parseley@0.12.1:
|
||||
dependencies:
|
||||
leac: 0.6.0
|
||||
@ -21243,11 +21286,11 @@ snapshots:
|
||||
|
||||
prelude-ls@1.2.1: {}
|
||||
|
||||
prettier-plugin-tailwindcss@0.6.12(@ianvs/prettier-plugin-sort-imports@4.1.1(@vue/compiler-sfc@3.3.4)(prettier@3.5.3))(prettier@3.5.3):
|
||||
prettier-plugin-tailwindcss@0.6.12(@ianvs/prettier-plugin-sort-imports@4.1.1(prettier@3.5.3))(prettier@3.5.3):
|
||||
dependencies:
|
||||
prettier: 3.5.3
|
||||
optionalDependencies:
|
||||
'@ianvs/prettier-plugin-sort-imports': 4.1.1(@vue/compiler-sfc@3.3.4)(prettier@3.5.3)
|
||||
'@ianvs/prettier-plugin-sort-imports': 4.1.1(prettier@3.5.3)
|
||||
|
||||
prettier@2.8.8: {}
|
||||
|
||||
@ -21528,12 +21571,6 @@ snapshots:
|
||||
react: 18.2.0
|
||||
scheduler: 0.23.2
|
||||
|
||||
react-dom@18.2.0(react@18.3.1):
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
react: 18.3.1
|
||||
scheduler: 0.23.2
|
||||
|
||||
react-dom@18.3.1(react@18.3.1):
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
@ -21573,7 +21610,7 @@ snapshots:
|
||||
postcss: 8.4.38
|
||||
prism-react-renderer: 2.1.0(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
socket.io: 4.7.3
|
||||
socket.io-client: 4.7.3
|
||||
sonner: 1.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@ -22376,7 +22413,7 @@ snapshots:
|
||||
sonner@1.3.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.3.1)
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
source-map-js@1.0.2: {}
|
||||
|
||||
@ -22903,7 +22940,7 @@ snapshots:
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tsup@8.5.0(@swc/core@1.3.101(@swc/helpers@0.5.17))(jiti@2.4.2)(postcss@8.5.5)(tsx@4.17.0)(typescript@5.8.3)(yaml@2.8.0):
|
||||
tsup@8.5.0(@swc/core@1.3.101)(jiti@2.4.2)(postcss@8.5.5)(tsx@4.17.0)(typescript@5.8.3)(yaml@2.8.0):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.25.5)
|
||||
cac: 6.7.14
|
||||
@ -23081,6 +23118,8 @@ snapshots:
|
||||
|
||||
undici@6.21.3: {}
|
||||
|
||||
undici@7.13.0: {}
|
||||
|
||||
unicorn-magic@0.1.0: {}
|
||||
|
||||
unicorn-magic@0.3.0: {}
|
||||
@ -23304,13 +23343,13 @@ snapshots:
|
||||
'@types/unist': 3.0.3
|
||||
vfile-message: 4.0.2
|
||||
|
||||
vite-node@1.6.1(@types/node@20.19.7)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0):
|
||||
vite-node@1.6.1(@types/node@22.16.0)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.1(supports-color@10.0.0)
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.1.1
|
||||
vite: 5.4.19(@types/node@20.19.7)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
vite: 5.4.19(@types/node@22.16.0)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
@ -23322,13 +23361,13 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-node@3.2.3(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0):
|
||||
vite-node@3.2.3(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.1(supports-color@10.0.0)
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
@ -23343,31 +23382,31 @@ snapshots:
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)):
|
||||
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)):
|
||||
dependencies:
|
||||
debug: 4.4.1(supports-color@10.0.0)
|
||||
globrex: 0.1.2
|
||||
tsconfck: 3.1.6(typescript@5.8.3)
|
||||
optionalDependencies:
|
||||
vite: 6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
vite: 6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@5.4.19(@types/node@20.19.7)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0):
|
||||
vite@5.4.19(@types/node@22.16.0)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.5.5
|
||||
rollup: 4.43.0
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.7
|
||||
'@types/node': 22.16.0
|
||||
fsevents: 2.3.3
|
||||
less: 4.4.0
|
||||
lightningcss: 1.30.1
|
||||
sass: 1.89.2
|
||||
terser: 5.42.0
|
||||
|
||||
vite@6.3.5(@types/node@20.19.7)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0):
|
||||
vite@6.3.5(@types/node@22.16.0)(jiti@2.4.2)(less@4.4.0)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.42.0)(tsx@4.17.0)(yaml@2.8.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.5
|
||||
fdir: 6.4.6(picomatch@4.0.2)
|
||||
@ -23376,7 +23415,7 @@ snapshots:
|
||||
rollup: 4.43.0
|
||||
tinyglobby: 0.2.14
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.7
|
||||
'@types/node': 22.16.0
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.4.2
|
||||
less: 4.4.0
|
||||
@ -23478,6 +23517,12 @@ snapshots:
|
||||
- uglify-js
|
||||
optional: true
|
||||
|
||||
whatwg-encoding@3.1.1:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
|
||||
whatwg-mimetype@4.0.0: {}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user