feat(db): mesh data model — meshes, members, invites, audit log

- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 21:19:32 +01:00
commit d3163a5bff
1384 changed files with 314925 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
// PDF Hooks
export { usePdfViewer, useCanGoBack, useCanGoForward } from "../context";
export { usePdfNavigation } from "./use-pdf-navigation";
export { useEmbedding } from "./use-embedding";
export { useCitationUnit } from "./use-citation-unit";
export type { CitationUnitDetail, BoundingBox } from "./use-citation-unit";

View File

@@ -0,0 +1,73 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { api } from "~/lib/api/client";
// ============================================================================
// Types
// ============================================================================
/**
* Bounding box for pixel-perfect highlighting
*/
export interface BoundingBox {
x: number;
y: number;
width: number;
height: number;
}
/**
* Citation unit with precise location for highlighting (WF-0028)
*/
export interface CitationUnitDetail {
id: string;
content: string;
pageNumber: number;
paragraphIndex: number;
charStart: number;
charEnd: number;
bbox: BoundingBox | null;
sectionTitle: string | null;
unitType: string;
}
// ============================================================================
// Hook
// ============================================================================
/**
* Fetch citation unit details by ID for bounding box-based highlighting
*
* Falls back to legacy embedding endpoint if citation unit not found
*/
export function useCitationUnit(unitId: string | null) {
return useQuery({
queryKey: ["pdf", "citation-unit", unitId],
queryFn: async (): Promise<CitationUnitDetail | null> => {
if (!unitId) return null;
// Try citation unit endpoint first (WF-0028 dual-resolution)
const response = await api.ai.pdf.search["citation-units"].single[":id"].$get({
param: { id: unitId },
});
if (response.ok) {
const result = await response.json();
return (result as { data: CitationUnitDetail }).data;
}
// If not found in citation units, this might be a legacy embedding ID
// Return null - the highlight layer will fall back to word overlap
if (response.status === 404) {
return null;
}
throw new Error("Failed to fetch citation unit");
},
enabled: Boolean(unitId),
staleTime: Infinity, // Citation units don't change
gcTime: 1000 * 60 * 30, // Keep in cache for 30 minutes
});
}

View File

@@ -0,0 +1,48 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { api } from "~/lib/api/client";
// ============================================================================
// Types
// ============================================================================
export interface EmbeddingDetail {
id: string;
content: string;
pageNumber: number;
charStart?: number;
charEnd?: number;
sectionTitle?: string;
}
// ============================================================================
// Hook
// ============================================================================
/**
* Fetch embedding details by ID for citation highlighting
*/
export function useEmbedding(embeddingId: string | null) {
return useQuery({
queryKey: ["pdf", "embedding", embeddingId],
queryFn: async (): Promise<EmbeddingDetail | null> => {
if (!embeddingId) return null;
const response = await api.ai.pdf.embeddings[":id"].$get({
param: { id: embeddingId },
});
if (!response.ok) {
if (response.status === 404) return null;
throw new Error("Failed to fetch embedding");
}
return response.json() as Promise<EmbeddingDetail>;
},
enabled: Boolean(embeddingId),
staleTime: Infinity, // Embeddings don't change
gcTime: 1000 * 60 * 30, // Keep in cache for 30 minutes
});
}

View File

@@ -0,0 +1,26 @@
"use client";
import { useCanGoBack, useCanGoForward, usePdfViewer } from "../context";
/**
* Convenience hook for PDF navigation controls.
* Combines navigation state and actions in one place.
*/
export function usePdfNavigation() {
const { goBack, goForward, navigateTo, history, historyIndex } =
usePdfViewer();
const canGoBack = useCanGoBack();
const canGoForward = useCanGoForward();
return {
// Actions
goBack,
goForward,
navigateTo,
// State
canGoBack,
canGoForward,
historyLength: history.length,
currentIndex: historyIndex,
};
}