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();