links.ts
1 import type { BlockLink } from '@/data/models/blockLink'; 2 import type { PaginatedLinksResponse } from '@/data/models/paginatedLinksResponse'; 3 4 // Type for the response from the /links endpoint 5 export type LinksResponse = PaginatedLinksResponse; 6 7 /** 8 * Validates a paginated links response against the schema 9 * @param data - The data to validate 10 * @returns Type predicate indicating if the data matches the PaginatedLinksResponse schema 11 */ 12 export function validateLinks(data: unknown): data is PaginatedLinksResponse { 13 return Boolean( 14 data && 15 typeof data === 'object' && 16 'links' in data && 17 Array.isArray((data as Record<string, unknown>).links) && 18 'page_size' in data && 19 typeof (data as Record<string, unknown>).page_size === 'number' 20 ); 21 } 22 23 /** 24 * Validates a single block link against the schema 25 * @param data - The data to validate 26 * @returns Type predicate indicating if the data matches the BlockLink schema 27 */ 28 export function validateLink(data: unknown): data is BlockLink { 29 // Basic check for required properties 30 return Boolean( 31 data && 32 typeof data === 'object' && 33 'from_id' in data && 34 'to_id' in data && 35 'relation' in data 36 ); 37 } 38 39 // API URL pointing to local Next.js API routes (which proxy to backend) 40 const API_URL = '/api/v1'; 41 42 /** 43 * Fetches all block links from the API with validation 44 * @param branch - Optional branch name to fetch links from (defaults to 'main') 45 * @param namespace - Optional namespace to filter links (defaults to 'legacy') 46 * @param cursor - Optional cursor for pagination 47 * @param limit - Optional limit for number of results 48 * @returns Promise resolving to a validated paginated links response 49 */ 50 export async function fetchLinks( 51 branch?: string, 52 namespace?: string, 53 cursor?: string, 54 limit?: number 55 ): Promise<LinksResponse> { 56 const url = new URL(`${API_URL}/links`, window.location.origin); 57 if (branch) { 58 url.searchParams.set('branch', branch); 59 } 60 if (namespace) { 61 url.searchParams.set('namespace', namespace); 62 } 63 if (cursor) { 64 url.searchParams.set('cursor', cursor); 65 } 66 if (limit !== undefined) { 67 url.searchParams.set('limit', limit.toString()); 68 } 69 70 const response = await fetch(url.toString()); 71 72 if (!response.ok) { 73 throw new Error(`Failed to fetch links: ${response.status} ${response.statusText}`); 74 } 75 76 const data = await response.json(); 77 78 // Validate the response data 79 if (!validateLinks(data)) { 80 throw new Error('Invalid links response from API'); 81 } 82 83 return data; 84 } 85 86 /** 87 * Fetches links from a specific block 88 * @param blockId - The ID of the block to fetch links from 89 * @param branch - Optional branch name to fetch links from (defaults to 'main') 90 * @param cursor - Optional cursor for pagination 91 * @param limit - Optional limit for number of results 92 * @returns Promise resolving to a validated paginated links response 93 */ 94 export async function fetchLinksFrom( 95 blockId: string, 96 branch?: string, 97 cursor?: string, 98 limit?: number 99 ): Promise<LinksResponse> { 100 const url = new URL(`${API_URL}/links/from/${blockId}`, window.location.origin); 101 if (branch) { 102 url.searchParams.set('branch', branch); 103 } 104 if (cursor) { 105 url.searchParams.set('cursor', cursor); 106 } 107 if (limit !== undefined) { 108 url.searchParams.set('limit', limit.toString()); 109 } 110 111 const response = await fetch(url.toString()); 112 113 if (!response.ok) { 114 throw new Error(`Failed to fetch links from block: ${response.status} ${response.statusText}`); 115 } 116 117 const data = await response.json(); 118 119 // Validate the response data 120 if (!validateLinks(data)) { 121 throw new Error('Invalid links response from API'); 122 } 123 124 return data; 125 } 126 127 /** 128 * Fetches links to a specific block 129 * @param blockId - The ID of the block to fetch links to 130 * @param branch - Optional branch name to fetch links from (defaults to 'main') 131 * @param cursor - Optional cursor for pagination 132 * @param limit - Optional limit for number of results 133 * @returns Promise resolving to a validated paginated links response 134 */ 135 export async function fetchLinksTo( 136 blockId: string, 137 branch?: string, 138 cursor?: string, 139 limit?: number 140 ): Promise<LinksResponse> { 141 const url = new URL(`${API_URL}/links/to/${blockId}`, window.location.origin); 142 if (branch) { 143 url.searchParams.set('branch', branch); 144 } 145 if (cursor) { 146 url.searchParams.set('cursor', cursor); 147 } 148 if (limit !== undefined) { 149 url.searchParams.set('limit', limit.toString()); 150 } 151 152 const response = await fetch(url.toString()); 153 154 if (!response.ok) { 155 throw new Error(`Failed to fetch links to block: ${response.status} ${response.statusText}`); 156 } 157 158 const data = await response.json(); 159 160 // Validate the response data 161 if (!validateLinks(data)) { 162 throw new Error('Invalid links response from API'); 163 } 164 165 return data; 166 }