botversion-sdk 1.0.5 → 1.0.6

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.
package/bin/init.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  // botversion-sdk/bin/init.js
3
3
  "use strict";
4
4
 
@@ -102,7 +102,7 @@ async function main() {
102
102
  if (!args.key) {
103
103
  error("API key is required.");
104
104
  log(`\n Usage: npx botversion-sdk init --key YOUR_WORKSPACE_KEY\n`);
105
- log(` Get your key from: https://app.botversion.com/settings\n`);
105
+ log(` Get your key from: https://botversion.com/settings\n`);
106
106
  process.exit(1);
107
107
  }
108
108
 
@@ -113,7 +113,7 @@ async function main() {
113
113
  step("Fetching project info from platform...");
114
114
  let projectInfo;
115
115
  try {
116
- projectInfo = await fetchProjectInfo(args.key, "http://localhost:3000");
116
+ projectInfo = await fetchProjectInfo(args.key, "https://botversion.com");
117
117
  success(`Project found — ID: ${projectInfo.projectId}`);
118
118
  } catch (err) {
119
119
  error(err.message);
package/cli/generator.js CHANGED
@@ -149,11 +149,11 @@ Fastify support is coming soon. For now, add this manually:
149
149
  `,
150
150
  koa: `
151
151
  Koa support is coming soon. For now, add this manually:
152
- See: https://docs.botversion.com/koa
152
+ See: https://botversion.com/docs
153
153
  `,
154
154
  "@nestjs/core": `
155
155
  NestJS support is coming soon. For now, add this manually:
156
- See: https://docs.botversion.com/nestjs
156
+ See: https://botversion.com/docs
157
157
  `,
158
158
  };
159
159
 
@@ -161,7 +161,7 @@ NestJS support is coming soon. For now, add this manually:
161
161
  instructions[framework] ||
162
162
  `
163
163
  This framework is not yet supported automatically.
164
- Visit https://docs.botversion.com for manual setup instructions.
164
+ Visit https://botversion.com/docs for manual setup instructions.
165
165
  `
166
166
  );
167
167
  }
package/cli/writer.js CHANGED
@@ -257,7 +257,7 @@ function writeSummary(changes) {
257
257
  }
258
258
 
259
259
  lines.push(" Next: Restart your server and test the chat widget.");
260
- lines.push(" Docs: https://docs.botversion.com");
260
+ lines.push(" Docs: https://botversion.com/docs");
261
261
  lines.push("");
262
262
 
263
263
  return lines.join("\n");
package/client.js CHANGED
@@ -11,7 +11,7 @@ const url = require("url");
11
11
  */
12
12
  function BotVersionClient(options) {
13
13
  this.apiKey = options.apiKey;
14
- this.platformUrl = options.platformUrl || "http://localhost:3000";
14
+ this.platformUrl = options.platformUrl || "https://botversion.com";
15
15
  this.debug = options.debug || false;
16
16
  this.timeout = options.timeout || 30000;
17
17
 
@@ -19,25 +19,12 @@ function BotVersionClient(options) {
19
19
  this._queue = [];
20
20
  this._flushTimer = null;
21
21
  this._flushDelay = options.flushDelay || 3000;
22
- this._ws = null;
23
- this._executor = null;
24
22
  var self = this;
25
23
  process.on("beforeExit", function () {
26
24
  if (self._queue.length > 0) self._flush();
27
25
  });
28
26
  }
29
27
 
30
- /**
31
- * Register the executor function — called by interceptor
32
- * So WebSocket can forward calls to user's backend
33
- */
34
- BotVersionClient.prototype.setExecutor = function (executorFn) {
35
- this._executor = executorFn;
36
- if (this.debug) {
37
- console.log("[BotVersion SDK] ✅ Executor registered");
38
- }
39
- };
40
-
41
28
  /**
42
29
  * Register multiple endpoints at once (batched)
43
30
  */
@@ -46,14 +33,6 @@ BotVersionClient.prototype.registerEndpoints = function (endpoints) {
46
33
 
47
34
  if (!endpoints || endpoints.length === 0) return Promise.resolve();
48
35
 
49
- if (self.debug) {
50
- console.log(
51
- "[BotVersion SDK] Queuing",
52
- endpoints.length,
53
- "endpoints for registration",
54
- );
55
- }
56
-
57
36
  self._queue = self._queue.concat(endpoints);
58
37
 
59
38
  if (!self._flushTimer) {
@@ -79,20 +58,7 @@ BotVersionClient.prototype.registerEndpointsNow = function (endpoints) {
79
58
  endpoints: endpoints,
80
59
  })
81
60
  .then(function (data) {
82
- if (self.debug) {
83
- console.log(
84
- "[BotVersion SDK] ✅ Registered",
85
- endpoints.length,
86
- "endpoints immediately",
87
- );
88
- }
89
61
  return data;
90
- })
91
- .catch(function (err) {
92
- console.warn(
93
- "[BotVersion SDK] ⚠ Failed to register endpoints:",
94
- err.message,
95
- );
96
62
  });
97
63
  };
98
64
 
@@ -108,35 +74,14 @@ BotVersionClient.prototype._flush = function () {
108
74
  var toSend = self._queue.slice();
109
75
  self._queue = [];
110
76
 
111
- if (self.debug) {
112
- console.log(
113
- "[BotVersion SDK] Flushing",
114
- toSend.length,
115
- "endpoints to platform",
116
- );
117
- }
118
-
119
77
  self
120
78
  ._post("/api/sdk/register-endpoints", {
121
79
  workspaceKey: self.apiKey,
122
80
  endpoints: toSend,
123
81
  })
124
- .then(function (data) {
125
- if (self.debug) {
126
- console.log(
127
- "[BotVersion SDK] Registered",
128
- data.succeeded,
129
- "endpoints successfully",
130
- );
131
- }
132
- })
82
+ .then(function (data) {})
133
83
  .catch(function (err) {
134
- if (self.debug) {
135
- console.warn(
136
- "[BotVersion SDK] Failed to register endpoints:",
137
- err.message,
138
- );
139
- }
84
+ console.error("[botversion] flush failed:", err.message);
140
85
  });
141
86
  };
142
87
 
@@ -160,27 +105,10 @@ BotVersionClient.prototype.registerRoutePatterns = function (patterns) {
160
105
  var self = this;
161
106
  if (!patterns || patterns.length === 0) return Promise.resolve();
162
107
 
163
- if (self.debug) {
164
- console.log(
165
- "[BotVersion SDK] Sending",
166
- patterns.length,
167
- "route patterns to platform",
168
- );
169
- }
170
-
171
- return self
172
- ._post("/api/sdk/register-route-patterns", {
173
- workspaceKey: self.apiKey,
174
- patterns: patterns,
175
- })
176
- .catch(function (err) {
177
- if (self.debug) {
178
- console.warn(
179
- "[BotVersion SDK] Failed to register route patterns:",
180
- err.message,
181
- );
182
- }
183
- });
108
+ return self._post("/api/sdk/register-route-patterns", {
109
+ workspaceKey: self.apiKey,
110
+ patterns: patterns,
111
+ });
184
112
  };
185
113
 
186
114
  /**
@@ -324,145 +252,4 @@ BotVersionClient.prototype._get = function (path) {
324
252
  });
325
253
  };
326
254
 
327
- /**
328
- * Connect to platform via WebSocket — establishes persistent connection
329
- * Auto-reconnects every 5 seconds if connection drops
330
- */
331
- BotVersionClient.prototype.connect = function () {
332
- var self = this;
333
- // Run in next tick so it doesn't block initialization
334
- setImmediate(function () {
335
- self._wsLoop();
336
- });
337
- };
338
-
339
- BotVersionClient.prototype._wsLoop = function () {
340
- var self = this;
341
-
342
- var wsUrl = self.platformUrl
343
- .replace("https://", "wss://")
344
- .replace("http://", "ws://")
345
- .replace(":3000", ":3001");
346
- wsUrl = wsUrl + "?apiKey=" + encodeURIComponent(self.apiKey);
347
-
348
- var WebSocket;
349
- try {
350
- WebSocket = require("ws");
351
- } catch (e) {
352
- console.error(
353
- "[BotVersion SDK] ❌ 'ws' package not installed. Run: npm install ws",
354
- );
355
- return;
356
- }
357
-
358
- function attempt() {
359
- var ws = new WebSocket(wsUrl);
360
- self._ws = ws;
361
-
362
- ws.on("open", function () {
363
- if (self.debug) {
364
- console.log("[BotVersion SDK] ✅ WebSocket connected to platform");
365
- }
366
- // Identify this SDK to the platform
367
- ws.send(
368
- JSON.stringify({
369
- type: "IDENTIFY",
370
- apiKey: self.apiKey,
371
- }),
372
- );
373
- });
374
-
375
- ws.on("message", function (data) {
376
- try {
377
- var msg = JSON.parse(data.toString());
378
-
379
- if (msg.type === "EXECUTE_CALL") {
380
- self._handleExecuteCall(msg);
381
- }
382
- } catch (e) {
383
- if (self.debug) {
384
- console.warn("[BotVersion SDK] ⚠ Error parsing message:", e.message);
385
- }
386
- }
387
- });
388
-
389
- ws.on("error", function (err) {
390
- if (self.debug) {
391
- console.warn("[BotVersion SDK] ⚠ WebSocket error:", err.message);
392
- }
393
- });
394
-
395
- ws.on("close", function () {
396
- if (self.debug) {
397
- console.log(
398
- "[BotVersion SDK] WebSocket closed — reconnecting in 5s...",
399
- );
400
- }
401
- self._ws = null;
402
- // Auto-reconnect after 5 seconds
403
- setTimeout(attempt, 5000);
404
- });
405
- }
406
-
407
- attempt();
408
- };
409
-
410
- BotVersionClient.prototype._handleExecuteCall = function (data) {
411
- var self = this;
412
-
413
- var callId = data.callId;
414
- var method = data.method;
415
- var path = data.path;
416
- var body = data.body;
417
- var cookies = data.cookies || "";
418
- var headers = data.headers || {};
419
- var baseUrl = data.baseUrl || "http://127.0.0.1:3000";
420
-
421
- if (!self._executor) {
422
- self._sendCallResult(callId, {
423
- status: 500,
424
- ok: false,
425
- data: { error: "No executor registered" },
426
- });
427
- return;
428
- }
429
-
430
- self
431
- ._executor(method, path, body, cookies, headers, baseUrl)
432
- .then(function (result) {
433
- self._sendCallResult(callId, result);
434
- })
435
- .catch(function (err) {
436
- self._sendCallResult(callId, {
437
- status: 500,
438
- ok: false,
439
- data: { error: err.message },
440
- });
441
- });
442
- };
443
-
444
- BotVersionClient.prototype._sendCallResult = function (callId, result) {
445
- var self = this;
446
- try {
447
- if (self._ws && self._ws.readyState === 1) {
448
- // 1 = OPEN
449
- self._ws.send(
450
- JSON.stringify({
451
- type: "CALL_RESULT",
452
- callId: callId,
453
- result: result,
454
- }),
455
- );
456
- } else if (self.debug) {
457
- console.warn(
458
- "[BotVersion SDK] ⚠ WebSocket not open — could not send CALL_RESULT",
459
- );
460
- }
461
- } catch (e) {
462
- if (self.debug) {
463
- console.warn("[BotVersion SDK] ⚠ Failed to send CALL_RESULT:", e.message);
464
- }
465
- }
466
- };
467
-
468
255
  module.exports = BotVersionClient;
package/index.js CHANGED
@@ -26,22 +26,17 @@ var BotVersion = {
26
26
  app = null;
27
27
  }
28
28
 
29
- if (!options.apiKey) {
30
- console.error("[BotVersion SDK] ❌ apiKey is required.");
31
- return;
32
- }
33
-
34
29
  // Restore from global if module was re-imported after hot reload
35
30
  if (global._botVersionClient) {
36
31
  this._client = global._botVersionClient;
37
32
  this._options = global._botVersionOptions;
38
33
  this._initialized = true;
39
- console.warn("[BotVersion SDK] Restored from global — skipping re-init");
40
- return;
41
- }
42
-
43
- if (this._initialized) {
44
- console.warn("[BotVersion SDK] Already initialized — skipping");
34
+ // Re-attach interceptor after hot reload
35
+ interceptor.attachNextJsInterceptor(this._client, {
36
+ exclude: (global._botVersionOptions || {}).exclude || [],
37
+ apiPrefix: (global._botVersionOptions || {}).apiPrefix || "/api",
38
+ debug: (global._botVersionOptions || {}).debug || false,
39
+ });
45
40
  return;
46
41
  }
47
42
 
@@ -51,7 +46,7 @@ var BotVersion = {
51
46
 
52
47
  this._client = new BotVersionClient({
53
48
  apiKey: options.apiKey,
54
- platformUrl: options.platformUrl || "http://localhost:3000",
49
+ platformUrl: options.platformUrl || "https://botversion.com",
55
50
  debug: options.debug || false,
56
51
  timeout: options.timeout || 30000,
57
52
  });
@@ -62,80 +57,34 @@ var BotVersion = {
62
57
  var self = this;
63
58
  var debug = options.debug || false;
64
59
 
65
- if (debug) {
66
- console.log("[BotVersion SDK] Initializing...");
67
- if (app) {
68
- console.log("[BotVersion SDK] Mode: Express");
69
- } else {
70
- console.log("[BotVersion SDK] Mode: Next.js (file-based routing)");
71
- }
72
- }
73
-
74
- // ── Framework detection ──────────────────────────────────────────────────
75
- if (app) {
76
- var frameworkCheck = detectFramework(app);
77
- if (!frameworkCheck.supported) {
78
- console.error(
79
- "[BotVersion SDK] ❌ Unsupported framework:",
80
- frameworkCheck.name,
81
- );
82
- console.error(
83
- "[BotVersion SDK] ❌ Currently supports Express and Next.js only.",
84
- );
85
- return;
86
- }
87
- if (debug) {
88
- console.log(
89
- "[BotVersion SDK] ✅ Framework detected:",
90
- frameworkCheck.name,
91
- );
92
- }
93
- }
94
-
95
- // ── Runtime interceptor — Express only ───────────────────────────────────
60
+ // ── Runtime interceptor — Express or Next.js ─────────────────────────────
96
61
  if (app && app.use) {
62
+ // Express
97
63
  interceptor.attachInterceptor(app, self._client, {
98
64
  exclude: options.exclude || [],
99
65
  apiPrefix: options.apiPrefix || null,
100
66
  debug: debug,
101
67
  });
102
- if (debug) {
103
- console.log("[BotVersion SDK] ✅ Runtime interceptor attached");
104
- }
105
- } else if (!app) {
106
- if (debug) {
107
- console.log(
108
- "[BotVersion SDK] ℹ Runtime interceptor skipped — Next.js mode uses file scanning only",
109
- );
110
- }
68
+ } else {
69
+ // Next.js — patch the global fetch/http server to intercept API calls
70
+ interceptor.attachNextJsInterceptor(self._client, {
71
+ exclude: options.exclude || [],
72
+ apiPrefix: options.apiPrefix || "/api",
73
+ debug: debug,
74
+ });
111
75
  }
112
76
 
113
77
  // ── Static scan ──────────────────────────────────────────────────────────
114
78
  setTimeout(function () {
115
79
  var endpoints = [];
116
80
 
117
- // Express scan
118
81
  // Express scan
119
82
  if (app) {
120
- console.log("[BotVersion SDK] Scanning Express routes...");
121
83
  var cwd = options.cwd || process.cwd();
122
84
  endpoints = scanner.scanExpressRoutes(app, cwd);
123
- console.log(
124
- "[BotVersion SDK] Found",
125
- endpoints.length,
126
- "Express routes",
127
- );
128
-
129
- if (endpoints.length === 0) {
130
- console.warn("[BotVersion SDK] ⚠ No endpoints found.");
131
- console.warn(
132
- "[BotVersion SDK] ⚠ Make sure routes are defined BEFORE BotVersion.init()",
133
- );
134
- }
135
85
  }
136
86
 
137
87
  // Next.js scan
138
- // Next.js scan — auto detect all possible structures
139
88
  if (!app) {
140
89
  const fs = require("fs");
141
90
  const path = require("path");
@@ -153,91 +102,32 @@ var BotVersion = {
153
102
 
154
103
  for (const dir of possibleAppDirs) {
155
104
  if (fs.existsSync(dir)) {
156
- console.log("[BotVersion SDK] Scanning App Router routes at:", dir);
157
105
  const routes = scanner.scanNextJsAppRoutes(dir);
158
106
  endpoints = endpoints.concat(routes);
159
- console.log(
160
- "[BotVersion SDK] Found",
161
- routes.length,
162
- "App Router routes",
163
- );
164
107
  }
165
108
  }
166
109
 
167
110
  for (const dir of possiblePagesDirs) {
168
111
  if (fs.existsSync(dir)) {
169
- console.log(
170
- "[BotVersion SDK] Scanning Pages Router routes at:",
171
- dir,
172
- );
173
112
  const routes = scanner.scanNextJsRoutes(dir);
174
113
  endpoints = endpoints.concat(routes);
175
- console.log(
176
- "[BotVersion SDK] Found",
177
- routes.length,
178
- "Pages Router routes",
179
- );
180
114
  }
181
115
  }
182
116
  }
183
117
 
184
- // Neither Express nor pagesDir
185
- // Neither Express nor Next.js routes found
186
- if (!app && endpoints.length === 0) {
187
- console.error(
188
- "[BotVersion SDK] ❌ No routes found. Make sure your app/api or pages/api folder exists.",
189
- );
190
- return;
191
- }
192
-
193
118
  // Send to platform
194
119
  if (endpoints.length > 0) {
195
- console.log(
196
- "[BotVersion SDK] Sending",
197
- endpoints.length,
198
- "endpoints to platform...",
199
- );
200
- self._client
201
- .registerEndpoints(endpoints)
202
- .then(function () {
203
- console.log(
204
- "[BotVersion SDK] ✅ Endpoints queued —",
205
- endpoints.length,
206
- "endpoints will be sent shortly",
207
- );
208
- })
209
- .catch(function (err) {
210
- console.error(
211
- "[BotVersion SDK] ❌ Failed to register endpoints:",
212
- err.message,
213
- );
214
- });
120
+ self._client.registerEndpoints(endpoints);
215
121
  }
216
122
 
217
123
  var routePatterns = scanner.scanFrontendRoutes(
218
124
  options.cwd || process.cwd(),
219
125
  );
220
126
  if (routePatterns.length > 0) {
221
- console.log(
222
- "[BotVersion SDK] Found",
223
- routePatterns.length,
224
- "frontend route patterns",
225
- );
226
127
  self._client
227
128
  .registerRoutePatterns(routePatterns)
228
- .then(function () {
229
- console.log("[BotVersion SDK] ✅ Route patterns registered");
230
- })
231
- .catch(function (err) {
232
- console.error(
233
- "[BotVersion SDK] ❌ Failed to register route patterns:",
234
- err.message,
235
- );
236
- });
129
+ .catch(function (err) {});
237
130
  }
238
-
239
- self._client.connect();
240
- console.log("[BotVersion SDK] ✅ Initialization complete");
241
131
  }, 500);
242
132
  },
243
133
 
@@ -270,36 +160,3 @@ module.exports.default = BotVersion;
270
160
  module.exports.init = BotVersion.init;
271
161
  module.exports.getEndpoints = BotVersion.getEndpoints;
272
162
  module.exports.registerEndpoint = BotVersion.registerEndpoint;
273
-
274
- // ── Framework detection ──────────────────────────────────────────────────────
275
- function detectFramework(app) {
276
- if (!app) return { supported: false, name: "unknown (no app passed)" };
277
-
278
- var isExpress =
279
- typeof app.use === "function" &&
280
- typeof app.get === "function" &&
281
- typeof app.post === "function" &&
282
- (app._router !== undefined ||
283
- app.stack !== undefined ||
284
- app.lazyrouter !== undefined);
285
-
286
- if (isExpress) return { supported: true, name: "Express" };
287
-
288
- if (typeof app.addHook === "function" && typeof app.route === "function") {
289
- return { supported: false, name: "Fastify" };
290
- }
291
-
292
- if (
293
- typeof app.use === "function" &&
294
- app.context !== undefined &&
295
- typeof app.get !== "function"
296
- ) {
297
- return { supported: false, name: "Koa" };
298
- }
299
-
300
- if (typeof app.route === "function" && typeof app.start === "function") {
301
- return { supported: false, name: "Hapi" };
302
- }
303
-
304
- return { supported: false, name: "unknown" };
305
- }
package/interceptor.js CHANGED
@@ -1,109 +1,7 @@
1
1
  // botversion-sdk/interceptor.js
2
2
  "use strict";
3
3
 
4
- /**
5
- * Makes an internal HTTP request to the user's own backend.
6
- * Forwards cookies, auth headers, and CSRF tokens.
7
- */
8
- function makeInternalRequest(method, path, body, cookies, headers, baseUrl) {
9
- return new Promise(function (resolve) {
10
- var http = require("http");
11
- var https = require("https");
12
- var url = require("url");
13
-
14
- baseUrl = baseUrl || "http://127.0.0.1:8000";
15
- var fullUrl = baseUrl + path;
16
- var parsedUrl = url.parse(fullUrl);
17
- var isHttps = parsedUrl.protocol === "https:";
18
- var lib = isHttps ? https : http;
19
-
20
- var bodyStr = body ? JSON.stringify(body) : null;
21
-
22
- var reqHeaders = {
23
- "Content-Type": "application/json",
24
- };
25
-
26
- if (bodyStr) {
27
- reqHeaders["Content-Length"] = String(Buffer.byteLength(bodyStr));
28
- }
29
-
30
- // Forward cookies — this is what identifies the user
31
- if (cookies) reqHeaders["Cookie"] = cookies;
32
-
33
- // Forward auth header
34
- var authHeader =
35
- headers && (headers["authorization"] || headers["Authorization"]);
36
- if (authHeader) reqHeaders["Authorization"] = authHeader;
37
-
38
- // Forward CSRF token
39
- var csrf =
40
- headers &&
41
- (headers["x-csrftoken"] ||
42
- headers["X-CSRFToken"] ||
43
- headers["x-xsrf-token"] ||
44
- headers["X-XSRF-TOKEN"]);
45
- if (csrf) reqHeaders["X-CSRFToken"] = csrf;
46
-
47
- var reqOptions = {
48
- hostname: parsedUrl.hostname,
49
- port: parsedUrl.port || (isHttps ? 443 : 80),
50
- path: parsedUrl.path,
51
- method: method.toUpperCase(),
52
- headers: reqHeaders,
53
- timeout: 30000,
54
- };
55
-
56
- var req = lib.request(reqOptions, function (res) {
57
- var data = "";
58
- res.on("data", function (chunk) {
59
- data += chunk;
60
- });
61
- res.on("end", function () {
62
- var parsed;
63
- try {
64
- parsed = JSON.parse(data);
65
- } catch (e) {
66
- parsed = { raw: data };
67
- }
68
-
69
- // Handle redirects
70
- if (
71
- res.statusCode >= 300 &&
72
- res.statusCode < 400 &&
73
- res.headers.location
74
- ) {
75
- makeInternalRequest(
76
- res.statusCode === 303 ? "GET" : method,
77
- res.headers.location,
78
- res.statusCode === 303 ? null : body,
79
- cookies,
80
- headers,
81
- baseUrl,
82
- ).then(resolve);
83
- return;
84
- }
85
-
86
- resolve({
87
- status: res.statusCode,
88
- ok: res.statusCode >= 200 && res.statusCode < 300,
89
- data: parsed,
90
- });
91
- });
92
- });
93
-
94
- req.on("error", function (err) {
95
- resolve({ status: 500, ok: false, data: { error: err.message } });
96
- });
97
-
98
- req.on("timeout", function () {
99
- req.destroy();
100
- resolve({ status: 500, ok: false, data: { error: "Request timed out" } });
101
- });
102
-
103
- if (bodyStr) req.write(bodyStr);
104
- req.end();
105
- });
106
- }
4
+ const reportedEndpoints = new Set();
107
5
 
108
6
  /**
109
7
  * Attaches a middleware to the Express app that
@@ -122,9 +20,6 @@ function attachInterceptor(app, client, options) {
122
20
  "/public",
123
21
  ].concat(options.exclude || []);
124
22
 
125
- // Track which endpoints we have already reported
126
- const reportedEndpoints = new Set();
127
-
128
23
  app.use(function botVersionInterceptor(req, res, next) {
129
24
  const path = req.path || req.url || "";
130
25
  const shouldIgnore = ignorePaths.some(function (p) {
@@ -183,22 +78,13 @@ function attachInterceptor(app, client, options) {
183
78
  detectedBy: "runtime",
184
79
  })
185
80
  .catch(function (err) {
186
- if (options.debug) {
187
- console.warn(
188
- "[BotVersion SDK] Failed to report endpoint:",
189
- err.message,
190
- );
191
- }
81
+ console.error("[botversion] update_endpoint failed:", err.message);
192
82
  });
193
83
  });
194
84
  }
195
85
 
196
86
  next();
197
87
  });
198
- // Register executor so WebSocket can forward calls to user's backend
199
- client.setExecutor(function (method, path, body, cookies, headers, baseUrl) {
200
- return makeInternalRequest(method, path, body, cookies, headers, baseUrl);
201
- });
202
88
  }
203
89
 
204
90
  /**
@@ -286,8 +172,59 @@ function buildBodyStructure(body) {
286
172
  return structure;
287
173
  }
288
174
 
175
+ function attachNextJsInterceptor(client, options) {
176
+ try {
177
+ const http = require("http");
178
+ const originalEmit = http.Server.prototype.emit;
179
+
180
+ http.Server.prototype.emit = function (event, req, res) {
181
+ if (event === "request") {
182
+ const path = req.url ? req.url.split("?")[0] : "";
183
+ const method = req.method ? req.method.toUpperCase() : "";
184
+
185
+ const shouldIgnore = (options.exclude || [])
186
+ .concat(["/health", "/favicon.ico", "/_next", "/static", "/public"])
187
+ .some(function (p) {
188
+ return path.startsWith(p);
189
+ });
190
+
191
+ const isApiPath = path.startsWith(options.apiPrefix || "/api");
192
+
193
+ if (!shouldIgnore && isApiPath) {
194
+ const normalizedPath = normalizePath(path);
195
+ const endpointKey = method + ":" + normalizedPath;
196
+
197
+ if (!reportedEndpoints.has(endpointKey)) {
198
+ reportedEndpoints.add(endpointKey);
199
+
200
+ setImmediate(function () {
201
+ client
202
+ .updateEndpoint({
203
+ method: method,
204
+ path: normalizedPath,
205
+ detectedBy: "runtime",
206
+ })
207
+ .catch(function (err) {
208
+ console.error("[botversion] update failed:", err.message);
209
+ });
210
+ });
211
+ }
212
+ }
213
+ }
214
+
215
+ return originalEmit.apply(this, arguments);
216
+ };
217
+ } catch (err) {
218
+ console.error(
219
+ "[botversion] failed to attach Next.js interceptor:",
220
+ err.message,
221
+ );
222
+ }
223
+ }
224
+
289
225
  module.exports = {
290
226
  attachInterceptor,
227
+ attachNextJsInterceptor,
291
228
  normalizePath,
292
229
  buildBodyStructure,
293
230
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botversion-sdk",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "BotVersion SDK — auto-detect and register your API endpoints",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -16,7 +16,8 @@
16
16
  "author": "Saurav Dhakal",
17
17
  "license": "MIT",
18
18
  "dependencies": {
19
- "express": "^4.18.2",
20
- "ws": "^8.20.0"
19
+ "@babel/parser": "^7.29.3",
20
+ "@babel/traverse": "^7.29.0",
21
+ "express": "^4.18.2"
21
22
  }
22
23
  }
package/scanner.js CHANGED
@@ -29,14 +29,9 @@ function scanExpressRoutes(app, cwd) {
29
29
  }
30
30
  }
31
31
 
32
- console.log(`\n[DEBUG] ===== JS SCAN SUMMARY =====`);
33
32
  endpoints.forEach((ep) => {
34
33
  const status = ep.requestBody ? "✅" : "❌ NULL";
35
- console.log(
36
- `[DEBUG] ${status} ${ep.method.padEnd(6)} ${ep.path} → ${JSON.stringify(ep.requestBody)}`,
37
- );
38
34
  });
39
- console.log(`[DEBUG] ================================\n`);
40
35
 
41
36
  return endpoints;
42
37
  }
@@ -51,7 +46,6 @@ function scanExpressFileStatically(filePath, seen) {
51
46
  } catch {
52
47
  return endpoints;
53
48
  }
54
- console.log(`[DEBUG] Scanning file statically: ${filePath}`);
55
49
 
56
50
  // Match patterns like:
57
51
  // app.get('/path', ...)
@@ -76,10 +70,6 @@ function scanExpressFileStatically(filePath, seen) {
76
70
  const needsBody = ["POST", "PUT", "PATCH"].includes(effectiveMethod);
77
71
  const bodyFields = needsBody ? extractBodyFieldsFromFile(content) : null;
78
72
 
79
- console.log(
80
- `[DEBUG] ${effectiveMethod} ${routePath} → bodyFields: ${JSON.stringify(bodyFields)}`,
81
- );
82
-
83
73
  const routeParamMap = buildRouteParamMap(routePath, []);
84
74
 
85
75
  endpoints.push({
@@ -133,9 +123,6 @@ function extractRoutes(stack, prefix, endpoints, seen, bodyMap) {
133
123
  const fields = extractBodyFieldsFromFile(fnStr);
134
124
  if (fields) {
135
125
  requestBody = fields;
136
- console.log(
137
- `[DEBUG] Found body from inline handler for ${method} ${routePath}`,
138
- );
139
126
  break;
140
127
  }
141
128
  }
@@ -143,12 +130,8 @@ function extractRoutes(stack, prefix, endpoints, seen, bodyMap) {
143
130
  // Strategy 2: fall back to bodyMap using handler name
144
131
  if (!requestBody) {
145
132
  const handlerName = extractHandlerName(layer);
146
- console.log(
147
- `[DEBUG] ${method} ${routePath} → handlerName: ${handlerName}`,
148
- );
149
133
  if (handlerName && bodyMap[handlerName]) {
150
134
  requestBody = bodyMap[handlerName];
151
- console.log(`[DEBUG] Found body from bodyMap for ${handlerName}`);
152
135
  }
153
136
  }
154
137
  }
@@ -190,7 +173,6 @@ function scanNextJsRoutes(pagesDir) {
190
173
  const apiDir = path.join(pagesDir, "api");
191
174
 
192
175
  if (!fs.existsSync(apiDir)) {
193
- console.warn("[BotVersion SDK] No pages/api directory found at:", apiDir);
194
176
  return endpoints;
195
177
  }
196
178
 
@@ -345,6 +327,8 @@ function inferFieldType(fieldName, content) {
345
327
  new RegExp(`${fieldName}\\s*===?\\s*(true|false)`),
346
328
  new RegExp(`(true|false)\\s*===?\\s*${fieldName}`),
347
329
  new RegExp(`Boolean\\s*\\(\\s*${fieldName}`),
330
+ new RegExp(`typeof\\s+${fieldName}\\s*!==?\\s*["']boolean["']`),
331
+ new RegExp(`typeof\\s+${fieldName}\\s*===?\\s*["']boolean["']`),
348
332
  ];
349
333
  if (boolPatterns.some((p) => p.test(content))) return "boolean";
350
334
 
@@ -411,9 +395,6 @@ function extractBodyFieldsFromFile(content) {
411
395
  ? { type: "array", items: { type: "object" } }
412
396
  : { type };
413
397
  });
414
- console.log(
415
- `[DEBUG] extractBodyFieldsFromFile found fields: ${JSON.stringify([...fields])}`,
416
- );
417
398
 
418
399
  return { type: "object", properties };
419
400
  }
@@ -425,7 +406,6 @@ function scanNextJsAppRoutes(appDir) {
425
406
 
426
407
  const apiDir = path.join(appDir, "api");
427
408
  if (!fs.existsSync(apiDir)) {
428
- console.warn("[BotVersion SDK] No app/api directory found at:", apiDir);
429
409
  return endpoints;
430
410
  }
431
411
 
@@ -710,16 +690,8 @@ function scanAllExpressFiles(cwd) {
710
690
  );
711
691
 
712
692
  if (isExpressFile) {
713
- console.log(`[DEBUG] Found route file: ${fullPath}`); // already have this
714
693
  routeFiles.push(fullPath);
715
694
  }
716
-
717
- // ADD THIS — log files that are being SKIPPED
718
- else if (/\.(js|ts)$/.test(entry)) {
719
- console.log(
720
- `[DEBUG] Skipped (not detected as route file): ${fullPath}`,
721
- );
722
- }
723
695
  } catch {
724
696
  continue;
725
697
  }
@@ -795,23 +767,6 @@ function buildBodyMap(cwd) {
795
767
  /export\s+const\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>\s*\{/g,
796
768
  ];
797
769
 
798
- if (content.includes("req.body")) {
799
- console.log(`[DEBUG] bodyMap: found req.body in ${fullPath}`);
800
- console.log(
801
- `[DEBUG] bodyMap: fnNames found: ${JSON.stringify([
802
- ...(() => {
803
- const names = new Set();
804
- for (const p of fnPatterns) {
805
- p.lastIndex = 0;
806
- let m;
807
- while ((m = p.exec(content)) !== null) names.add(m[1]);
808
- }
809
- return names;
810
- })(),
811
- ])}`,
812
- );
813
- }
814
-
815
770
  // Map every function name in this file to the whole file's body fields.
816
771
  // Brace-counting is unreliable across JS/TS variations, so we use
817
772
  // file-level field extraction here. Per-function accuracy is handled
@@ -835,7 +790,6 @@ function buildBodyMap(cwd) {
835
790
  }
836
791
 
837
792
  walk(cwd, 0);
838
- console.log(`[DEBUG] bodyMap keys: ${Object.keys(bodyMap)}`);
839
793
  return bodyMap;
840
794
  }
841
795
 
@@ -932,8 +886,6 @@ function scanConfigBasedRoutes(cwd) {
932
886
  continue;
933
887
  }
934
888
 
935
- console.log("[BotVersion SDK] Scanning config-based routes in:", filePath);
936
-
937
889
  // Pattern 1 — React Router JSX: <Route path="/:projectId/dashboard" />
938
890
  const jsxRouteMatches = content.matchAll(
939
891
  /<Route[^>]+path=["']([^"']+)["']/g,
@@ -984,12 +936,6 @@ function addConfigPattern(routePath, seen, patterns) {
984
936
  if (Object.keys(paramMap).length === 0) return;
985
937
 
986
938
  patterns.push({ pattern: normalized, params: paramMap });
987
- console.log(
988
- "[BotVersion SDK] Found config-based route pattern:",
989
- normalized,
990
- "→",
991
- paramMap,
992
- );
993
939
  }
994
940
 
995
941
  function scanFrontendRoutes(cwd) {
@@ -1093,12 +1039,6 @@ function scanFrontendRoutes(cwd) {
1093
1039
  if (Object.keys(paramMap).length === 0) return; // no dynamic params, skip static routes
1094
1040
 
1095
1041
  patterns.push({ pattern, params: paramMap });
1096
- console.log(
1097
- "[BotVersion SDK] Found frontend route pattern:",
1098
- pattern,
1099
- "→",
1100
- paramMap,
1101
- );
1102
1042
  });
1103
1043
  }
1104
1044