A social knowledge tool for researchers built on ATProto
1import { 2 QueryClient, 3 CardClient, 4 CollectionClient, 5 UserClient, 6 FeedClient, 7} from './clients'; 8import { TokenManager } from '../services/TokenManager'; 9import { 10 createClientTokenManager, 11 createServerTokenManager, 12} from '../services/auth'; 13import type { 14 // Request types 15 AddUrlToLibraryRequest, 16 AddCardToLibraryRequest, 17 AddCardToCollectionRequest, 18 UpdateNoteCardRequest, 19 RemoveCardFromLibraryRequest, 20 RemoveCardFromCollectionRequest, 21 CreateCollectionRequest, 22 UpdateCollectionRequest, 23 DeleteCollectionRequest, 24 LoginWithAppPasswordRequest, 25 InitiateOAuthSignInRequest, 26 CompleteOAuthSignInRequest, 27 RefreshAccessTokenRequest, 28 GenerateExtensionTokensRequest, 29 GetMyUrlCardsParams, 30 GetCollectionPageParams, 31 GetCollectionPageByAtUriParams, 32 GetMyCollectionsParams, 33 GetGlobalFeedParams, 34 // Response types 35 AddUrlToLibraryResponse, 36 AddCardToLibraryResponse, 37 AddCardToCollectionResponse, 38 UpdateNoteCardResponse, 39 RemoveCardFromLibraryResponse, 40 RemoveCardFromCollectionResponse, 41 CreateCollectionResponse, 42 UpdateCollectionResponse, 43 DeleteCollectionResponse, 44 LoginWithAppPasswordResponse, 45 InitiateOAuthSignInResponse, 46 CompleteOAuthSignInResponse, 47 RefreshAccessTokenResponse, 48 GenerateExtensionTokensResponse, 49 GetUrlMetadataResponse, 50 GetUrlCardViewResponse, 51 GetLibrariesForCardResponse, 52 GetCollectionPageResponse, 53 GetGlobalFeedResponse, 54 GetCollectionsResponse, 55 GetCollectionsParams, 56 GetUrlCardsParams, 57 GetUrlCardsResponse, 58 GetProfileResponse, 59 GetProfileParams, 60 GetUrlStatusForMyLibraryParams, 61 GetUrlStatusForMyLibraryResponse, 62 GetLibrariesForUrlParams, 63 GetLibrariesForUrlResponse, 64 GetNoteCardsForUrlParams, 65 GetNoteCardsForUrlResponse, 66 GetCollectionsForUrlParams, 67 GetCollectionsForUrlResponse, 68} from './types'; 69 70// Main API Client class using composition 71export class ApiClient { 72 private queryClient: QueryClient; 73 private cardClient: CardClient; 74 private collectionClient: CollectionClient; 75 private userClient: UserClient; 76 private feedClient: FeedClient; 77 78 constructor( 79 private baseUrl: string, 80 private tokenManager?: TokenManager, 81 ) { 82 this.queryClient = new QueryClient(baseUrl, tokenManager); 83 this.cardClient = new CardClient(baseUrl, tokenManager); 84 this.collectionClient = new CollectionClient(baseUrl, tokenManager); 85 this.userClient = new UserClient(baseUrl, tokenManager); 86 this.feedClient = new FeedClient(baseUrl, tokenManager); 87 } 88 89 // Helper to check if client is authenticated 90 get isAuthenticated(): boolean { 91 return !!this.tokenManager; 92 } 93 94 // Helper method to ensure authentication for protected operations 95 private requireAuthentication(operation: string): void { 96 if (!this.tokenManager) { 97 throw new Error(`Authentication required for ${operation}`); 98 } 99 } 100 101 // Query operations - delegate to QueryClient 102 async getUrlMetadata(url: string): Promise<GetUrlMetadataResponse> { 103 return this.queryClient.getUrlMetadata(url); 104 } 105 106 async getMyUrlCards( 107 params?: GetMyUrlCardsParams, 108 ): Promise<GetUrlCardsResponse> { 109 this.requireAuthentication('getMyUrlCards'); 110 return this.queryClient.getMyUrlCards(params); 111 } 112 113 async getUrlCards(params: GetUrlCardsParams): Promise<GetUrlCardsResponse> { 114 return this.queryClient.getUserUrlCards(params); 115 } 116 117 async getUrlCardView(cardId: string): Promise<GetUrlCardViewResponse> { 118 return this.queryClient.getUrlCardView(cardId); 119 } 120 121 async getLibrariesForCard( 122 cardId: string, 123 ): Promise<GetLibrariesForCardResponse> { 124 this.requireAuthentication('getLibrariesForCard'); 125 return this.queryClient.getLibrariesForCard(cardId); 126 } 127 128 async getMyProfile(): Promise<GetProfileResponse> { 129 this.requireAuthentication('getMyProfile'); 130 return this.queryClient.getMyProfile(); 131 } 132 133 async getProfile(params: GetProfileParams): Promise<GetProfileResponse> { 134 return this.queryClient.getUserProfile(params); 135 } 136 137 async getCollectionPage( 138 collectionId: string, 139 params?: GetCollectionPageParams, 140 ): Promise<GetCollectionPageResponse> { 141 return this.queryClient.getCollectionPage(collectionId, params); 142 } 143 144 async getCollectionPageByAtUri( 145 params: GetCollectionPageByAtUriParams, 146 ): Promise<GetCollectionPageResponse> { 147 return this.queryClient.getCollectionPageByAtUri(params); 148 } 149 150 async getMyCollections( 151 params?: GetMyCollectionsParams, 152 ): Promise<GetCollectionsResponse> { 153 this.requireAuthentication('getMyCollections'); 154 return this.queryClient.getMyCollections(params); 155 } 156 157 async getCollections( 158 params: GetCollectionsParams, 159 ): Promise<GetCollectionsResponse> { 160 return this.queryClient.getUserCollections(params); 161 } 162 163 async getUrlStatusForMyLibrary( 164 params: GetUrlStatusForMyLibraryParams, 165 ): Promise<GetUrlStatusForMyLibraryResponse> { 166 this.requireAuthentication('getUrlStatusForMyLibrary'); 167 return this.queryClient.getUrlStatusForMyLibrary(params); 168 } 169 170 async getLibrariesForUrl( 171 params: GetLibrariesForUrlParams, 172 ): Promise<GetLibrariesForUrlResponse> { 173 return this.queryClient.getLibrariesForUrl(params); 174 } 175 176 async getNoteCardsForUrl( 177 params: GetNoteCardsForUrlParams, 178 ): Promise<GetNoteCardsForUrlResponse> { 179 return this.queryClient.getNoteCardsForUrl(params); 180 } 181 182 async getCollectionsForUrl( 183 params: GetCollectionsForUrlParams, 184 ): Promise<GetCollectionsForUrlResponse> { 185 return this.queryClient.getCollectionsForUrl(params); 186 } 187 188 // Card operations - delegate to CardClient (all require authentication) 189 async addUrlToLibrary( 190 request: AddUrlToLibraryRequest, 191 ): Promise<AddUrlToLibraryResponse> { 192 this.requireAuthentication('addUrlToLibrary'); 193 return this.cardClient.addUrlToLibrary(request); 194 } 195 196 async addCardToLibrary( 197 request: AddCardToLibraryRequest, 198 ): Promise<AddCardToLibraryResponse> { 199 this.requireAuthentication('addCardToLibrary'); 200 return this.cardClient.addCardToLibrary(request); 201 } 202 203 async addCardToCollection( 204 request: AddCardToCollectionRequest, 205 ): Promise<AddCardToCollectionResponse> { 206 this.requireAuthentication('addCardToCollection'); 207 return this.cardClient.addCardToCollection(request); 208 } 209 210 async updateNoteCard( 211 request: UpdateNoteCardRequest, 212 ): Promise<UpdateNoteCardResponse> { 213 this.requireAuthentication('updateNoteCard'); 214 return this.cardClient.updateNoteCard(request); 215 } 216 217 async removeCardFromLibrary( 218 request: RemoveCardFromLibraryRequest, 219 ): Promise<RemoveCardFromLibraryResponse> { 220 this.requireAuthentication('removeCardFromLibrary'); 221 return this.cardClient.removeCardFromLibrary(request); 222 } 223 224 async removeCardFromCollection( 225 request: RemoveCardFromCollectionRequest, 226 ): Promise<RemoveCardFromCollectionResponse> { 227 this.requireAuthentication('removeCardFromCollection'); 228 return this.cardClient.removeCardFromCollection(request); 229 } 230 231 // Collection operations - delegate to CollectionClient (all require authentication) 232 async createCollection( 233 request: CreateCollectionRequest, 234 ): Promise<CreateCollectionResponse> { 235 this.requireAuthentication('createCollection'); 236 return this.collectionClient.createCollection(request); 237 } 238 239 async updateCollection( 240 request: UpdateCollectionRequest, 241 ): Promise<UpdateCollectionResponse> { 242 this.requireAuthentication('updateCollection'); 243 return this.collectionClient.updateCollection(request); 244 } 245 246 async deleteCollection( 247 request: DeleteCollectionRequest, 248 ): Promise<DeleteCollectionResponse> { 249 this.requireAuthentication('deleteCollection'); 250 return this.collectionClient.deleteCollection(request); 251 } 252 253 // User operations - delegate to UserClient 254 async loginWithAppPassword( 255 request: LoginWithAppPasswordRequest, 256 ): Promise<LoginWithAppPasswordResponse> { 257 return this.userClient.loginWithAppPassword(request); 258 } 259 260 async initiateOAuthSignIn( 261 request?: InitiateOAuthSignInRequest, 262 ): Promise<InitiateOAuthSignInResponse> { 263 return this.userClient.initiateOAuthSignIn(request); 264 } 265 266 async completeOAuthSignIn( 267 request: CompleteOAuthSignInRequest, 268 ): Promise<CompleteOAuthSignInResponse> { 269 return this.userClient.completeOAuthSignIn(request); 270 } 271 272 async refreshAccessToken( 273 request: RefreshAccessTokenRequest, 274 ): Promise<RefreshAccessTokenResponse> { 275 this.requireAuthentication('refreshAccessToken'); 276 return this.userClient.refreshAccessToken(request); 277 } 278 279 async generateExtensionTokens( 280 request?: GenerateExtensionTokensRequest, 281 ): Promise<GenerateExtensionTokensResponse> { 282 this.requireAuthentication('generateExtensionTokens'); 283 return this.userClient.generateExtensionTokens(request); 284 } 285 286 async logout(): Promise<{ success: boolean; message: string }> { 287 this.requireAuthentication('logout'); 288 return this.userClient.logout(); 289 } 290 291 // Feed operations - delegate to FeedClient 292 async getGlobalFeed( 293 params?: GetGlobalFeedParams, 294 ): Promise<GetGlobalFeedResponse> { 295 return this.feedClient.getGlobalFeed(params); 296 } 297} 298 299// Re-export types for convenience 300export * from './types'; 301 302// Factory functions for different client types 303export const createAuthenticatedApiClient = () => { 304 return new ApiClient( 305 process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', 306 createClientTokenManager(), 307 ); 308}; 309 310export const createUnauthenticatedApiClient = () => { 311 return new ApiClient( 312 process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', 313 undefined, 314 ); 315}; 316 317// Factory function for server-side API client 318export const createServerApiClient = async () => { 319 const tokenManager = await createServerTokenManager(); 320 return new ApiClient( 321 process.env.API_BASE_URL || 'http://localhost:3000', 322 tokenManager, 323 ); 324}; 325 326// Default authenticated client instance for client-side usage (backward compatibility) 327export const apiClient = createAuthenticatedApiClient(); 328 329// Default unauthenticated client instance for public operations 330export const publicApiClient = createUnauthenticatedApiClient();