/** * Knowledge API Client * Handles knowledge entry-related API requests */ import type { KnowledgeEntryWithTags, KnowledgeTag, KnowledgeEntryVersionWithAuthor, PaginatedResponse, EntryStatus, Visibility, } from "@mosaic/shared"; import { apiGet, apiPost, apiPatch, apiDelete, type ApiResponse } from "./client"; export interface EntryFilters { status?: EntryStatus; visibility?: Visibility; tag?: string; tags?: string[]; page?: number; limit?: number; search?: string; sortBy?: "updatedAt" | "createdAt" | "title"; sortOrder?: "asc" | "desc"; } export interface EntriesResponse { data: KnowledgeEntryWithTags[]; meta?: { total?: number; page?: number; limit?: number; }; } export interface CreateEntryData { title: string; content: string; summary?: string; status?: EntryStatus; visibility?: Visibility; tags?: string[]; } export interface UpdateEntryData { title?: string; content?: string; summary?: string; status?: EntryStatus; visibility?: Visibility; tags?: string[]; changeNote?: string; } export interface RestoreVersionData { changeNote?: string; } /** * Fetch knowledge entries with optional filters */ export async function fetchEntries(filters?: EntryFilters): Promise { const params = new URLSearchParams(); if (filters?.status) { params.append("status", filters.status); } if (filters?.visibility) { params.append("visibility", filters.visibility); } if (filters?.tag) { params.append("tag", filters.tag); } if (filters?.tags && filters.tags.length > 0) { filters.tags.forEach((tag) => { params.append("tags", tag); }); } if (filters?.page) { params.append("page", filters.page.toString()); } if (filters?.limit) { params.append("limit", filters.limit.toString()); } if (filters?.search) { params.append("search", filters.search); } if (filters?.sortBy) { params.append("sortBy", filters.sortBy); } if (filters?.sortOrder) { params.append("sortOrder", filters.sortOrder); } const queryString = params.toString(); const endpoint = queryString ? `/api/knowledge/entries?${queryString}` : "/api/knowledge/entries"; const response = await apiGet(endpoint); return response; } /** * Fetch a single knowledge entry by slug */ export async function fetchEntry(slug: string): Promise { return apiGet(`/api/knowledge/entries/${slug}`); } /** * Create a new knowledge entry */ export async function createEntry(data: CreateEntryData): Promise { return apiPost("/api/knowledge/entries", data); } /** * Update an existing knowledge entry */ export async function updateEntry( slug: string, data: UpdateEntryData ): Promise { return apiPatch(`/api/knowledge/entries/${slug}`, data); } /** * Delete (archive) a knowledge entry */ export async function deleteEntry(slug: string): Promise { await apiDelete(`/api/knowledge/entries/${slug}`); } /** * Fetch all knowledge tags */ export async function fetchTags(): Promise { const response = await apiGet>("/api/knowledge/tags"); return response.data; } /** * Fetch version history for an entry */ export async function fetchVersions( slug: string, page = 1, limit = 20 ): Promise> { const params = new URLSearchParams(); params.append("page", page.toString()); params.append("limit", limit.toString()); return apiGet>( `/api/knowledge/entries/${slug}/versions?${params.toString()}` ); } /** * Fetch a specific version of an entry */ export async function fetchVersion( slug: string, version: number ): Promise { return apiGet( `/api/knowledge/entries/${slug}/versions/${version.toString()}` ); } /** * Restore a previous version of an entry */ export async function restoreVersion( slug: string, version: number, data?: RestoreVersionData ): Promise { return apiPost( `/api/knowledge/entries/${slug}/restore/${version.toString()}`, data ?? {} ); } /** * Fetch backlinks for an entry (entries that link to this entry) */ export async function fetchBacklinks(slug: string): Promise<{ entry: { id: string; slug: string; title: string }; backlinks: { id: string; sourceId: string; targetId: string; linkText: string; displayText: string; positionStart: number; positionEnd: number; resolved: boolean; context: string | null; createdAt: Date; source: { id: string; title: string; slug: string; summary?: string | null; }; }[]; count: number; }> { return apiGet<{ entry: { id: string; slug: string; title: string }; backlinks: { id: string; sourceId: string; targetId: string; linkText: string; displayText: string; positionStart: number; positionEnd: number; resolved: boolean; context: string | null; createdAt: Date; source: { id: string; title: string; slug: string; summary?: string | null; }; }[]; count: number; }>(`/api/knowledge/entries/${slug}/backlinks`); } /** * Fetch knowledge base statistics */ export async function fetchKnowledgeStats(): Promise<{ overview: { totalEntries: number; totalTags: number; totalLinks: number; publishedEntries: number; draftEntries: number; archivedEntries: number; }; mostConnected: { id: string; slug: string; title: string; incomingLinks: number; outgoingLinks: number; totalConnections: number; }[]; recentActivity: { id: string; slug: string; title: string; updatedAt: string; status: string; }[]; tagDistribution: { id: string; name: string; slug: string; color: string | null; entryCount: number; }[]; }> { return apiGet(`/api/knowledge/stats`); } /** * Fetch entry graph (network of connected entries) */ export async function fetchEntryGraph( slug: string, depth = 1 ): Promise<{ centerNode: { id: string; slug: string; title: string; summary: string | null; tags: { id: string; name: string; slug: string; color: string | null; }[]; depth: number; }; nodes: { id: string; slug: string; title: string; summary: string | null; tags: { id: string; name: string; slug: string; color: string | null; }[]; depth: number; }[]; edges: { id: string; sourceId: string; targetId: string; linkText: string; }[]; stats: { totalNodes: number; totalEdges: number; maxDepth: number; }; }> { const params = new URLSearchParams(); params.append("depth", depth.toString()); return apiGet(`/api/knowledge/entries/${slug}/graph?${params.toString()}`); } /** * Fetch full knowledge graph */ export async function fetchKnowledgeGraph(filters?: { tags?: string[]; status?: string; limit?: number; }): Promise<{ nodes: { id: string; slug: string; title: string; summary: string | null; status?: string; tags: { id: string; name: string; slug: string; color: string | null; }[]; depth: number; isOrphan?: boolean; }[]; edges: { id: string; sourceId: string; targetId: string; linkText: string; }[]; stats: { totalNodes: number; totalEdges: number; orphanCount: number; }; }> { const params = new URLSearchParams(); if (filters?.tags && filters.tags.length > 0) { filters.tags.forEach((tag) => { params.append("tags", tag); }); } if (filters?.status) { params.append("status", filters.status); } if (filters?.limit !== undefined) { params.append("limit", filters.limit.toString()); } const queryString = params.toString(); const endpoint = queryString ? `/api/knowledge/graph?${queryString}` : "/api/knowledge/graph"; return apiGet(endpoint); }