bsv-mcp 0.2.0 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.js +87272 -70728
  3. package/index.ts +409 -22
  4. package/package.json +28 -24
  5. package/tools/a2b/discover.ts +13 -13
  6. package/tools/bap/friend.ts +2 -3
  7. package/tools/bap/generate.ts +7 -9
  8. package/tools/bap/getCurrentAddress.ts +1 -8
  9. package/tools/bap/getId.ts +3 -3
  10. package/tools/bsocial/bmapFollow.ts +3 -7
  11. package/tools/bsocial/bmapLikes.ts +3 -3
  12. package/tools/bsocial/bmapReadPosts.ts +3 -3
  13. package/tools/bsocial/createPost.ts +3 -3
  14. package/tools/bsocial/readPosts.ts +3 -3
  15. package/tools/bsv/decodeTransaction.ts +2 -5
  16. package/tools/bsv/explore.ts +2 -3
  17. package/tools/bsv/getPrice.ts +1 -9
  18. package/tools/bsv/token.ts +14 -26
  19. package/tools/index.ts +0 -13
  20. package/tools/mnee/getBalance.ts +2 -4
  21. package/tools/mnee/parseTx.ts +3 -3
  22. package/tools/mnee/sendMnee.ts +5 -5
  23. package/tools/ordinals/getInscription.ts +2 -3
  24. package/tools/ordinals/getTokenByIdOrTicker.ts +6 -3
  25. package/tools/ordinals/marketListings.ts +2 -18
  26. package/tools/ordinals/marketSales.ts +2 -4
  27. package/tools/ordinals/searchInscriptions.ts +2 -4
  28. package/tools/utils/index.ts +14 -16
  29. package/tools/wallet/a2bPublishAgent.ts +18 -27
  30. package/tools/wallet/a2bPublishMcp.ts +17 -22
  31. package/tools/wallet/createOrdinals.ts +11 -20
  32. package/tools/wallet/fetchPaymentUtxos.ts +9 -1
  33. package/tools/wallet/gatherCollectionInfo.ts +9 -13
  34. package/tools/wallet/getAddress.ts +1 -9
  35. package/tools/wallet/getBalance.ts +2 -14
  36. package/tools/wallet/getBalanceDroplet.ts +2 -13
  37. package/tools/wallet/getPublicKey.ts +3 -16
  38. package/tools/wallet/mintCollection.ts +17 -22
  39. package/tools/wallet/purchaseListing.ts +19 -29
  40. package/tools/wallet/refreshUtxos.ts +2 -14
  41. package/tools/wallet/sendOrdinals.ts +11 -20
  42. package/tools/wallet/sendToAddress.ts +2 -22
  43. package/tools/wallet/setupDroplet.ts +7 -18
  44. package/tools/wallet/tools.ts +10 -80
  45. package/tools/wallet/transferOrdToken.ts +12 -20
  46. package/vite.config.ts +15 -0
  47. package/tools/bigblocks/components.ts +0 -304
  48. package/tools/bigblocks/docs.ts +0 -488
  49. package/tools/bigblocks/examples.ts +0 -527
  50. package/tools/bigblocks/generator.ts +0 -485
  51. package/tools/bigblocks/index.ts +0 -23
  52. package/tools/bsocial/bigblocksApiClient.ts +0 -189
@@ -1,485 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { basename, extname, resolve } from "node:path";
3
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
- import { z } from "zod";
5
-
6
- // TypeScript interfaces for BigBlocks registry
7
- interface BigBlocksComponent {
8
- name: string;
9
- type: string;
10
- description: string;
11
- files?: string[];
12
- dependencies?: string[];
13
- registryDependencies?: string[];
14
- }
15
-
16
- interface BigBlocksRegistry {
17
- components: BigBlocksComponent[];
18
- version?: string;
19
- }
20
-
21
- // Load BigBlocks registry data
22
- function loadBigBlocksRegistry(): BigBlocksRegistry | null {
23
- try {
24
- const registryPath = resolve(
25
- "node_modules/bigblocks/registry/registry.json",
26
- );
27
- const registryData = JSON.parse(readFileSync(registryPath, "utf-8"));
28
- return registryData as BigBlocksRegistry;
29
- } catch (error) {
30
- console.error("Failed to load BigBlocks registry:", error);
31
- return null;
32
- }
33
- }
34
-
35
- // Code generation templates
36
- const TEMPLATES = {
37
- "auth-setup": {
38
- description: "Complete authentication setup with providers",
39
- code: `import {
40
- BitcoinAuthProvider,
41
- BitcoinThemeProvider,
42
- BitcoinQueryProvider,
43
- type BitcoinAuthConfig
44
- } from 'bigblocks';
45
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
46
- import { Theme } from '@radix-ui/themes';
47
- import '@radix-ui/themes/styles.css';
48
-
49
- const queryClient = new QueryClient();
50
-
51
- const authConfig: BitcoinAuthConfig = {
52
- apiUrl: process.env.NEXT_PUBLIC_API_URL || '/api/auth',
53
- storage: {
54
- async get(key: string): Promise<string | null> {
55
- return localStorage.getItem(key);
56
- },
57
- async set(key: string, value: string): Promise<void> {
58
- localStorage.setItem(key, value);
59
- },
60
- async remove(key: string): Promise<void> {
61
- localStorage.removeItem(key);
62
- }
63
- },
64
- storageNamespace: 'my-app-auth',
65
- oauthProviders: ['google', 'github'],
66
- backupTypes: {
67
- enabled: ['BapMasterBackup', 'BapMemberBackup', 'OneSatBackup', 'WifBackup']
68
- }
69
- };
70
-
71
- export function AuthProvider({ children }: { children: React.ReactNode }) {
72
- return (
73
- <QueryClientProvider client={queryClient}>
74
- <Theme appearance="dark" accentColor="amber">
75
- <BitcoinThemeProvider>
76
- <BitcoinQueryProvider>
77
- <BitcoinAuthProvider config={authConfig}>
78
- {children}
79
- </BitcoinAuthProvider>
80
- </BitcoinQueryProvider>
81
- </BitcoinThemeProvider>
82
- </Theme>
83
- </QueryClientProvider>
84
- );
85
- }`,
86
- },
87
- "auth-flow": {
88
- description: "Complete authentication flow component",
89
- code: `import {
90
- AuthFlowOrchestrator,
91
- useBitcoinAuth,
92
- type AuthFlowType
93
- } from 'bigblocks';
94
- import { useState } from 'react';
95
-
96
- interface AuthFlowProps {
97
- onSuccess?: () => void;
98
- flowType?: AuthFlowType;
99
- }
100
-
101
- export function AuthFlow({ onSuccess, flowType = 'unified' }: AuthFlowProps) {
102
- const { user, isAuthenticated } = useBitcoinAuth();
103
-
104
- const handleSuccess = (user: any) => {
105
- console.log('Authentication successful:', user);
106
- onSuccess?.();
107
- };
108
-
109
- const handleError = (error: Error) => {
110
- console.error('Authentication error:', error);
111
- };
112
-
113
- if (isAuthenticated) {
114
- return (
115
- <div className="text-center">
116
- <h2>Welcome, {user?.profile?.name || 'User'}!</h2>
117
- <p>You are successfully authenticated.</p>
118
- </div>
119
- );
120
- }
121
-
122
- return (
123
- <AuthFlowOrchestrator
124
- flowType={flowType}
125
- enableOAuth={true}
126
- enableFileImport={true}
127
- enableLocalBackup={true}
128
- onSuccess={handleSuccess}
129
- onError={handleError}
130
- title="Sign In"
131
- subtitle="Secure authentication using Bitcoin signatures"
132
- showHeader={true}
133
- showFooter={true}
134
- layout="centered"
135
- autoDetectFlow={true}
136
- persistFlow={true}
137
- />
138
- );
139
- }`,
140
- },
141
- "social-feed": {
142
- description: "Social media feed with BigBlocks components",
143
- code: `import {
144
- SocialFeed,
145
- PostButton,
146
- LikeButton,
147
- FollowButton,
148
- PostCard
149
- } from 'bigblocks';
150
- import { useState, useEffect } from 'react';
151
-
152
- export function MySocialFeed() {
153
- const [posts, setPosts] = useState([]);
154
- const [loading, setLoading] = useState(true);
155
-
156
- const handleNewPost = (txid: string) => {
157
- console.log('New post created:', txid);
158
- // Refresh posts or add new post to feed
159
- loadPosts();
160
- };
161
-
162
- const loadPosts = async () => {
163
- try {
164
- // Fetch posts from your API or BMAP
165
- const response = await fetch('/api/posts');
166
- const data = await response.json();
167
- setPosts(data.posts || []);
168
- } catch (error) {
169
- console.error('Failed to load posts:', error);
170
- } finally {
171
- setLoading(false);
172
- }
173
- };
174
-
175
- useEffect(() => {
176
- loadPosts();
177
- }, []);
178
-
179
- return (
180
- <div className="max-w-2xl mx-auto p-4">
181
- <div className="mb-6">
182
- <PostButton
183
- onSuccess={handleNewPost}
184
- encryption={false}
185
- placeholder="What's happening?"
186
- />
187
- </div>
188
-
189
- <SocialFeed
190
- posts={posts}
191
- loading={loading}
192
- onLoadMore={() => {
193
- // Load more posts
194
- console.log('Load more posts');
195
- }}
196
- showActions={true}
197
- />
198
- </div>
199
- );
200
- }`,
201
- },
202
- "wallet-interface": {
203
- description: "Complete wallet interface with BigBlocks",
204
- code: `import {
205
- WalletOverview,
206
- SendBSVButton,
207
- TokenBalance,
208
- QuickSendButton,
209
- DonateButton
210
- } from 'bigblocks';
211
- import { useState } from 'react';
212
-
213
- export function WalletInterface() {
214
- const [selectedAmount, setSelectedAmount] = useState(0.001);
215
-
216
- const handleSendSuccess = (txid: string) => {
217
- console.log('Payment sent successfully:', txid);
218
- // Refresh wallet data
219
- };
220
-
221
- const handleDonation = (txid: string) => {
222
- console.log('Donation sent:', txid);
223
- };
224
-
225
- return (
226
- <div className="max-w-4xl mx-auto p-6">
227
- <h1 className="text-2xl font-bold mb-6">My Wallet</h1>
228
-
229
- {/* Wallet Overview */}
230
- <div className="mb-8">
231
- <WalletOverview
232
- showHistory={true}
233
- showTokens={true}
234
- compact={false}
235
- />
236
- </div>
237
-
238
- {/* Quick Actions */}
239
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
240
- <div className="space-y-4">
241
- <h2 className="text-lg font-semibold">Send BSV</h2>
242
- <SendBSVButton
243
- amount={selectedAmount}
244
- onSuccess={handleSendSuccess}
245
- confirmRequired={true}
246
- />
247
-
248
- <QuickSendButton
249
- presetAmounts={[0.001, 0.01, 0.1]}
250
- onSend={handleSendSuccess}
251
- />
252
- </div>
253
-
254
- <div className="space-y-4">
255
- <h2 className="text-lg font-semibold">Donations</h2>
256
- <DonateButton
257
- recipient="1DonationAddressExample..."
258
- amounts={[0.01, 0.05, 0.1]}
259
- message="Thank you for your support!"
260
- onDonate={handleDonation}
261
- />
262
- </div>
263
- </div>
264
-
265
- {/* Token Balances */}
266
- <div>
267
- <h2 className="text-lg font-semibold mb-4">Token Balances</h2>
268
- <TokenBalance
269
- showTokens={true}
270
- showUSD={true}
271
- refreshInterval={30000}
272
- />
273
- </div>
274
- </div>
275
- );
276
- }`,
277
- },
278
- };
279
-
280
- const generatorSchema = z.object({
281
- template: z
282
- .string()
283
- .optional()
284
- .describe(
285
- "Template to generate (auth-setup, auth-flow, social-feed, wallet-interface)",
286
- ),
287
- component: z
288
- .string()
289
- .optional()
290
- .describe("Specific component to generate code for"),
291
- framework: z
292
- .enum(["react", "next", "astro"])
293
- .optional()
294
- .describe("Framework-specific code generation"),
295
- listTemplates: z
296
- .boolean()
297
- .optional()
298
- .describe("List all available templates"),
299
- });
300
-
301
- /**
302
- * Register the BigBlocks code generator tool
303
- */
304
- export function registerBigBlocksGeneratorTool(server: McpServer): void {
305
- server.tool(
306
- "bigblocks_generate",
307
- "Generate BigBlocks integration code and boilerplate. Get ready-to-use code snippets for authentication, social features, wallet interfaces, and more.\n\n" +
308
- "Available templates:\n" +
309
- "- auth-setup: Complete authentication provider setup\n" +
310
- "- auth-flow: Authentication flow component\n" +
311
- "- social-feed: Social media feed interface\n" +
312
- "- wallet-interface: Complete wallet dashboard\n\n" +
313
- "Usage examples:\n" +
314
- '- Generate template: {"template": "auth-setup"}\n' +
315
- '- Component code: {"component": "auth-flow"}\n' +
316
- '- List templates: {"listTemplates": true}',
317
- { args: generatorSchema },
318
- async ({ args }) => {
319
- try {
320
- const { template, component, framework, listTemplates } = args;
321
-
322
- // List all templates
323
- if (listTemplates) {
324
- let result = "# BigBlocks Code Generator Templates\n\n";
325
-
326
- for (const [name, data] of Object.entries(TEMPLATES)) {
327
- result += `## ${name}\n`;
328
- result += `${data.description}\n\n`;
329
- }
330
-
331
- result +=
332
- 'Use {"template": "template-name"} to generate specific code.';
333
- return { content: [{ type: "text", text: result }] };
334
- }
335
-
336
- // Generate specific template
337
- if (template) {
338
- const templateData = TEMPLATES[template];
339
- if (!templateData) {
340
- const available = Object.keys(TEMPLATES).join(", ");
341
- return {
342
- content: [
343
- {
344
- type: "text",
345
- text: `Template "${template}" not found. Available templates: ${available}`,
346
- },
347
- ],
348
- isError: true,
349
- };
350
- }
351
-
352
- let result = `# ${template} - ${templateData.description}\n\n`;
353
- result += "```tsx\n";
354
- result += templateData.code;
355
- result += "\n```\n\n";
356
-
357
- // Add usage instructions
358
- result += "## Usage Instructions\n\n";
359
- if (template === "auth-setup") {
360
- result +=
361
- "1. Install dependencies: `npm install @tanstack/react-query @radix-ui/themes`\n";
362
- result += "2. Wrap your app with the AuthProvider component\n";
363
- result += "3. Configure your authentication API endpoint\n";
364
- result += "4. Customize the theme and storage options\n";
365
- } else if (template === "auth-flow") {
366
- result += "1. Use this component on your login/signup pages\n";
367
- result += "2. Customize the flow type and callbacks\n";
368
- result += "3. Handle success/error states appropriately\n";
369
- } else if (template === "social-feed") {
370
- result += "1. Implement your posts API endpoint\n";
371
- result += "2. Configure BMAP integration if needed\n";
372
- result += "3. Customize the feed appearance and actions\n";
373
- } else if (template === "wallet-interface") {
374
- result += "1. Ensure wallet provider is configured\n";
375
- result += "2. Set up transaction handling\n";
376
- result += "3. Configure recipient addresses for donations\n";
377
- }
378
-
379
- return { content: [{ type: "text", text: result }] };
380
- }
381
-
382
- // Generate code for specific component
383
- if (component) {
384
- const registry = loadBigBlocksRegistry();
385
- if (!registry) {
386
- return {
387
- content: [
388
- {
389
- type: "text",
390
- text: "Error: Could not load BigBlocks registry data",
391
- },
392
- ],
393
- isError: true,
394
- };
395
- }
396
-
397
- const comp = registry.components.find((c) => c.name === component);
398
- if (!comp) {
399
- return {
400
- content: [
401
- {
402
- type: "text",
403
- text: `Component "${component}" not found in registry`,
404
- },
405
- ],
406
- isError: true,
407
- };
408
- }
409
-
410
- // Determine the actual component name from files
411
- const mainFile = comp.files?.[0];
412
- const componentName = mainFile
413
- ? basename(mainFile, extname(mainFile))
414
- : component;
415
-
416
- let result = `# ${componentName} Integration\n\n`;
417
- result += `**Description:** ${comp.description}\n\n`;
418
-
419
- // Basic usage example
420
- result += "## Basic Usage\n\n";
421
- result += "```tsx\n";
422
- result += `import { ${componentName}`;
423
-
424
- // Add dependencies to import
425
- if (comp.registryDependencies?.length) {
426
- result += `, ${comp.registryDependencies.join(", ")}`;
427
- }
428
-
429
- result += " } from 'bigblocks';\n\n";
430
-
431
- // Generate basic component usage
432
- result += `export function My${componentName}() {\n`;
433
- result += " return (\n";
434
- result += ` <${componentName}\n`;
435
- result += " // Add your props here\n";
436
- result += " />\n";
437
- result += " );\n";
438
- result += "}\n";
439
- result += "```\n\n";
440
-
441
- // Add dependencies info
442
- if (comp.dependencies?.length) {
443
- result += `## Dependencies\n\nInstall required packages:\n\`\`\`bash\nnpm install ${comp.dependencies.join(" ")}\n\`\`\`\n\n`;
444
- }
445
-
446
- return { content: [{ type: "text", text: result }] };
447
- }
448
-
449
- // Default: show available options
450
- return {
451
- content: [
452
- {
453
- type: "text",
454
- text: `# BigBlocks Code Generator
455
-
456
- Generate ready-to-use BigBlocks integration code.
457
-
458
- **Available templates:**
459
- ${Object.entries(TEMPLATES)
460
- .map(([name, data]) => `- **${name}**: ${data.description}`)
461
- .join("\n")}
462
-
463
- **Usage:**
464
- - Generate template: {"template": "template-name"}
465
- - Component code: {"component": "component-name"}
466
- - List all templates: {"listTemplates": true}
467
-
468
- Get started with {"template": "auth-setup"} for authentication or {"template": "wallet-interface"} for wallet functionality.`,
469
- },
470
- ],
471
- };
472
- } catch (error) {
473
- return {
474
- content: [
475
- {
476
- type: "text",
477
- text: `Error: ${error instanceof Error ? error.message : String(error)}`,
478
- },
479
- ],
480
- isError: true,
481
- };
482
- }
483
- },
484
- );
485
- }
@@ -1,23 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { registerBigBlocksComponentTool } from "./components.js";
3
- import { registerBigBlocksDocsTool } from "./docs.js";
4
- import { registerBigBlocksExamplesTool } from "./examples.js";
5
- import { registerBigBlocksGeneratorTool } from "./generator.js";
6
-
7
- /**
8
- * Register all BigBlocks tools with the MCP server
9
- * @param server The MCP server instance
10
- */
11
- export function registerBigBlocksTools(server: McpServer): void {
12
- // Component discovery and information
13
- registerBigBlocksComponentTool(server);
14
-
15
- // Documentation and guides
16
- registerBigBlocksDocsTool(server);
17
-
18
- // Code generation helpers
19
- registerBigBlocksGeneratorTool(server);
20
-
21
- // Examples and patterns
22
- registerBigBlocksExamplesTool(server);
23
- }
@@ -1,189 +0,0 @@
1
- /**
2
- * BigBlocks API Client wrapper for MCP tools
3
- * Leverages the centralized API client from BigBlocks v0.0.13
4
- */
5
-
6
- // Note: In production, this would import from 'bigblocks/src/components/social'
7
- // For now, we'll maintain our custom implementation but with BigBlocks types
8
- import type {
9
- ApiResponse,
10
- BapIdentity,
11
- LikeInfo,
12
- LikesResponse,
13
- PostMeta,
14
- PostResponse,
15
- PostsParams,
16
- PostsResponse,
17
- PostTransaction,
18
- SearchParams,
19
- } from "bmap-api-types";
20
- import { BMAP_URL } from "../constants";
21
-
22
- /**
23
- * Enhanced API client following BigBlocks v0.0.13 patterns
24
- */
25
- export const bmapApiClient = {
26
- posts: {
27
- async search(params: SearchParams) {
28
- const queryParams = new URLSearchParams();
29
- queryParams.append("q", params.q);
30
- queryParams.append("limit", params.limit.toString());
31
- queryParams.append("offset", params.offset.toString());
32
-
33
- const response = await fetch(`${BMAP_URL}/posts/search?${queryParams}`);
34
- if (!response.ok) throw new Error(`Search failed: ${response.status}`);
35
- return response.json();
36
- },
37
-
38
- async byBapId(params: PostsParams & { bapId: string }) {
39
- const queryParams = new URLSearchParams();
40
- queryParams.append("bapId", params.bapId);
41
- if (params.limit) queryParams.append("limit", params.limit.toString());
42
- if (params.page) queryParams.append("page", params.page.toString());
43
-
44
- const response = await fetch(`${BMAP_URL}/posts?${queryParams}`);
45
- if (!response.ok)
46
- throw new Error(`Posts by BAP ID failed: ${response.status}`);
47
- return response.json() as Promise<ApiResponse<PostsResponse>>;
48
- },
49
-
50
- async byAddress(params: PostsParams & { address: string }) {
51
- const queryParams = new URLSearchParams();
52
- queryParams.append("address", params.address);
53
- if (params.limit) queryParams.append("limit", params.limit.toString());
54
- if (params.page) queryParams.append("page", params.page.toString());
55
-
56
- const response = await fetch(`${BMAP_URL}/posts?${queryParams}`);
57
- if (!response.ok)
58
- throw new Error(`Posts by address failed: ${response.status}`);
59
- return response.json() as Promise<ApiResponse<PostsResponse>>;
60
- },
61
-
62
- async single(txid: string) {
63
- const response = await fetch(`${BMAP_URL}/post/${txid}`);
64
- if (!response.ok)
65
- throw new Error(`Single post failed: ${response.status}`);
66
- return response.json() as Promise<ApiResponse<PostResponse>>;
67
- },
68
-
69
- async replies(txid: string, page = 1, limit = 20) {
70
- const queryParams = new URLSearchParams();
71
- queryParams.append("page", page.toString());
72
- queryParams.append("limit", limit.toString());
73
-
74
- const response = await fetch(
75
- `${BMAP_URL}/post/${txid}/replies?${queryParams}`,
76
- );
77
- if (!response.ok) throw new Error(`Replies failed: ${response.status}`);
78
- return response.json();
79
- },
80
-
81
- async trending(
82
- timeframe: "hour" | "day" | "week" | "month" = "day",
83
- limit = 20,
84
- ) {
85
- const queryParams = new URLSearchParams();
86
- queryParams.append("timeframe", timeframe);
87
- queryParams.append("limit", limit.toString());
88
-
89
- const response = await fetch(`${BMAP_URL}/posts/trending?${queryParams}`);
90
- if (!response.ok)
91
- throw new Error(`Trending posts failed: ${response.status}`);
92
- return response.json();
93
- },
94
- },
95
-
96
- likes: {
97
- async forPost(txid: string): Promise<LikeInfo> {
98
- const response = await fetch(`${BMAP_URL}/likes/${txid}`);
99
- if (!response.ok)
100
- throw new Error(`Likes for post failed: ${response.status}`);
101
- return response.json() as Promise<LikeInfo>;
102
- },
103
-
104
- async byUser(bapId: string, page = 1, limit = 20): Promise<LikesResponse> {
105
- const queryParams = new URLSearchParams();
106
- queryParams.append("page", page.toString());
107
- queryParams.append("limit", limit.toString());
108
-
109
- const response = await fetch(
110
- `${BMAP_URL}/users/${bapId}/likes?${queryParams}`,
111
- );
112
- if (!response.ok)
113
- throw new Error(`User likes failed: ${response.status}`);
114
- return response.json() as Promise<LikesResponse>;
115
- },
116
- },
117
-
118
- follows: {
119
- async getFollowers(bapId: string, page = 1, limit = 20) {
120
- const queryParams = new URLSearchParams();
121
- queryParams.append("page", page.toString());
122
- queryParams.append("limit", limit.toString());
123
-
124
- const response = await fetch(
125
- `${BMAP_URL}/users/${bapId}/followers?${queryParams}`,
126
- );
127
- if (!response.ok)
128
- throw new Error(`Get followers failed: ${response.status}`);
129
- return response.json();
130
- },
131
-
132
- async getFollowing(bapId: string, page = 1, limit = 20) {
133
- const queryParams = new URLSearchParams();
134
- queryParams.append("page", page.toString());
135
- queryParams.append("limit", limit.toString());
136
-
137
- const response = await fetch(
138
- `${BMAP_URL}/users/${bapId}/following?${queryParams}`,
139
- );
140
- if (!response.ok)
141
- throw new Error(`Get following failed: ${response.status}`);
142
- return response.json();
143
- },
144
- },
145
-
146
- identity: {
147
- async search(query: string, limit = 10) {
148
- const queryParams = new URLSearchParams();
149
- queryParams.append("q", query);
150
- queryParams.append("limit", limit.toString());
151
-
152
- const response = await fetch(
153
- `${BMAP_URL}/identity/search?${queryParams}`,
154
- );
155
- if (!response.ok)
156
- throw new Error(`Identity search failed: ${response.status}`);
157
- return response.json();
158
- },
159
-
160
- async byBapId(bapId: string) {
161
- const response = await fetch(`${BMAP_URL}/identity/${bapId}`);
162
- if (!response.ok)
163
- throw new Error(`Identity by BAP ID failed: ${response.status}`);
164
- return response.json();
165
- },
166
- },
167
- };
168
-
169
- /**
170
- * Error handling wrapper that follows BigBlocks patterns
171
- */
172
- export class BmapApiError extends Error {
173
- constructor(
174
- public code: string,
175
- message: string,
176
- public details?: Record<string, unknown>,
177
- ) {
178
- super(message);
179
- this.name = "BmapApiError";
180
- }
181
-
182
- static fromError(error: unknown, code = "UNKNOWN_ERROR"): BmapApiError {
183
- if (error instanceof BmapApiError) return error;
184
- if (error instanceof Error) {
185
- return new BmapApiError(code, error.message, { originalError: error });
186
- }
187
- return new BmapApiError(code, String(error));
188
- }
189
- }