@phuetz/code-buddy 0.1.0 → 0.1.2

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 (305) hide show
  1. package/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
  2. package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
  3. package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
  4. package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
  5. package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
  6. package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
  7. package/README.md +56 -0
  8. package/dist/agent/agent-state.d.ts +3 -3
  9. package/dist/agent/agent-state.js +6 -6
  10. package/dist/agent/agent-state.js.map +1 -1
  11. package/dist/agent/base-agent.d.ts +4 -4
  12. package/dist/agent/base-agent.js +22 -9
  13. package/dist/agent/base-agent.js.map +1 -1
  14. package/dist/agent/cache-trace.d.ts +56 -0
  15. package/dist/agent/cache-trace.js +98 -0
  16. package/dist/agent/cache-trace.js.map +1 -0
  17. package/dist/agent/codebuddy-agent.js +4 -2
  18. package/dist/agent/codebuddy-agent.js.map +1 -1
  19. package/dist/agent/execution/agent-executor.d.ts +4 -4
  20. package/dist/agent/execution/agent-executor.js +46 -14
  21. package/dist/agent/execution/agent-executor.js.map +1 -1
  22. package/dist/agent/facades/agent-context-facade.js +1 -3
  23. package/dist/agent/facades/agent-context-facade.js.map +1 -1
  24. package/dist/agent/facades/message-history-manager.js +14 -12
  25. package/dist/agent/facades/message-history-manager.js.map +1 -1
  26. package/dist/agent/facades/session-facade.d.ts +3 -3
  27. package/dist/agent/facades/session-facade.js +6 -6
  28. package/dist/agent/facades/session-facade.js.map +1 -1
  29. package/dist/agent/history-repair.d.ts +37 -0
  30. package/dist/agent/history-repair.js +124 -0
  31. package/dist/agent/history-repair.js.map +1 -0
  32. package/dist/agent/index.d.ts +3 -3
  33. package/dist/agent/index.js +3 -3
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/isolation/agent-workspace.d.ts +1 -0
  36. package/dist/agent/isolation/agent-workspace.js +10 -0
  37. package/dist/agent/isolation/agent-workspace.js.map +1 -1
  38. package/dist/agent/specialized/archive-agent.d.ts +3 -0
  39. package/dist/agent/specialized/archive-agent.js +71 -31
  40. package/dist/agent/specialized/archive-agent.js.map +1 -1
  41. package/dist/agent/specialized/index.d.ts +9 -8
  42. package/dist/agent/specialized/index.js +16 -8
  43. package/dist/agent/specialized/index.js.map +1 -1
  44. package/dist/agent/specialized/security-review/agent.js +19 -8
  45. package/dist/agent/specialized/security-review/agent.js.map +1 -1
  46. package/dist/agent/tool-executor.js +5 -0
  47. package/dist/agent/tool-executor.js.map +1 -1
  48. package/dist/agent/turn-diff-tracker.d.ts +79 -0
  49. package/dist/agent/turn-diff-tracker.js +195 -0
  50. package/dist/agent/turn-diff-tracker.js.map +1 -0
  51. package/dist/browser/controller.js +8 -4
  52. package/dist/browser/controller.js.map +1 -1
  53. package/dist/browser-automation/browser-manager.js +8 -1
  54. package/dist/browser-automation/browser-manager.js.map +1 -1
  55. package/dist/checkpoints/checkpoint-versioning.js +78 -20
  56. package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
  57. package/dist/cli/config-loader.js +2 -4
  58. package/dist/cli/config-loader.js.map +1 -1
  59. package/dist/codebuddy/client.js +70 -11
  60. package/dist/codebuddy/client.js.map +1 -1
  61. package/dist/codebuddy/tools.d.ts +1 -7
  62. package/dist/codebuddy/tools.js +2 -30
  63. package/dist/codebuddy/tools.js.map +1 -1
  64. package/dist/commands/cli/daemon-commands.d.ts +14 -0
  65. package/dist/commands/cli/daemon-commands.js +166 -0
  66. package/dist/commands/cli/daemon-commands.js.map +1 -0
  67. package/dist/commands/cli/speak-command.d.ts +10 -0
  68. package/dist/commands/cli/speak-command.js +97 -0
  69. package/dist/commands/cli/speak-command.js.map +1 -0
  70. package/dist/commands/cli/utility-commands.d.ts +10 -0
  71. package/dist/commands/cli/utility-commands.js +88 -0
  72. package/dist/commands/cli/utility-commands.js.map +1 -0
  73. package/dist/commands/handlers/fcs-handlers.js +1 -1
  74. package/dist/commands/handlers/fcs-handlers.js.map +1 -1
  75. package/dist/commands/handlers/memory-handlers.js +2 -1
  76. package/dist/commands/handlers/memory-handlers.js.map +1 -1
  77. package/dist/commands/handlers/vibe-handlers.js +0 -1
  78. package/dist/commands/handlers/vibe-handlers.js.map +1 -1
  79. package/dist/commands/handlers/worktree-handlers.js +11 -0
  80. package/dist/commands/handlers/worktree-handlers.js.map +1 -1
  81. package/dist/commands/index.d.ts +8 -7
  82. package/dist/commands/index.js +10 -8
  83. package/dist/commands/index.js.map +1 -1
  84. package/dist/commands/mcp.d.ts +1 -0
  85. package/dist/commands/mcp.js +66 -7
  86. package/dist/commands/mcp.js.map +1 -1
  87. package/dist/commands/pipeline.js +25 -13
  88. package/dist/commands/pipeline.js.map +1 -1
  89. package/dist/config/hot-reload/watcher.js +4 -4
  90. package/dist/config/hot-reload/watcher.js.map +1 -1
  91. package/dist/config/model-tools.d.ts +41 -0
  92. package/dist/config/model-tools.js +194 -0
  93. package/dist/config/model-tools.js.map +1 -0
  94. package/dist/context/context-manager-v2.d.ts +2 -1
  95. package/dist/context/context-manager-v2.js +34 -5
  96. package/dist/context/context-manager-v2.js.map +1 -1
  97. package/dist/context/index.d.ts +12 -12
  98. package/dist/context/index.js +25 -12
  99. package/dist/context/index.js.map +1 -1
  100. package/dist/daemon/daemon-manager.js +23 -19
  101. package/dist/daemon/daemon-manager.js.map +1 -1
  102. package/dist/database/database-manager.d.ts +4 -0
  103. package/dist/database/database-manager.js +16 -7
  104. package/dist/database/database-manager.js.map +1 -1
  105. package/dist/desktop-automation/nutjs-provider.js +89 -0
  106. package/dist/desktop-automation/nutjs-provider.js.map +1 -1
  107. package/dist/errors/index.d.ts +4 -4
  108. package/dist/errors/index.js +8 -4
  109. package/dist/errors/index.js.map +1 -1
  110. package/dist/fcs/builtins.d.ts +2 -6
  111. package/dist/fcs/builtins.js +2 -568
  112. package/dist/fcs/builtins.js.map +1 -1
  113. package/dist/fcs/codebuddy-bindings.d.ts +3 -43
  114. package/dist/fcs/codebuddy-bindings.js +2 -606
  115. package/dist/fcs/codebuddy-bindings.js.map +1 -1
  116. package/dist/fcs/index.d.ts +2 -27
  117. package/dist/fcs/index.js +2 -53
  118. package/dist/fcs/index.js.map +1 -1
  119. package/dist/fcs/lexer.d.ts +2 -37
  120. package/dist/fcs/lexer.js +2 -459
  121. package/dist/fcs/lexer.js.map +1 -1
  122. package/dist/fcs/parser.d.ts +2 -68
  123. package/dist/fcs/parser.js +2 -893
  124. package/dist/fcs/parser.js.map +1 -1
  125. package/dist/fcs/runtime.d.ts +2 -59
  126. package/dist/fcs/runtime.js +2 -623
  127. package/dist/fcs/runtime.js.map +1 -1
  128. package/dist/fcs/script-registry.d.ts +3 -69
  129. package/dist/fcs/script-registry.js +2 -219
  130. package/dist/fcs/script-registry.js.map +1 -1
  131. package/dist/fcs/sync-bindings.d.ts +3 -101
  132. package/dist/fcs/sync-bindings.js +2 -410
  133. package/dist/fcs/sync-bindings.js.map +1 -1
  134. package/dist/fcs/types.d.ts +2 -285
  135. package/dist/fcs/types.js +2 -103
  136. package/dist/fcs/types.js.map +1 -1
  137. package/dist/hooks/index.d.ts +4 -4
  138. package/dist/hooks/index.js +4 -4
  139. package/dist/hooks/index.js.map +1 -1
  140. package/dist/hooks/use-input-handler.d.ts +1 -1
  141. package/dist/index.js +20 -330
  142. package/dist/index.js.map +1 -1
  143. package/dist/input/voice-control.js +11 -5
  144. package/dist/input/voice-control.js.map +1 -1
  145. package/dist/integrations/json-rpc/server.d.ts +9 -0
  146. package/dist/integrations/json-rpc/server.js +43 -13
  147. package/dist/integrations/json-rpc/server.js.map +1 -1
  148. package/dist/integrations/mcp/mcp-server.js +1 -1
  149. package/dist/integrations/mcp/mcp-server.js.map +1 -1
  150. package/dist/integrations/notification-integrations.d.ts +1 -0
  151. package/dist/integrations/notification-integrations.js +6 -1
  152. package/dist/integrations/notification-integrations.js.map +1 -1
  153. package/dist/mcp/client.js +2 -1
  154. package/dist/mcp/client.js.map +1 -1
  155. package/dist/mcp/config.js +89 -5
  156. package/dist/mcp/config.js.map +1 -1
  157. package/dist/mcp/mcp-client.js +65 -14
  158. package/dist/mcp/mcp-client.js.map +1 -1
  159. package/dist/mcp/transports.d.ts +0 -1
  160. package/dist/mcp/transports.js +1 -5
  161. package/dist/mcp/transports.js.map +1 -1
  162. package/dist/mcp/types.d.ts +2 -0
  163. package/dist/memory/index.d.ts +2 -2
  164. package/dist/memory/index.js +2 -2
  165. package/dist/memory/index.js.map +1 -1
  166. package/dist/persistence/session-lock.d.ts +42 -0
  167. package/dist/persistence/session-lock.js +165 -0
  168. package/dist/persistence/session-lock.js.map +1 -0
  169. package/dist/persistence/session-store.d.ts +18 -3
  170. package/dist/persistence/session-store.js +90 -21
  171. package/dist/persistence/session-store.js.map +1 -1
  172. package/dist/plugins/conflict-detection.js +2 -1
  173. package/dist/plugins/conflict-detection.js.map +1 -1
  174. package/dist/plugins/index.d.ts +3 -3
  175. package/dist/plugins/index.js +3 -3
  176. package/dist/plugins/index.js.map +1 -1
  177. package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
  178. package/dist/plugins/isolated-plugin-runner.js +19 -1
  179. package/dist/plugins/isolated-plugin-runner.js.map +1 -1
  180. package/dist/providers/local-llm-provider.js +28 -8
  181. package/dist/providers/local-llm-provider.js.map +1 -1
  182. package/dist/sandbox/docker-sandbox.js +7 -4
  183. package/dist/sandbox/docker-sandbox.js.map +1 -1
  184. package/dist/scripting/builtins.d.ts +8 -3
  185. package/dist/scripting/builtins.js +506 -355
  186. package/dist/scripting/builtins.js.map +1 -1
  187. package/dist/scripting/codebuddy-bindings.d.ts +47 -0
  188. package/dist/scripting/codebuddy-bindings.js +488 -0
  189. package/dist/scripting/codebuddy-bindings.js.map +1 -0
  190. package/dist/scripting/index.d.ts +33 -30
  191. package/dist/scripting/index.js +41 -36
  192. package/dist/scripting/index.js.map +1 -1
  193. package/dist/scripting/lexer.d.ts +31 -13
  194. package/dist/scripting/lexer.js +379 -292
  195. package/dist/scripting/lexer.js.map +1 -1
  196. package/dist/scripting/parser.d.ts +63 -44
  197. package/dist/scripting/parser.js +700 -473
  198. package/dist/scripting/parser.js.map +1 -1
  199. package/dist/scripting/runtime.d.ts +55 -24
  200. package/dist/scripting/runtime.js +600 -288
  201. package/dist/scripting/runtime.js.map +1 -1
  202. package/dist/scripting/script-registry.d.ts +54 -0
  203. package/dist/scripting/script-registry.js +202 -0
  204. package/dist/scripting/script-registry.js.map +1 -0
  205. package/dist/scripting/sync-bindings.d.ts +105 -0
  206. package/dist/scripting/sync-bindings.js +353 -0
  207. package/dist/scripting/sync-bindings.js.map +1 -0
  208. package/dist/scripting/types.d.ts +297 -199
  209. package/dist/scripting/types.js +86 -60
  210. package/dist/scripting/types.js.map +1 -1
  211. package/dist/search/usearch-index.js +42 -7
  212. package/dist/search/usearch-index.js.map +1 -1
  213. package/dist/security/bash-parser.d.ts +51 -0
  214. package/dist/security/bash-parser.js +327 -0
  215. package/dist/security/bash-parser.js.map +1 -0
  216. package/dist/security/index.d.ts +7 -5
  217. package/dist/security/index.js +8 -7
  218. package/dist/security/index.js.map +1 -1
  219. package/dist/security/skill-scanner.d.ts +36 -0
  220. package/dist/security/skill-scanner.js +149 -0
  221. package/dist/security/skill-scanner.js.map +1 -0
  222. package/dist/security/trust-folders.d.ts +1 -0
  223. package/dist/security/trust-folders.js +19 -1
  224. package/dist/security/trust-folders.js.map +1 -1
  225. package/dist/server/auth/index.d.ts +2 -2
  226. package/dist/server/auth/index.js +2 -2
  227. package/dist/server/auth/index.js.map +1 -1
  228. package/dist/server/middleware/index.d.ts +5 -5
  229. package/dist/server/middleware/index.js +5 -5
  230. package/dist/server/middleware/index.js.map +1 -1
  231. package/dist/server/middleware/rate-limit.js +15 -3
  232. package/dist/server/middleware/rate-limit.js.map +1 -1
  233. package/dist/server/websocket/handler.js +54 -6
  234. package/dist/server/websocket/handler.js.map +1 -1
  235. package/dist/skills/eligibility.js +26 -4
  236. package/dist/skills/eligibility.js.map +1 -1
  237. package/dist/tasks/background-tasks.js +5 -1
  238. package/dist/tasks/background-tasks.js.map +1 -1
  239. package/dist/tools/apply-patch.d.ts +55 -0
  240. package/dist/tools/apply-patch.js +273 -0
  241. package/dist/tools/apply-patch.js.map +1 -0
  242. package/dist/tools/hooks/default-hooks.d.ts +1 -1
  243. package/dist/tools/hooks/default-hooks.js +2 -1
  244. package/dist/tools/hooks/default-hooks.js.map +1 -1
  245. package/dist/tools/index.d.ts +10 -10
  246. package/dist/tools/index.js +11 -11
  247. package/dist/tools/index.js.map +1 -1
  248. package/dist/tools/registry/bash-tools.js +6 -3
  249. package/dist/tools/registry/bash-tools.js.map +1 -1
  250. package/dist/tools/registry/misc-tools.js +1 -2
  251. package/dist/tools/registry/misc-tools.js.map +1 -1
  252. package/dist/tools/registry/search-tools.js +1 -1
  253. package/dist/tools/registry/search-tools.js.map +1 -1
  254. package/dist/tools/registry/text-editor-tools.js +1 -1
  255. package/dist/tools/registry/text-editor-tools.js.map +1 -1
  256. package/dist/tools/registry/todo-tools.js +37 -5
  257. package/dist/tools/registry/todo-tools.js.map +1 -1
  258. package/dist/tools/registry/tool-registry.js +5 -4
  259. package/dist/tools/registry/tool-registry.js.map +1 -1
  260. package/dist/tools/registry/web-tools.d.ts +1 -1
  261. package/dist/tools/registry/web-tools.js +28 -8
  262. package/dist/tools/registry/web-tools.js.map +1 -1
  263. package/dist/tools/text-editor.d.ts +1 -1
  264. package/dist/tools/text-editor.js +23 -5
  265. package/dist/tools/text-editor.js.map +1 -1
  266. package/dist/tools/web-search.d.ts +52 -37
  267. package/dist/tools/web-search.js +368 -163
  268. package/dist/tools/web-search.js.map +1 -1
  269. package/dist/types/errors.d.ts +1 -1
  270. package/dist/types/errors.js +2 -8
  271. package/dist/types/errors.js.map +1 -1
  272. package/dist/types/index.d.ts +2 -1
  273. package/dist/types/index.js +1 -2
  274. package/dist/types/index.js.map +1 -1
  275. package/dist/ui/components/ChatInterface.d.ts +1 -1
  276. package/dist/ui/index.d.ts +17 -21
  277. package/dist/ui/index.js +25 -22
  278. package/dist/ui/index.js.map +1 -1
  279. package/dist/utils/config-validation/schema.d.ts +15 -15
  280. package/dist/utils/head-tail-truncation.d.ts +34 -0
  281. package/dist/utils/head-tail-truncation.js +98 -0
  282. package/dist/utils/head-tail-truncation.js.map +1 -0
  283. package/dist/utils/logger.js +3 -9
  284. package/dist/utils/logger.js.map +1 -1
  285. package/dist/utils/sanitize.d.ts +5 -0
  286. package/dist/utils/sanitize.js +19 -0
  287. package/dist/utils/sanitize.js.map +1 -1
  288. package/dist/utils/settings-manager.js +4 -4
  289. package/dist/utils/settings-manager.js.map +1 -1
  290. package/dist/workflows/index.d.ts +4 -279
  291. package/dist/workflows/index.js +8 -822
  292. package/dist/workflows/index.js.map +1 -1
  293. package/dist/workflows/state-manager.d.ts +77 -0
  294. package/dist/workflows/state-manager.js +198 -0
  295. package/dist/workflows/state-manager.js.map +1 -0
  296. package/dist/workflows/step-manager.d.ts +39 -0
  297. package/dist/workflows/step-manager.js +196 -0
  298. package/dist/workflows/step-manager.js.map +1 -0
  299. package/dist/workflows/types.d.ts +87 -0
  300. package/dist/workflows/types.js +5 -0
  301. package/dist/workflows/types.js.map +1 -0
  302. package/dist/workflows/workflow-engine.d.ts +34 -0
  303. package/dist/workflows/workflow-engine.js +354 -0
  304. package/dist/workflows/workflow-engine.js.map +1 -0
  305. package/package.json +5 -1
@@ -0,0 +1,1003 @@
1
+ ---
2
+ name: web-fetch
3
+ version: 1.0.0
4
+ description: HTTP requests, REST API testing, fetch API, axios, curl, webhook testing, and API integration
5
+ author: Code Buddy
6
+ tags: http, rest, api, fetch, axios, curl, webhooks, testing
7
+ env:
8
+ HTTP_PROXY: ""
9
+ HTTPS_PROXY: ""
10
+ NO_PROXY: ""
11
+ ---
12
+
13
+ # Web Fetch & HTTP Client
14
+
15
+ Web Fetch provides powerful tools for making HTTP requests, testing REST APIs, consuming webhooks, and integrating with external services. Includes native fetch API, axios library, curl commands, and specialized testing utilities.
16
+
17
+ ## Direct Control (CLI / API / Scripting)
18
+
19
+ ### Native Fetch API (Node.js 18+)
20
+
21
+ ```javascript
22
+ // Basic GET request
23
+ async function fetchData(url) {
24
+ try {
25
+ const response = await fetch(url);
26
+
27
+ if (!response.ok) {
28
+ throw new Error(`HTTP error! status: ${response.status}`);
29
+ }
30
+
31
+ const data = await response.json();
32
+ return data;
33
+ } catch (error) {
34
+ console.error('Fetch error:', error.message);
35
+ throw error;
36
+ }
37
+ }
38
+
39
+ // POST request with JSON body
40
+ async function createResource(url, data) {
41
+ const response = await fetch(url, {
42
+ method: 'POST',
43
+ headers: {
44
+ 'Content-Type': 'application/json',
45
+ 'Authorization': `Bearer ${process.env.API_TOKEN}`
46
+ },
47
+ body: JSON.stringify(data)
48
+ });
49
+
50
+ if (!response.ok) {
51
+ const error = await response.text();
52
+ throw new Error(`Failed to create resource: ${error}`);
53
+ }
54
+
55
+ return await response.json();
56
+ }
57
+
58
+ // Multipart form data (file upload)
59
+ async function uploadFile(url, filePath) {
60
+ const fs = require('fs');
61
+ const FormData = require('form-data');
62
+
63
+ const form = new FormData();
64
+ form.append('file', fs.createReadStream(filePath));
65
+ form.append('metadata', JSON.stringify({ name: 'test.pdf' }));
66
+
67
+ const response = await fetch(url, {
68
+ method: 'POST',
69
+ headers: form.getHeaders(),
70
+ body: form
71
+ });
72
+
73
+ return await response.json();
74
+ }
75
+
76
+ // Request with timeout
77
+ async function fetchWithTimeout(url, timeout = 5000) {
78
+ const controller = new AbortController();
79
+ const id = setTimeout(() => controller.abort(), timeout);
80
+
81
+ try {
82
+ const response = await fetch(url, {
83
+ signal: controller.signal
84
+ });
85
+ clearTimeout(id);
86
+ return await response.json();
87
+ } catch (error) {
88
+ if (error.name === 'AbortError') {
89
+ throw new Error('Request timeout');
90
+ }
91
+ throw error;
92
+ }
93
+ }
94
+
95
+ // Streaming response
96
+ async function streamResponse(url) {
97
+ const response = await fetch(url);
98
+ const reader = response.body.getReader();
99
+ const decoder = new TextDecoder();
100
+
101
+ while (true) {
102
+ const { done, value } = await reader.read();
103
+ if (done) break;
104
+
105
+ const chunk = decoder.decode(value, { stream: true });
106
+ process.stdout.write(chunk);
107
+ }
108
+ }
109
+
110
+ // Retry logic with exponential backoff
111
+ async function fetchWithRetry(url, maxRetries = 3) {
112
+ for (let i = 0; i < maxRetries; i++) {
113
+ try {
114
+ const response = await fetch(url);
115
+ if (response.ok) return await response.json();
116
+
117
+ // Retry on 5xx errors
118
+ if (response.status >= 500 && i < maxRetries - 1) {
119
+ const delay = Math.pow(2, i) * 1000;
120
+ await new Promise(resolve => setTimeout(resolve, delay));
121
+ continue;
122
+ }
123
+
124
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
125
+ } catch (error) {
126
+ if (i === maxRetries - 1) throw error;
127
+ await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
128
+ }
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### Axios Library
134
+
135
+ ```javascript
136
+ const axios = require('axios');
137
+
138
+ // Basic requests
139
+ async function axiosExamples() {
140
+ // GET request
141
+ const response = await axios.get('https://api.example.com/users');
142
+ console.log(response.data);
143
+
144
+ // POST with data
145
+ const created = await axios.post('https://api.example.com/users', {
146
+ name: 'John Doe',
147
+ email: 'john@example.com'
148
+ });
149
+
150
+ // PUT update
151
+ await axios.put('https://api.example.com/users/123', {
152
+ name: 'Jane Doe'
153
+ });
154
+
155
+ // DELETE
156
+ await axios.delete('https://api.example.com/users/123');
157
+
158
+ // PATCH partial update
159
+ await axios.patch('https://api.example.com/users/123', {
160
+ email: 'newemail@example.com'
161
+ });
162
+ }
163
+
164
+ // Configure axios instance
165
+ const api = axios.create({
166
+ baseURL: 'https://api.example.com',
167
+ timeout: 10000,
168
+ headers: {
169
+ 'Content-Type': 'application/json',
170
+ 'Authorization': `Bearer ${process.env.API_TOKEN}`
171
+ }
172
+ });
173
+
174
+ // Request interceptor (add auth token)
175
+ api.interceptors.request.use(
176
+ config => {
177
+ const token = getAccessToken();
178
+ if (token) {
179
+ config.headers.Authorization = `Bearer ${token}`;
180
+ }
181
+ return config;
182
+ },
183
+ error => Promise.reject(error)
184
+ );
185
+
186
+ // Response interceptor (refresh token on 401)
187
+ api.interceptors.response.use(
188
+ response => response,
189
+ async error => {
190
+ const originalRequest = error.config;
191
+
192
+ if (error.response?.status === 401 && !originalRequest._retry) {
193
+ originalRequest._retry = true;
194
+ const newToken = await refreshAccessToken();
195
+ originalRequest.headers.Authorization = `Bearer ${newToken}`;
196
+ return api(originalRequest);
197
+ }
198
+
199
+ return Promise.reject(error);
200
+ }
201
+ );
202
+
203
+ // File upload with progress
204
+ async function uploadWithProgress(url, filePath) {
205
+ const fs = require('fs');
206
+ const FormData = require('form-data');
207
+
208
+ const form = new FormData();
209
+ form.append('file', fs.createReadStream(filePath));
210
+
211
+ const response = await axios.post(url, form, {
212
+ headers: form.getHeaders(),
213
+ onUploadProgress: progressEvent => {
214
+ const percentCompleted = Math.round(
215
+ (progressEvent.loaded * 100) / progressEvent.total
216
+ );
217
+ console.log(`Upload progress: ${percentCompleted}%`);
218
+ }
219
+ });
220
+
221
+ return response.data;
222
+ }
223
+
224
+ // Concurrent requests
225
+ async function parallelRequests(urls) {
226
+ try {
227
+ const responses = await Promise.all(
228
+ urls.map(url => axios.get(url))
229
+ );
230
+ return responses.map(r => r.data);
231
+ } catch (error) {
232
+ console.error('One or more requests failed:', error.message);
233
+ throw error;
234
+ }
235
+ }
236
+
237
+ // Rate-limited requests
238
+ class RateLimiter {
239
+ constructor(maxRequests, timeWindow) {
240
+ this.maxRequests = maxRequests;
241
+ this.timeWindow = timeWindow;
242
+ this.queue = [];
243
+ this.pending = 0;
244
+ }
245
+
246
+ async request(fn) {
247
+ while (this.pending >= this.maxRequests) {
248
+ await new Promise(resolve => this.queue.push(resolve));
249
+ }
250
+
251
+ this.pending++;
252
+ setTimeout(() => {
253
+ this.pending--;
254
+ const resolve = this.queue.shift();
255
+ if (resolve) resolve();
256
+ }, this.timeWindow);
257
+
258
+ return await fn();
259
+ }
260
+ }
261
+
262
+ const limiter = new RateLimiter(5, 1000); // 5 requests per second
263
+
264
+ async function fetchRateLimited(url) {
265
+ return await limiter.request(() => axios.get(url));
266
+ }
267
+ ```
268
+
269
+ ### cURL Commands
270
+
271
+ ```bash
272
+ # Basic GET request
273
+ curl https://api.example.com/users
274
+
275
+ # GET with headers
276
+ curl -H "Authorization: Bearer TOKEN" \
277
+ -H "Content-Type: application/json" \
278
+ https://api.example.com/users
279
+
280
+ # POST with JSON data
281
+ curl -X POST https://api.example.com/users \
282
+ -H "Content-Type: application/json" \
283
+ -d '{"name":"John Doe","email":"john@example.com"}'
284
+
285
+ # POST from file
286
+ curl -X POST https://api.example.com/users \
287
+ -H "Content-Type: application/json" \
288
+ -d @data.json
289
+
290
+ # PUT request
291
+ curl -X PUT https://api.example.com/users/123 \
292
+ -H "Content-Type: application/json" \
293
+ -d '{"name":"Jane Doe"}'
294
+
295
+ # DELETE request
296
+ curl -X DELETE https://api.example.com/users/123 \
297
+ -H "Authorization: Bearer TOKEN"
298
+
299
+ # File upload
300
+ curl -X POST https://api.example.com/upload \
301
+ -F "file=@document.pdf" \
302
+ -F "metadata=@meta.json;type=application/json"
303
+
304
+ # Follow redirects
305
+ curl -L https://example.com/redirect
306
+
307
+ # Save response to file
308
+ curl -o output.json https://api.example.com/data
309
+
310
+ # Include response headers
311
+ curl -i https://api.example.com/users
312
+
313
+ # Verbose output (debugging)
314
+ curl -v https://api.example.com/users
315
+
316
+ # Send cookies
317
+ curl -b "session=abc123" https://api.example.com/profile
318
+
319
+ # Save cookies
320
+ curl -c cookies.txt https://api.example.com/login
321
+
322
+ # Use saved cookies
323
+ curl -b cookies.txt https://api.example.com/profile
324
+
325
+ # Basic authentication
326
+ curl -u username:password https://api.example.com/secure
327
+
328
+ # Custom request method
329
+ curl -X PATCH https://api.example.com/users/123 \
330
+ -d '{"email":"new@example.com"}'
331
+
332
+ # Timeout settings
333
+ curl --connect-timeout 5 --max-time 10 https://api.example.com
334
+
335
+ # Proxy
336
+ curl -x http://proxy:8080 https://api.example.com
337
+
338
+ # Ignore SSL certificate
339
+ curl -k https://self-signed.example.com
340
+
341
+ # Multiple requests
342
+ curl -Z https://api.example.com/endpoint1 https://api.example.com/endpoint2
343
+
344
+ # Rate limit (1 request per second)
345
+ for url in $(cat urls.txt); do
346
+ curl "$url"
347
+ sleep 1
348
+ done
349
+ ```
350
+
351
+ ### REST API Testing
352
+
353
+ ```javascript
354
+ const assert = require('assert');
355
+
356
+ class APITester {
357
+ constructor(baseURL) {
358
+ this.baseURL = baseURL;
359
+ this.results = [];
360
+ }
361
+
362
+ async test(name, testFn) {
363
+ try {
364
+ await testFn();
365
+ this.results.push({ name, status: 'PASS' });
366
+ console.log(`✓ ${name}`);
367
+ } catch (error) {
368
+ this.results.push({ name, status: 'FAIL', error: error.message });
369
+ console.error(`✗ ${name}: ${error.message}`);
370
+ }
371
+ }
372
+
373
+ async get(path, expectedStatus = 200) {
374
+ const response = await fetch(`${this.baseURL}${path}`);
375
+ assert.strictEqual(response.status, expectedStatus,
376
+ `Expected status ${expectedStatus}, got ${response.status}`);
377
+ return response;
378
+ }
379
+
380
+ async post(path, data, expectedStatus = 201) {
381
+ const response = await fetch(`${this.baseURL}${path}`, {
382
+ method: 'POST',
383
+ headers: { 'Content-Type': 'application/json' },
384
+ body: JSON.stringify(data)
385
+ });
386
+ assert.strictEqual(response.status, expectedStatus);
387
+ return response;
388
+ }
389
+
390
+ assertSchema(data, schema) {
391
+ for (const [key, type] of Object.entries(schema)) {
392
+ assert(key in data, `Missing key: ${key}`);
393
+ assert.strictEqual(typeof data[key], type,
394
+ `Expected ${key} to be ${type}, got ${typeof data[key]}`);
395
+ }
396
+ }
397
+
398
+ async report() {
399
+ const passed = this.results.filter(r => r.status === 'PASS').length;
400
+ const failed = this.results.filter(r => r.status === 'FAIL').length;
401
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
402
+ return { passed, failed, results: this.results };
403
+ }
404
+ }
405
+
406
+ // Usage
407
+ async function runTests() {
408
+ const tester = new APITester('https://api.example.com');
409
+
410
+ await tester.test('GET /users returns 200', async () => {
411
+ const response = await tester.get('/users');
412
+ const data = await response.json();
413
+ assert(Array.isArray(data), 'Response should be an array');
414
+ });
415
+
416
+ await tester.test('POST /users creates user', async () => {
417
+ const userData = { name: 'Test User', email: 'test@example.com' };
418
+ const response = await tester.post('/users', userData);
419
+ const created = await response.json();
420
+ tester.assertSchema(created, { id: 'number', name: 'string', email: 'string' });
421
+ });
422
+
423
+ await tester.test('GET /users/:id returns user', async () => {
424
+ const response = await tester.get('/users/1');
425
+ const user = await response.json();
426
+ assert(user.id === 1, 'User ID should match');
427
+ });
428
+
429
+ await tester.report();
430
+ }
431
+ ```
432
+
433
+ ### Webhook Testing
434
+
435
+ ```javascript
436
+ const express = require('express');
437
+ const crypto = require('crypto');
438
+
439
+ // Webhook receiver server
440
+ function createWebhookServer(port = 3000) {
441
+ const app = express();
442
+ app.use(express.json());
443
+
444
+ const receivedWebhooks = [];
445
+
446
+ // Verify HMAC signature
447
+ function verifySignature(payload, signature, secret) {
448
+ const hmac = crypto.createHmac('sha256', secret);
449
+ const digest = hmac.update(payload).digest('hex');
450
+ return crypto.timingSafeEqual(
451
+ Buffer.from(signature),
452
+ Buffer.from(digest)
453
+ );
454
+ }
455
+
456
+ app.post('/webhook', (req, res) => {
457
+ const signature = req.headers['x-hub-signature-256'];
458
+ const payload = JSON.stringify(req.body);
459
+
460
+ // Verify signature if present
461
+ if (signature) {
462
+ const secret = process.env.WEBHOOK_SECRET;
463
+ if (!verifySignature(payload, signature.replace('sha256=', ''), secret)) {
464
+ return res.status(401).json({ error: 'Invalid signature' });
465
+ }
466
+ }
467
+
468
+ // Store webhook
469
+ receivedWebhooks.push({
470
+ timestamp: new Date(),
471
+ headers: req.headers,
472
+ body: req.body
473
+ });
474
+
475
+ console.log('Webhook received:', req.body);
476
+ res.status(200).json({ received: true });
477
+ });
478
+
479
+ app.get('/webhooks', (req, res) => {
480
+ res.json(receivedWebhooks);
481
+ });
482
+
483
+ return app.listen(port, () => {
484
+ console.log(`Webhook server listening on port ${port}`);
485
+ });
486
+ }
487
+
488
+ // Webhook sender/tester
489
+ async function sendWebhook(url, data, secret) {
490
+ const payload = JSON.stringify(data);
491
+ const hmac = crypto.createHmac('sha256', secret);
492
+ const signature = hmac.update(payload).digest('hex');
493
+
494
+ const response = await fetch(url, {
495
+ method: 'POST',
496
+ headers: {
497
+ 'Content-Type': 'application/json',
498
+ 'X-Hub-Signature-256': `sha256=${signature}`,
499
+ 'X-Webhook-Event': data.event
500
+ },
501
+ body: payload
502
+ });
503
+
504
+ return {
505
+ status: response.status,
506
+ body: await response.json()
507
+ };
508
+ }
509
+
510
+ // Test webhook delivery
511
+ async function testWebhookDelivery(webhookUrl, testData) {
512
+ const results = [];
513
+
514
+ for (const test of testData) {
515
+ try {
516
+ const result = await sendWebhook(webhookUrl, test.payload, test.secret);
517
+ results.push({
518
+ test: test.name,
519
+ success: result.status === 200,
520
+ status: result.status,
521
+ response: result.body
522
+ });
523
+ } catch (error) {
524
+ results.push({
525
+ test: test.name,
526
+ success: false,
527
+ error: error.message
528
+ });
529
+ }
530
+ }
531
+
532
+ return results;
533
+ }
534
+ ```
535
+
536
+ ### GraphQL Queries
537
+
538
+ ```javascript
539
+ async function graphqlQuery(endpoint, query, variables = {}) {
540
+ const response = await fetch(endpoint, {
541
+ method: 'POST',
542
+ headers: {
543
+ 'Content-Type': 'application/json',
544
+ 'Authorization': `Bearer ${process.env.GRAPHQL_TOKEN}`
545
+ },
546
+ body: JSON.stringify({ query, variables })
547
+ });
548
+
549
+ const result = await response.json();
550
+
551
+ if (result.errors) {
552
+ throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
553
+ }
554
+
555
+ return result.data;
556
+ }
557
+
558
+ // Example queries
559
+ const GET_USER = `
560
+ query GetUser($id: ID!) {
561
+ user(id: $id) {
562
+ id
563
+ name
564
+ email
565
+ posts {
566
+ id
567
+ title
568
+ }
569
+ }
570
+ }
571
+ `;
572
+
573
+ const CREATE_POST = `
574
+ mutation CreatePost($input: PostInput!) {
575
+ createPost(input: $input) {
576
+ id
577
+ title
578
+ content
579
+ author {
580
+ name
581
+ }
582
+ }
583
+ }
584
+ `;
585
+
586
+ // Usage
587
+ const user = await graphqlQuery('https://api.example.com/graphql', GET_USER, {
588
+ id: '123'
589
+ });
590
+
591
+ const post = await graphqlQuery('https://api.example.com/graphql', CREATE_POST, {
592
+ input: {
593
+ title: 'New Post',
594
+ content: 'Content here',
595
+ authorId: '123'
596
+ }
597
+ });
598
+ ```
599
+
600
+ ## MCP Server Integration
601
+
602
+ Add to `.codebuddy/mcp.json`:
603
+
604
+ ```json
605
+ {
606
+ "mcpServers": {
607
+ "fetch": {
608
+ "command": "npx",
609
+ "args": [
610
+ "-y",
611
+ "@modelcontextprotocol/server-fetch"
612
+ ]
613
+ }
614
+ }
615
+ }
616
+ ```
617
+
618
+ Or with custom configuration:
619
+
620
+ ```json
621
+ {
622
+ "mcpServers": {
623
+ "fetch": {
624
+ "command": "node",
625
+ "args": [
626
+ "/path/to/fetch-mcp-server/index.js"
627
+ ],
628
+ "env": {
629
+ "HTTP_TIMEOUT": "30000",
630
+ "MAX_REDIRECTS": "5",
631
+ "USER_AGENT": "Code-Buddy-MCP/1.0"
632
+ }
633
+ }
634
+ }
635
+ }
636
+ ```
637
+
638
+ ### Available MCP Tools
639
+
640
+ **From @modelcontextprotocol/server-fetch:**
641
+ - `fetch` - Make HTTP request with full control
642
+ - `get` - Simple GET request
643
+ - `post` - POST request with JSON body
644
+ - `put` - PUT request for updates
645
+ - `delete` - DELETE request
646
+ - `head` - HEAD request (headers only)
647
+ - `options` - OPTIONS request (CORS preflight)
648
+
649
+ **Tool Parameters:**
650
+ ```javascript
651
+ {
652
+ "url": "https://api.example.com/endpoint",
653
+ "method": "GET|POST|PUT|DELETE|PATCH",
654
+ "headers": {
655
+ "Authorization": "Bearer TOKEN",
656
+ "Content-Type": "application/json"
657
+ },
658
+ "body": "string or JSON",
659
+ "timeout": 30000,
660
+ "follow_redirects": true,
661
+ "max_redirects": 5
662
+ }
663
+ ```
664
+
665
+ ## Common Workflows
666
+
667
+ ### 1. API Integration: Complete CRUD Operations
668
+
669
+ ```javascript
670
+ // Step 1: Setup API client with authentication
671
+ const API_BASE = 'https://api.example.com';
672
+ const API_KEY = process.env.API_KEY;
673
+
674
+ const headers = {
675
+ 'Content-Type': 'application/json',
676
+ 'Authorization': `Bearer ${API_KEY}`
677
+ };
678
+
679
+ // Step 2: Create new resource
680
+ async function createUser(userData) {
681
+ const response = await fetch(`${API_BASE}/users`, {
682
+ method: 'POST',
683
+ headers,
684
+ body: JSON.stringify(userData)
685
+ });
686
+
687
+ if (!response.ok) {
688
+ throw new Error(`Create failed: ${response.statusText}`);
689
+ }
690
+
691
+ return await response.json();
692
+ }
693
+
694
+ // Step 3: Read and verify
695
+ async function getUser(userId) {
696
+ const response = await fetch(`${API_BASE}/users/${userId}`, { headers });
697
+ return await response.json();
698
+ }
699
+
700
+ // Step 4: Update resource
701
+ async function updateUser(userId, updates) {
702
+ const response = await fetch(`${API_BASE}/users/${userId}`, {
703
+ method: 'PATCH',
704
+ headers,
705
+ body: JSON.stringify(updates)
706
+ });
707
+ return await response.json();
708
+ }
709
+
710
+ // Step 5: Delete and verify
711
+ async function deleteUser(userId) {
712
+ const response = await fetch(`${API_BASE}/users/${userId}`, {
713
+ method: 'DELETE',
714
+ headers
715
+ });
716
+ return response.ok;
717
+ }
718
+
719
+ // Execute workflow
720
+ const user = await createUser({ name: 'John', email: 'john@example.com' });
721
+ const fetched = await getUser(user.id);
722
+ await updateUser(user.id, { email: 'newemail@example.com' });
723
+ await deleteUser(user.id);
724
+ ```
725
+
726
+ ### 2. REST API Testing Suite
727
+
728
+ ```javascript
729
+ // Step 1: Define test suite
730
+ const tests = [
731
+ {
732
+ name: 'Authentication works',
733
+ run: async () => {
734
+ const response = await fetch('https://api.example.com/auth/login', {
735
+ method: 'POST',
736
+ headers: { 'Content-Type': 'application/json' },
737
+ body: JSON.stringify({ username: 'test', password: 'test123' })
738
+ });
739
+ assert.strictEqual(response.status, 200);
740
+ const data = await response.json();
741
+ assert(data.token, 'Token should be present');
742
+ return data.token;
743
+ }
744
+ },
745
+ {
746
+ name: 'Protected endpoint requires auth',
747
+ run: async () => {
748
+ const response = await fetch('https://api.example.com/protected');
749
+ assert.strictEqual(response.status, 401);
750
+ }
751
+ },
752
+ {
753
+ name: 'Pagination works correctly',
754
+ run: async () => {
755
+ const response = await fetch('https://api.example.com/users?page=1&limit=10');
756
+ const data = await response.json();
757
+ assert(data.items.length <= 10, 'Should respect limit');
758
+ assert(data.pagination, 'Should include pagination info');
759
+ }
760
+ }
761
+ ];
762
+
763
+ // Step 2: Run all tests
764
+ let passed = 0;
765
+ let failed = 0;
766
+ const results = [];
767
+
768
+ for (const test of tests) {
769
+ try {
770
+ await test.run();
771
+ console.log(`✓ ${test.name}`);
772
+ passed++;
773
+ results.push({ name: test.name, status: 'PASS' });
774
+ } catch (error) {
775
+ console.error(`✗ ${test.name}: ${error.message}`);
776
+ failed++;
777
+ results.push({ name: test.name, status: 'FAIL', error: error.message });
778
+ }
779
+ }
780
+
781
+ // Step 3: Generate test report
782
+ const report = {
783
+ timestamp: new Date(),
784
+ summary: { passed, failed, total: tests.length },
785
+ results
786
+ };
787
+
788
+ // Step 4: Save results
789
+ await fs.writeFile('test-results.json', JSON.stringify(report, null, 2));
790
+
791
+ // Step 5: Exit with appropriate code
792
+ process.exit(failed > 0 ? 1 : 0);
793
+ ```
794
+
795
+ ### 3. Webhook Listener and Processor
796
+
797
+ ```javascript
798
+ // Step 1: Create webhook server
799
+ const express = require('express');
800
+ const app = express();
801
+ app.use(express.json());
802
+
803
+ const webhookQueue = [];
804
+
805
+ // Step 2: Setup webhook endpoint with validation
806
+ app.post('/webhook', (req, res) => {
807
+ const signature = req.headers['x-webhook-signature'];
808
+ const secret = process.env.WEBHOOK_SECRET;
809
+
810
+ // Verify signature
811
+ const crypto = require('crypto');
812
+ const hmac = crypto.createHmac('sha256', secret);
813
+ const digest = hmac.update(JSON.stringify(req.body)).digest('hex');
814
+
815
+ if (signature !== digest) {
816
+ return res.status(401).json({ error: 'Invalid signature' });
817
+ }
818
+
819
+ // Queue webhook for processing
820
+ webhookQueue.push({
821
+ id: crypto.randomUUID(),
822
+ timestamp: new Date(),
823
+ event: req.headers['x-webhook-event'],
824
+ data: req.body
825
+ });
826
+
827
+ res.status(200).json({ received: true });
828
+ });
829
+
830
+ // Step 3: Process webhooks asynchronously
831
+ async function processWebhooks() {
832
+ while (true) {
833
+ if (webhookQueue.length > 0) {
834
+ const webhook = webhookQueue.shift();
835
+ try {
836
+ await handleWebhook(webhook);
837
+ console.log(`Processed webhook ${webhook.id}`);
838
+ } catch (error) {
839
+ console.error(`Failed to process webhook ${webhook.id}:`, error);
840
+ // Retry logic here
841
+ }
842
+ }
843
+ await new Promise(resolve => setTimeout(resolve, 1000));
844
+ }
845
+ }
846
+
847
+ // Step 4: Handle different webhook types
848
+ async function handleWebhook(webhook) {
849
+ switch (webhook.event) {
850
+ case 'user.created':
851
+ await onUserCreated(webhook.data);
852
+ break;
853
+ case 'order.completed':
854
+ await onOrderCompleted(webhook.data);
855
+ break;
856
+ default:
857
+ console.log(`Unknown event: ${webhook.event}`);
858
+ }
859
+ }
860
+
861
+ // Step 5: Start server and processor
862
+ app.listen(3000, () => console.log('Webhook server running on port 3000'));
863
+ processWebhooks();
864
+ ```
865
+
866
+ ### 4. API Performance Monitoring
867
+
868
+ ```javascript
869
+ // Step 1: Define monitoring targets
870
+ const endpoints = [
871
+ { name: 'Homepage', url: 'https://api.example.com/', method: 'GET' },
872
+ { name: 'Users API', url: 'https://api.example.com/users', method: 'GET' },
873
+ { name: 'Search API', url: 'https://api.example.com/search?q=test', method: 'GET' }
874
+ ];
875
+
876
+ // Step 2: Measure response times
877
+ async function measureEndpoint(endpoint) {
878
+ const startTime = Date.now();
879
+ try {
880
+ const response = await fetch(endpoint.url, {
881
+ method: endpoint.method,
882
+ signal: AbortSignal.timeout(10000)
883
+ });
884
+
885
+ const responseTime = Date.now() - startTime;
886
+
887
+ return {
888
+ name: endpoint.name,
889
+ status: response.status,
890
+ responseTime,
891
+ success: response.ok,
892
+ timestamp: new Date()
893
+ };
894
+ } catch (error) {
895
+ return {
896
+ name: endpoint.name,
897
+ error: error.message,
898
+ success: false,
899
+ timestamp: new Date()
900
+ };
901
+ }
902
+ }
903
+
904
+ // Step 3: Run monitoring loop
905
+ const results = [];
906
+ for (const endpoint of endpoints) {
907
+ const result = await measureEndpoint(endpoint);
908
+ results.push(result);
909
+ console.log(`${result.name}: ${result.responseTime}ms (${result.status})`);
910
+ }
911
+
912
+ // Step 4: Calculate statistics
913
+ const stats = {
914
+ avgResponseTime: results.reduce((sum, r) => sum + (r.responseTime || 0), 0) / results.length,
915
+ successRate: (results.filter(r => r.success).length / results.length) * 100,
916
+ slowest: results.sort((a, b) => (b.responseTime || 0) - (a.responseTime || 0))[0]
917
+ };
918
+
919
+ // Step 5: Alert if thresholds exceeded
920
+ if (stats.avgResponseTime > 1000) {
921
+ console.warn(`⚠️ Average response time high: ${stats.avgResponseTime}ms`);
922
+ }
923
+ if (stats.successRate < 95) {
924
+ console.error(`❌ Success rate low: ${stats.successRate}%`);
925
+ }
926
+
927
+ await fs.writeFile('monitoring-results.json', JSON.stringify({ stats, results }, null, 2));
928
+ ```
929
+
930
+ ### 5. Bulk Data Import via API
931
+
932
+ ```javascript
933
+ // Step 1: Load data from CSV
934
+ const fs = require('fs').promises;
935
+ const csv = require('csv-parser');
936
+ const { createReadStream } = require('fs');
937
+
938
+ async function loadCSV(filePath) {
939
+ return new Promise((resolve, reject) => {
940
+ const rows = [];
941
+ createReadStream(filePath)
942
+ .pipe(csv())
943
+ .on('data', row => rows.push(row))
944
+ .on('end', () => resolve(rows))
945
+ .on('error', reject);
946
+ });
947
+ }
948
+
949
+ // Step 2: Batch upload function
950
+ async function uploadBatch(records, batchSize = 100) {
951
+ const API_URL = 'https://api.example.com/users/bulk';
952
+ const results = { success: 0, failed: 0, errors: [] };
953
+
954
+ for (let i = 0; i < records.length; i += batchSize) {
955
+ const batch = records.slice(i, i + batchSize);
956
+
957
+ try {
958
+ const response = await fetch(API_URL, {
959
+ method: 'POST',
960
+ headers: {
961
+ 'Content-Type': 'application/json',
962
+ 'Authorization': `Bearer ${process.env.API_KEY}`
963
+ },
964
+ body: JSON.stringify({ records: batch })
965
+ });
966
+
967
+ if (response.ok) {
968
+ results.success += batch.length;
969
+ console.log(`Uploaded batch ${i / batchSize + 1}: ${batch.length} records`);
970
+ } else {
971
+ results.failed += batch.length;
972
+ const error = await response.text();
973
+ results.errors.push({ batch: i / batchSize + 1, error });
974
+ }
975
+ } catch (error) {
976
+ results.failed += batch.length;
977
+ results.errors.push({ batch: i / batchSize + 1, error: error.message });
978
+ }
979
+
980
+ // Rate limiting: wait between batches
981
+ await new Promise(resolve => setTimeout(resolve, 1000));
982
+ }
983
+
984
+ return results;
985
+ }
986
+
987
+ // Step 3: Load and validate data
988
+ const records = await loadCSV('users.csv');
989
+ console.log(`Loaded ${records.length} records`);
990
+
991
+ // Step 4: Upload in batches
992
+ const results = await uploadBatch(records, 100);
993
+
994
+ // Step 5: Report results
995
+ console.log(`\nUpload complete:`);
996
+ console.log(`Success: ${results.success}`);
997
+ console.log(`Failed: ${results.failed}`);
998
+ if (results.errors.length > 0) {
999
+ console.error('Errors:', results.errors);
1000
+ }
1001
+
1002
+ await fs.writeFile('import-results.json', JSON.stringify(results, null, 2));
1003
+ ```