This repository has no description
24 kB
649 lines
1import { BaseClient } from './BaseClient';
2import {
3 GetUrlMetadataResponse,
4 GetUrlCardsResponse,
5 GetUrlCardViewResponse,
6 GetLibrariesForCardResponse,
7 GetProfileResponse,
8 GetCollectionPageResponse,
9 GetCollectionsResponse,
10 GetUrlStatusForMyLibraryResponse,
11 GetUrlMetadataParams,
12 GetMyUrlCardsParams,
13 GetUrlCardsParams,
14 GetCollectionPageParams,
15 GetMyCollectionsParams,
16 GetCollectionsParams,
17 GetCollectionPageByAtUriParams,
18 GetProfileParams,
19 GetUrlStatusForMyLibraryParams,
20 GetLibrariesForUrlParams,
21 GetLibrariesForUrlResponse,
22 GetNoteCardsForUrlParams,
23 GetNoteCardsForUrlResponse,
24 GetCollectionsForUrlParams,
25 GetCollectionsForUrlResponse,
26 GetSimilarUrlsForUrlParams,
27 GetSimilarUrlsForUrlResponse,
28 SemanticSearchUrlsParams,
29 SemanticSearchUrlsResponse,
30 SearchBskyPostsForUrlParams,
31 SearchBskyPostsForUrlResponse,
32 SearchAtProtoAccountsParams,
33 SearchAtProtoAccountsResponse,
34 SearchLeafletDocsForUrlParams,
35 SearchLeafletDocsForUrlResponse,
36 GetOpenCollectionsWithContributorParams,
37 GetFollowingUsersParams,
38 GetFollowingUsersResponse,
39 GetFollowersParams,
40 GetFollowersResponse,
41 GetFollowingCollectionsParams,
42 GetFollowingCollectionsResponse,
43 GetCollectionFollowersParams,
44 GetCollectionFollowersResponse,
45 GetFollowingCountParams,
46 GetFollowersCountParams,
47 GetFollowingCollectionsCountParams,
48 GetCollectionFollowersCountParams,
49 GetFollowCountResponse,
50 GetCollectionContributorsParams,
51 GetCollectionContributorsResponse,
52 GetConnectionsParams,
53 GetConnectionsResponse,
54 SearchUrlsParams,
55 SearchUrlsResponse,
56 GetGraphDataParams,
57 GetGraphDataResponse,
58 GetConnectionsForUrlParams,
59 GetConnectionsForUrlResponse,
60 GetUrlGraphDataParams,
61} from '@semble/types';
62
63export class QueryClient extends BaseClient {
64 async getUrlMetadata(
65 params: GetUrlMetadataParams,
66 ): Promise<GetUrlMetadataResponse> {
67 const queryParams = new URLSearchParams({ url: params.url });
68 if (params.includeStats !== undefined) {
69 queryParams.set('includeStats', params.includeStats.toString());
70 }
71 return this.request<GetUrlMetadataResponse>(
72 'GET',
73 `/api/cards/metadata?${queryParams}`,
74 );
75 }
76
77 async getMyUrlCards(
78 params?: GetMyUrlCardsParams,
79 ): Promise<GetUrlCardsResponse> {
80 const searchParams = new URLSearchParams();
81 if (params?.page) searchParams.set('page', params.page.toString());
82 if (params?.limit) searchParams.set('limit', params.limit.toString());
83 if (params?.sortBy) searchParams.set('sortBy', params.sortBy);
84 if (params?.sortOrder) searchParams.set('sortOrder', params.sortOrder);
85 if (params?.urlType) searchParams.set('urlType', params.urlType);
86 if (params?.uncollected) searchParams.set('uncollected', 'true');
87
88 const queryString = searchParams.toString();
89 const endpoint = queryString
90 ? `/api/cards/my?${queryString}`
91 : '/api/cards/my';
92
93 return this.request<GetUrlCardsResponse>('GET', endpoint);
94 }
95
96 async getUserUrlCards(
97 params: GetUrlCardsParams,
98 ): Promise<GetUrlCardsResponse> {
99 const searchParams = new URLSearchParams();
100 if (params.page) searchParams.set('page', params.page.toString());
101 if (params.limit) searchParams.set('limit', params.limit.toString());
102 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
103 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
104 if (params.urlType) searchParams.set('urlType', params.urlType);
105 if (params.uncollected) searchParams.set('uncollected', 'true');
106
107 const queryString = searchParams.toString();
108 const endpoint = queryString
109 ? `/api/cards/user/${params.identifier}?${queryString}`
110 : `/api/cards/user/${params.identifier}`;
111
112 return this.request<GetUrlCardsResponse>('GET', endpoint);
113 }
114
115 async getUrlCardView(cardId: string): Promise<GetUrlCardViewResponse> {
116 return this.request<GetUrlCardViewResponse>('GET', `/api/cards/${cardId}`);
117 }
118
119 async getLibrariesForCard(
120 cardId: string,
121 ): Promise<GetLibrariesForCardResponse> {
122 return this.request<GetLibrariesForCardResponse>(
123 'GET',
124 `/api/cards/${cardId}/libraries`,
125 );
126 }
127
128 async getMyProfile(params?: {
129 includeStats?: boolean;
130 }): Promise<GetProfileResponse> {
131 const queryParams = new URLSearchParams();
132 if (params?.includeStats !== undefined) {
133 queryParams.set('includeStats', params.includeStats.toString());
134 }
135 const queryString = queryParams.toString();
136 return this.request<GetProfileResponse>(
137 'GET',
138 `/api/users/me${queryString ? `?${queryString}` : ''}`,
139 );
140 }
141
142 async getUserProfile(params: GetProfileParams): Promise<GetProfileResponse> {
143 const queryParams = new URLSearchParams();
144 if (params.includeStats !== undefined) {
145 queryParams.set('includeStats', params.includeStats.toString());
146 }
147 const queryString = queryParams.toString();
148 return this.request<GetProfileResponse>(
149 'GET',
150 `/api/users/${params.identifier}${queryString ? `?${queryString}` : ''}`,
151 );
152 }
153
154 async getCollectionPage(
155 collectionId: string,
156 params?: GetCollectionPageParams,
157 ): Promise<GetCollectionPageResponse> {
158 const searchParams = new URLSearchParams();
159 if (params?.page) searchParams.set('page', params.page.toString());
160 if (params?.limit) searchParams.set('limit', params.limit.toString());
161 if (params?.sortBy) searchParams.set('sortBy', params.sortBy);
162 if (params?.sortOrder) searchParams.set('sortOrder', params.sortOrder);
163 if (params?.urlType) searchParams.set('urlType', params.urlType);
164
165 const queryString = searchParams.toString();
166 const endpoint = queryString
167 ? `/api/collections/${collectionId}?${queryString}`
168 : `/api/collections/${collectionId}`;
169
170 return this.request<GetCollectionPageResponse>('GET', endpoint);
171 }
172
173 async getCollectionPageByAtUri(
174 params: GetCollectionPageByAtUriParams,
175 ): Promise<GetCollectionPageResponse> {
176 const { handle, recordKey, ...queryParams } = params;
177 const searchParams = new URLSearchParams();
178
179 if (queryParams.page) searchParams.set('page', queryParams.page.toString());
180 if (queryParams.limit)
181 searchParams.set('limit', queryParams.limit.toString());
182 if (queryParams.sortBy) searchParams.set('sortBy', queryParams.sortBy);
183 if (queryParams.sortOrder)
184 searchParams.set('sortOrder', queryParams.sortOrder);
185 if (queryParams.urlType) searchParams.set('urlType', queryParams.urlType);
186
187 const queryString = searchParams.toString();
188 const endpoint = queryString
189 ? `/api/collections/at/${handle}/${recordKey}?${queryString}`
190 : `/api/collections/at/${handle}/${recordKey}`;
191
192 return this.request<GetCollectionPageResponse>('GET', endpoint);
193 }
194
195 async getMyCollections(
196 params?: GetMyCollectionsParams,
197 ): Promise<GetCollectionsResponse> {
198 const searchParams = new URLSearchParams();
199 if (params?.page) searchParams.set('page', params.page.toString());
200 if (params?.limit) searchParams.set('limit', params.limit.toString());
201 if (params?.sortBy) searchParams.set('sortBy', params.sortBy);
202 if (params?.sortOrder) searchParams.set('sortOrder', params.sortOrder);
203 if (params?.searchText) searchParams.set('searchText', params.searchText);
204
205 const queryString = searchParams.toString();
206 const endpoint = queryString
207 ? `/api/collections?${queryString}`
208 : '/api/collections';
209
210 return this.request<GetCollectionsResponse>('GET', endpoint);
211 }
212
213 async getUserCollections(
214 params: GetCollectionsParams,
215 ): Promise<GetCollectionsResponse> {
216 const searchParams = new URLSearchParams();
217 if (params.page) searchParams.set('page', params.page.toString());
218 if (params.limit) searchParams.set('limit', params.limit.toString());
219 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
220 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
221 if (params.searchText) searchParams.set('searchText', params.searchText);
222
223 const queryString = searchParams.toString();
224 const endpoint = queryString
225 ? `/api/collections/user/${params.identifier}?${queryString}`
226 : `/api/collections/user/${params.identifier}`;
227
228 return this.request<GetCollectionsResponse>('GET', endpoint);
229 }
230
231 async getUrlStatusForMyLibrary(
232 params: GetUrlStatusForMyLibraryParams,
233 ): Promise<GetUrlStatusForMyLibraryResponse> {
234 const searchParams = new URLSearchParams({ url: params.url });
235 return this.request<GetUrlStatusForMyLibraryResponse>(
236 'GET',
237 `/api/cards/library/status?${searchParams}`,
238 );
239 }
240
241 async getLibrariesForUrl(
242 params: GetLibrariesForUrlParams,
243 ): Promise<GetLibrariesForUrlResponse> {
244 const searchParams = new URLSearchParams({ url: params.url });
245 if (params.page) searchParams.set('page', params.page.toString());
246 if (params.limit) searchParams.set('limit', params.limit.toString());
247 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
248 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
249
250 return this.request<GetLibrariesForUrlResponse>(
251 'GET',
252 `/api/cards/libraries/url?${searchParams}`,
253 );
254 }
255
256 async getNoteCardsForUrl(
257 params: GetNoteCardsForUrlParams,
258 ): Promise<GetNoteCardsForUrlResponse> {
259 const searchParams = new URLSearchParams({ url: params.url });
260 if (params.page) searchParams.set('page', params.page.toString());
261 if (params.limit) searchParams.set('limit', params.limit.toString());
262 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
263 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
264
265 return this.request<GetNoteCardsForUrlResponse>(
266 'GET',
267 `/api/cards/notes/url?${searchParams}`,
268 );
269 }
270
271 async getCollectionsForUrl(
272 params: GetCollectionsForUrlParams,
273 ): Promise<GetCollectionsForUrlResponse> {
274 const searchParams = new URLSearchParams({ url: params.url });
275 if (params.page) searchParams.set('page', params.page.toString());
276 if (params.limit) searchParams.set('limit', params.limit.toString());
277 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
278 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
279
280 return this.request<GetCollectionsForUrlResponse>(
281 'GET',
282 `/api/collections/url?${searchParams}`,
283 );
284 }
285
286 async getSimilarUrlsForUrl(
287 params: GetSimilarUrlsForUrlParams,
288 ): Promise<GetSimilarUrlsForUrlResponse> {
289 const searchParams = new URLSearchParams({ url: params.url });
290 if (params.page) searchParams.set('page', params.page.toString());
291 if (params.limit) searchParams.set('limit', params.limit.toString());
292 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
293 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
294 if (params.threshold)
295 searchParams.set('threshold', params.threshold.toString());
296 if (params.urlType) searchParams.set('urlType', params.urlType);
297
298 return this.request<GetSimilarUrlsForUrlResponse>(
299 'GET',
300 `/api/search/similar-urls?${searchParams}`,
301 );
302 }
303
304 async semanticSearchUrls(
305 params: SemanticSearchUrlsParams,
306 ): Promise<SemanticSearchUrlsResponse> {
307 const searchParams = new URLSearchParams({ query: params.query });
308 if (params.page) searchParams.set('page', params.page.toString());
309 if (params.limit) searchParams.set('limit', params.limit.toString());
310 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
311 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
312 if (params.threshold)
313 searchParams.set('threshold', params.threshold.toString());
314 if (params.urlType) searchParams.set('urlType', params.urlType);
315 if (params.identifier) searchParams.set('identifier', params.identifier);
316
317 return this.request<SemanticSearchUrlsResponse>(
318 'GET',
319 `/api/search/semantic?${searchParams}`,
320 );
321 }
322
323 async searchBskyPosts(
324 params: SearchBskyPostsForUrlParams,
325 ): Promise<SearchBskyPostsForUrlResponse> {
326 const searchParams = new URLSearchParams({ q: params.q });
327 if (params.sort) searchParams.set('sort', params.sort);
328 if (params.since) searchParams.set('since', params.since);
329 if (params.until) searchParams.set('until', params.until);
330 if (params.mentions) searchParams.set('mentions', params.mentions);
331 if (params.author) searchParams.set('author', params.author);
332 if (params.lang) searchParams.set('lang', params.lang);
333 if (params.domain) searchParams.set('domain', params.domain);
334 if (params.url) searchParams.set('url', params.url);
335 if (params.tag) {
336 params.tag.forEach((t) => searchParams.append('tag', t));
337 }
338 if (params.limit) searchParams.set('limit', params.limit.toString());
339 if (params.cursor) searchParams.set('cursor', params.cursor);
340
341 return this.request<SearchBskyPostsForUrlResponse>(
342 'GET',
343 `/api/search/bsky-posts?${searchParams}`,
344 );
345 }
346
347 async searchAtProtoAccounts(
348 params: SearchAtProtoAccountsParams,
349 ): Promise<SearchAtProtoAccountsResponse> {
350 const searchParams = new URLSearchParams();
351 if (params.term) searchParams.set('term', params.term);
352 if (params.q) searchParams.set('q', params.q);
353 if (params.limit) searchParams.set('limit', params.limit.toString());
354 if (params.cursor) searchParams.set('cursor', params.cursor);
355
356 return this.request<SearchAtProtoAccountsResponse>(
357 'GET',
358 `/api/search/accounts?${searchParams}`,
359 );
360 }
361
362 async searchLeafletDocs(
363 params: SearchLeafletDocsForUrlParams,
364 ): Promise<SearchLeafletDocsForUrlResponse> {
365 const searchParams = new URLSearchParams({ url: params.url });
366 if (params.limit) searchParams.set('limit', params.limit.toString());
367 if (params.cursor) searchParams.set('cursor', params.cursor);
368
369 return this.request<SearchLeafletDocsForUrlResponse>(
370 'GET',
371 `/api/search/leaflet-docs?${searchParams}`,
372 );
373 }
374
375 async getOpenCollectionsWithContributor(
376 params: GetOpenCollectionsWithContributorParams,
377 ): Promise<GetCollectionsResponse> {
378 const searchParams = new URLSearchParams();
379 if (params.page) searchParams.set('page', params.page.toString());
380 if (params.limit) searchParams.set('limit', params.limit.toString());
381 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
382 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
383
384 const queryString = searchParams.toString();
385 const endpoint = queryString
386 ? `/api/collections/contributed/${params.identifier}?${queryString}`
387 : `/api/collections/contributed/${params.identifier}`;
388
389 return this.request<GetCollectionsResponse>('GET', endpoint);
390 }
391
392 // Follow query methods
393 async getFollowingUsers(
394 params: GetFollowingUsersParams,
395 ): Promise<GetFollowingUsersResponse> {
396 const searchParams = new URLSearchParams();
397 if (params.page) searchParams.set('page', params.page.toString());
398 if (params.limit) searchParams.set('limit', params.limit.toString());
399
400 const queryString = searchParams.toString();
401 const endpoint = queryString
402 ? `/api/users/${params.identifier}/following?${queryString}`
403 : `/api/users/${params.identifier}/following`;
404
405 return this.request<GetFollowingUsersResponse>('GET', endpoint);
406 }
407
408 async getFollowers(
409 params: GetFollowersParams,
410 ): Promise<GetFollowersResponse> {
411 const searchParams = new URLSearchParams();
412 if (params.page) searchParams.set('page', params.page.toString());
413 if (params.limit) searchParams.set('limit', params.limit.toString());
414
415 const queryString = searchParams.toString();
416 const endpoint = queryString
417 ? `/api/users/${params.identifier}/followers?${queryString}`
418 : `/api/users/${params.identifier}/followers`;
419
420 return this.request<GetFollowersResponse>('GET', endpoint);
421 }
422
423 async getFollowingCollections(
424 params: GetFollowingCollectionsParams,
425 ): Promise<GetFollowingCollectionsResponse> {
426 const searchParams = new URLSearchParams();
427 if (params.page) searchParams.set('page', params.page.toString());
428 if (params.limit) searchParams.set('limit', params.limit.toString());
429
430 const queryString = searchParams.toString();
431 const endpoint = queryString
432 ? `/api/users/${params.identifier}/following-collections?${queryString}`
433 : `/api/users/${params.identifier}/following-collections`;
434
435 return this.request<GetFollowingCollectionsResponse>('GET', endpoint);
436 }
437
438 async getCollectionFollowers(
439 params: GetCollectionFollowersParams,
440 ): Promise<GetCollectionFollowersResponse> {
441 const searchParams = new URLSearchParams();
442 if (params.page) searchParams.set('page', params.page.toString());
443 if (params.limit) searchParams.set('limit', params.limit.toString());
444
445 const queryString = searchParams.toString();
446 const endpoint = queryString
447 ? `/api/collections/${params.collectionId}/followers?${queryString}`
448 : `/api/collections/${params.collectionId}/followers`;
449
450 return this.request<GetCollectionFollowersResponse>('GET', endpoint);
451 }
452
453 async getFollowingCount(
454 params: GetFollowingCountParams,
455 ): Promise<GetFollowCountResponse> {
456 return this.request<GetFollowCountResponse>(
457 'GET',
458 `/api/users/${params.identifier}/following/count`,
459 );
460 }
461
462 async getFollowersCount(
463 params: GetFollowersCountParams,
464 ): Promise<GetFollowCountResponse> {
465 return this.request<GetFollowCountResponse>(
466 'GET',
467 `/api/users/${params.identifier}/followers/count`,
468 );
469 }
470
471 async getFollowingCollectionsCount(
472 params: GetFollowingCollectionsCountParams,
473 ): Promise<GetFollowCountResponse> {
474 return this.request<GetFollowCountResponse>(
475 'GET',
476 `/api/users/${params.identifier}/following-collections/count`,
477 );
478 }
479
480 async getCollectionFollowersCount(
481 params: GetCollectionFollowersCountParams,
482 ): Promise<GetFollowCountResponse> {
483 return this.request<GetFollowCountResponse>(
484 'GET',
485 `/api/collections/${params.collectionId}/followers/count`,
486 );
487 }
488
489 async getCollectionContributors(
490 params: GetCollectionContributorsParams,
491 ): Promise<GetCollectionContributorsResponse> {
492 const searchParams = new URLSearchParams();
493 if (params.page) searchParams.set('page', params.page.toString());
494 if (params.limit) searchParams.set('limit', params.limit.toString());
495
496 const queryString = searchParams.toString();
497 const endpoint = queryString
498 ? `/api/collections/${params.collectionId}/contributors?${queryString}`
499 : `/api/collections/${params.collectionId}/contributors`;
500
501 return this.request<GetCollectionContributorsResponse>('GET', endpoint);
502 }
503
504 async getConnectionsForUrl(
505 params: GetConnectionsForUrlParams,
506 ): Promise<GetConnectionsForUrlResponse> {
507 const searchParams = new URLSearchParams();
508 searchParams.set('url', params.url);
509 if (params.direction) searchParams.set('direction', params.direction);
510 if (params.page) searchParams.set('page', params.page.toString());
511 if (params.limit) searchParams.set('limit', params.limit.toString());
512 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
513 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
514 if (params.connectionTypes) {
515 searchParams.set('connectionTypes', params.connectionTypes.join(','));
516 }
517
518 return this.request<GetConnectionsForUrlResponse>(
519 'GET',
520 `/api/connections/url?${searchParams}`,
521 );
522 }
523
524 async getConnections(
525 params: GetConnectionsParams,
526 ): Promise<GetConnectionsResponse> {
527 const searchParams = new URLSearchParams();
528 if (params.page) searchParams.set('page', params.page.toString());
529 if (params.limit) searchParams.set('limit', params.limit.toString());
530 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
531 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
532 if (params.connectionTypes) {
533 searchParams.set('connectionTypes', params.connectionTypes.join(','));
534 }
535
536 const queryString = searchParams.toString();
537 const endpoint = queryString
538 ? `/api/connections/user/${params.identifier}?${queryString}`
539 : `/api/connections/user/${params.identifier}`;
540
541 return this.request<GetConnectionsResponse>('GET', endpoint);
542 }
543
544 async searchUrls(params: SearchUrlsParams): Promise<SearchUrlsResponse> {
545 const searchParams = new URLSearchParams();
546 searchParams.set('searchQuery', params.searchQuery);
547 if (params.page) searchParams.set('page', params.page.toString());
548 if (params.limit) searchParams.set('limit', params.limit.toString());
549 if (params.sortBy) searchParams.set('sortBy', params.sortBy);
550 if (params.sortOrder) searchParams.set('sortOrder', params.sortOrder);
551 if (params.urlType) searchParams.set('urlType', params.urlType);
552
553 return this.request<SearchUrlsResponse>(
554 'GET',
555 `/api/cards/search?${searchParams}`,
556 );
557 }
558
559 async getUserGraphData(params: {
560 identifier: string;
561 page?: number;
562 limit?: number;
563 }): Promise<GetGraphDataResponse> {
564 // Build query string with pagination parameters
565 const searchParams = new URLSearchParams();
566 if (params?.page) searchParams.set('page', params.page.toString());
567 if (params?.limit) searchParams.set('limit', params.limit.toString());
568
569 const queryString = searchParams.toString();
570 const endpoint = queryString
571 ? `/api/graph/user/${params.identifier}?${queryString}`
572 : `/api/graph/user/${params.identifier}`;
573
574 return this.request<GetGraphDataResponse>('GET', endpoint);
575 }
576
577 async getUrlGraphData(
578 params: GetUrlGraphDataParams,
579 ): Promise<GetGraphDataResponse> {
580 // Build query string with url and depth parameters
581 const searchParams = new URLSearchParams();
582 searchParams.set('url', params.url);
583 if (params.depth) searchParams.set('depth', params.depth.toString());
584
585 const endpoint = `/api/graph/url?${searchParams.toString()}`;
586
587 return this.request<GetGraphDataResponse>('GET', endpoint);
588 }
589
590 async getGraphData(
591 params?: GetGraphDataParams,
592 ): Promise<GetGraphDataResponse> {
593 // Check if mock data should be used (for performance testing)
594 // Set NEXT_PUBLIC_USE_MOCK_GRAPH_DATA=true in .env.local to enable
595 const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_GRAPH_DATA === 'true';
596 // const useMockData = true;
597
598 if (useMockData) {
599 const { generateMockGraphData, MOCK_GRAPH_PRESETS } = await import(
600 './mockGraphData'
601 );
602
603 // You can change the preset here for different test scenarios
604 // Options: small, medium, large, extraLarge, denseSmall
605 const mockData = generateMockGraphData(MOCK_GRAPH_PRESETS.large);
606
607 // Simulate network delay for realistic testing
608 await new Promise((resolve) => setTimeout(resolve, 500));
609
610 // Apply pagination to mock data
611 const page = params?.page || 1;
612 const limit = params?.limit || 300;
613 const offset = (page - 1) * limit;
614 const totalCount = mockData.nodes.length;
615 const totalPages = Math.ceil(totalCount / limit);
616 const hasMore = page < totalPages;
617
618 const paginatedNodes = mockData.nodes.slice(offset, offset + limit);
619 const loadedNodeIds = new Set(paginatedNodes.map((n) => n.id));
620 const filteredEdges = mockData.edges.filter(
621 (e) => loadedNodeIds.has(e.source) && loadedNodeIds.has(e.target),
622 );
623
624 return {
625 nodes: paginatedNodes,
626 edges: filteredEdges,
627 pagination: {
628 currentPage: page,
629 totalPages,
630 totalCount,
631 hasMore,
632 limit,
633 },
634 };
635 }
636
637 // Build query string with pagination parameters
638 const searchParams = new URLSearchParams();
639 if (params?.page) searchParams.set('page', params.page.toString());
640 if (params?.limit) searchParams.set('limit', params.limit.toString());
641
642 const queryString = searchParams.toString();
643 const endpoint = queryString
644 ? `/api/graph/data?${queryString}`
645 : '/api/graph/data';
646
647 return this.request<GetGraphDataResponse>('GET', endpoint);
648 }
649}