botversion-sdk 1.0.4 → 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/index.js CHANGED
@@ -10,15 +10,6 @@ var BotVersion = {
10
10
  _initialized: false,
11
11
 
12
12
  init: function (appOrOptions, optionsArg) {
13
- console.log("=== INIT CALLED ===");
14
- console.log("appOrOptions type:", typeof appOrOptions);
15
- console.log(
16
- "appOrOptions is express app:",
17
- appOrOptions && typeof appOrOptions.use === "function",
18
- );
19
- console.log("optionsArg type:", typeof optionsArg);
20
- console.log("optionsArg value:", JSON.stringify(optionsArg));
21
- console.log("===================");
22
13
  var app = null;
23
14
  var options = {};
24
15
 
@@ -35,23 +26,17 @@ var BotVersion = {
35
26
  app = null;
36
27
  }
37
28
 
38
- console.log("options received:", JSON.stringify(options));
39
- if (!options.apiKey) {
40
- console.error("[BotVersion SDK] ❌ apiKey is required.");
41
- return;
42
- }
43
-
44
29
  // Restore from global if module was re-imported after hot reload
45
30
  if (global._botVersionClient) {
46
31
  this._client = global._botVersionClient;
47
32
  this._options = global._botVersionOptions;
48
33
  this._initialized = true;
49
- console.warn("[BotVersion SDK] Restored from global — skipping re-init");
50
- return;
51
- }
52
-
53
- if (this._initialized) {
54
- 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
+ });
55
40
  return;
56
41
  }
57
42
 
@@ -61,7 +46,7 @@ var BotVersion = {
61
46
 
62
47
  this._client = new BotVersionClient({
63
48
  apiKey: options.apiKey,
64
- platformUrl: options.platformUrl || "https://chatbusiness-two.vercel.app",
49
+ platformUrl: options.platformUrl || "https://botversion.com",
65
50
  debug: options.debug || false,
66
51
  timeout: options.timeout || 30000,
67
52
  });
@@ -72,56 +57,21 @@ var BotVersion = {
72
57
  var self = this;
73
58
  var debug = options.debug || false;
74
59
 
75
- if (debug) {
76
- console.log("[BotVersion SDK] Initializing...");
77
- if (app) {
78
- console.log("[BotVersion SDK] Mode: Express");
79
- } else {
80
- console.log("[BotVersion SDK] Mode: Next.js (file-based routing)");
81
- }
82
- }
83
-
84
- // ── Framework detection ──────────────────────────────────────────────────
85
- if (app) {
86
- var frameworkCheck = detectFramework(app);
87
- if (!frameworkCheck.supported) {
88
- console.error(
89
- "[BotVersion SDK] ❌ Unsupported framework:",
90
- frameworkCheck.name,
91
- );
92
- console.error(
93
- "[BotVersion SDK] ❌ Currently supports Express and Next.js only.",
94
- );
95
- console.error(
96
- "[BotVersion SDK] ❌ Visit https://docs.botversion.com for supported frameworks.",
97
- );
98
- return;
99
- }
100
- if (debug) {
101
- console.log(
102
- "[BotVersion SDK] ✅ Framework detected:",
103
- frameworkCheck.name,
104
- );
105
- }
106
- }
107
-
108
- // ── Runtime interceptor — Express only ───────────────────────────────────
60
+ // ── Runtime interceptor — Express or Next.js ─────────────────────────────
109
61
  if (app && app.use) {
62
+ // Express
110
63
  interceptor.attachInterceptor(app, self._client, {
111
64
  exclude: options.exclude || [],
112
65
  apiPrefix: options.apiPrefix || null,
113
66
  debug: debug,
114
- getUserContext: options.getUserContext || null,
115
67
  });
116
- if (debug) {
117
- console.log("[BotVersion SDK] ✅ Runtime interceptor attached");
118
- }
119
- } else if (!app) {
120
- if (debug) {
121
- console.log(
122
- "[BotVersion SDK] ℹ Runtime interceptor skipped — Next.js mode uses file scanning only",
123
- );
124
- }
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
+ });
125
75
  }
126
76
 
127
77
  // ── Static scan ──────────────────────────────────────────────────────────
@@ -130,95 +80,54 @@ var BotVersion = {
130
80
 
131
81
  // Express scan
132
82
  if (app) {
133
- console.log("[BotVersion SDK] Scanning Express routes...");
134
- endpoints = scanner.scanExpressRoutes(app);
135
- console.log(
136
- "[BotVersion SDK] Found",
137
- endpoints.length,
138
- "Express routes",
139
- );
140
- console.log(
141
- "[BotVersion SDK] Endpoints:",
142
- JSON.stringify(endpoints, null, 2),
143
- );
144
-
145
- if (endpoints.length === 0) {
146
- console.warn("[BotVersion SDK] ⚠ No endpoints found.");
147
- console.warn(
148
- "[BotVersion SDK] ⚠ Make sure routes are defined BEFORE BotVersion.init()",
149
- );
150
- console.warn(
151
- "[BotVersion SDK] ⚠ and BotVersion.init() is called BEFORE app.listen().",
152
- );
153
- }
83
+ var cwd = options.cwd || process.cwd();
84
+ endpoints = scanner.scanExpressRoutes(app, cwd);
154
85
  }
155
86
 
156
- // Next.js scan — runs if pagesDir is provided (with or without app)
157
- if (options.pagesDir) {
158
- console.log(
159
- "[BotVersion SDK] Scanning Next.js API routes...",
160
- options.pagesDir,
161
- );
162
- var nextRoutes = scanner.scanNextJsRoutes(options.pagesDir);
163
- endpoints = endpoints.concat(nextRoutes);
164
- console.log(
165
- "[BotVersion SDK] Found",
166
- nextRoutes.length,
167
- "Next.js routes",
168
- );
169
- console.log(
170
- "[BotVersion SDK] Next.js routes:",
171
- JSON.stringify(nextRoutes, null, 2),
172
- );
87
+ // Next.js scan
88
+ if (!app) {
89
+ const fs = require("fs");
90
+ const path = require("path");
91
+ const cwd = process.cwd();
173
92
 
174
- if (nextRoutes.length === 0) {
175
- console.warn("[BotVersion SDK] ⚠ No Next.js routes found.");
176
- console.warn(
177
- "[BotVersion SDK] ⚠ Make sure pagesDir points to your pages folder.",
178
- );
179
- console.warn(
180
- "[BotVersion SDK] ⚠ Example: pagesDir: path.join(process.cwd(), 'pages')",
181
- );
93
+ const possibleAppDirs = [
94
+ path.join(cwd, "app"),
95
+ path.join(cwd, "src", "app"),
96
+ ];
97
+
98
+ const possiblePagesDirs = [
99
+ path.join(cwd, "pages"),
100
+ path.join(cwd, "src", "pages"),
101
+ ];
102
+
103
+ for (const dir of possibleAppDirs) {
104
+ if (fs.existsSync(dir)) {
105
+ const routes = scanner.scanNextJsAppRoutes(dir);
106
+ endpoints = endpoints.concat(routes);
107
+ }
182
108
  }
183
- }
184
109
 
185
- // Neither Express nor pagesDir
186
- if (!app && !options.pagesDir) {
187
- console.error("[BotVersion SDK] ❌ No routes to scan.");
188
- console.error(
189
- "[BotVersion SDK] ❌ For Express: pass your app object — BotVersion.init(app, options)",
190
- );
191
- console.error(
192
- "[BotVersion SDK] ❌ For Next.js: pass pagesDir — BotVersion.init({ apiKey, pagesDir: path.join(process.cwd(), 'pages') })",
193
- );
194
- return;
110
+ for (const dir of possiblePagesDirs) {
111
+ if (fs.existsSync(dir)) {
112
+ const routes = scanner.scanNextJsRoutes(dir);
113
+ endpoints = endpoints.concat(routes);
114
+ }
115
+ }
195
116
  }
196
117
 
197
118
  // Send to platform
198
119
  if (endpoints.length > 0) {
199
- console.log(
200
- "[BotVersion SDK] Sending",
201
- endpoints.length,
202
- "endpoints to platform...",
203
- );
204
- self._client
205
- .registerEndpoints(endpoints)
206
- .then(function () {
207
- console.log(
208
- "[BotVersion SDK] ✅ Endpoints queued —",
209
- endpoints.length,
210
- "endpoints will be sent shortly",
211
- );
212
- })
213
- .catch(function (err) {
214
- console.error(
215
- "[BotVersion SDK] ❌ Failed to register endpoints:",
216
- err.message,
217
- );
218
- });
120
+ self._client.registerEndpoints(endpoints);
219
121
  }
220
122
 
221
- console.log("[BotVersion SDK] ✅ Initialization complete");
123
+ var routePatterns = scanner.scanFrontendRoutes(
124
+ options.cwd || process.cwd(),
125
+ );
126
+ if (routePatterns.length > 0) {
127
+ self._client
128
+ .registerRoutePatterns(routePatterns)
129
+ .catch(function (err) {});
130
+ }
222
131
  }, 500);
223
132
  },
224
133
 
@@ -239,385 +148,15 @@ var BotVersion = {
239
148
  }
240
149
  return this._client.registerEndpoints([endpoint]);
241
150
  },
242
-
243
- chat: function (req, res) {
244
- if (!this._client) {
245
- return res.status(500).json({ error: "BotVersion SDK not initialized." });
246
- }
247
-
248
- var getUserContext = this._options && this._options.getUserContext;
249
- var userContext = getUserContext
250
- ? getUserContext(req)
251
- : extractDefaultContext(req);
252
-
253
- this._client
254
- .agentChat({
255
- message: req.body.message,
256
- conversationHistory: req.body.conversationHistory || [],
257
- pageContext: req.body.pageContext || {},
258
- userContext: userContext,
259
- workspaceKey: this._options.apiKey,
260
- chatbotId: req.body.chatbotId,
261
- })
262
- .then(function (response) {
263
- return res.status(200).json(response);
264
- })
265
- .catch(function (err) {
266
- console.error("[BotVersion SDK] chat error:", err);
267
- return res.status(500).json({ error: "Agent error" });
268
- });
269
- },
270
151
  };
271
152
 
272
- function extractDefaultContext(req) {
273
- var user = req.user || (req.session && req.session.user) || {};
274
- var sensitiveKeys = [
275
- "password",
276
- "passwd",
277
- "pwd",
278
- "token",
279
- "accesstoken",
280
- "refreshtoken",
281
- "bearertoken",
282
- "secret",
283
- "privatesecret",
284
- "apikey",
285
- "api_key",
286
- "privatekey",
287
- "private_key",
288
- "signingkey",
289
- "hash",
290
- "passwordhash",
291
- "salt",
292
- "cvv",
293
- "ssn",
294
- "pin",
295
- "creditcard",
296
- "credit_card",
297
- "cardnumber",
298
- "card_number",
299
- "otp",
300
- "mfa",
301
- "totp",
302
- "image",
303
- "avatar",
304
- "photo",
305
- ];
306
-
307
- // Step 1: Flatten nested objects
308
- function flattenObject(obj, prefix) {
309
- prefix = prefix || "";
310
- var result = {};
311
- Object.keys(obj).forEach(function (key) {
312
- var value = obj[key];
313
- var fullKey = prefix ? prefix + "_" + key : key;
314
- if (value === null || value === undefined) return;
315
- if (typeof value === "object" && !Array.isArray(value)) {
316
- var nested = flattenObject(value, fullKey);
317
- Object.keys(nested).forEach(function (k) {
318
- result[k] = nested[k];
319
- });
320
- } else if (typeof value !== "object") {
321
- result[fullKey] = value;
322
- }
323
- });
324
- return result;
325
- }
326
-
327
- var flatUser = flattenObject(user);
328
-
329
- // Step 2: Strip sensitive keys
330
- var context = {};
331
- Object.keys(flatUser).forEach(function (key) {
332
- var isSensitive = sensitiveKeys.some(function (sk) {
333
- return key.toLowerCase().includes(sk);
334
- });
335
- if (!isSensitive) {
336
- context[key] = flatUser[key];
337
- }
338
- });
339
-
340
- // Step 3: Smart aliasing
341
- var idSuffixes = ["id", "key", "code", "ref", "slug", "uuid", "num", "no"];
342
- var cleanPrefixes = [
343
- "active",
344
- "current",
345
- "selected",
346
- "default",
347
- "my",
348
- "the",
349
- "this",
350
- ];
351
-
352
- Object.keys(context).forEach(function (key) {
353
- var lowerKey = key.toLowerCase();
354
- var isIdField = idSuffixes.some(function (suffix) {
355
- return lowerKey.endsWith(suffix);
356
- });
357
-
358
- if (isIdField && context[key]) {
359
- var cleanKey = key;
360
- cleanPrefixes.forEach(function (prefix) {
361
- var regex = new RegExp("^" + prefix, "i");
362
- if (regex.test(cleanKey)) {
363
- cleanKey = cleanKey.replace(regex, "");
364
- cleanKey = cleanKey.charAt(0).toLowerCase() + cleanKey.slice(1);
365
- }
366
- });
367
-
368
- if (cleanKey !== key && !context[cleanKey]) {
369
- context[cleanKey] = context[key];
370
- }
371
- }
372
- });
373
-
374
- return context;
375
- }
376
-
377
- // Named exports for both CJS and ESM
153
+ // Named exports
378
154
  BotVersion.init = BotVersion.init.bind(BotVersion);
379
155
  BotVersion.getEndpoints = BotVersion.getEndpoints.bind(BotVersion);
380
156
  BotVersion.registerEndpoint = BotVersion.registerEndpoint.bind(BotVersion);
381
- BotVersion.chat = BotVersion.chat.bind(BotVersion);
382
-
383
- BotVersion.nextHandler = function (options) {
384
- options = options || {};
385
- var self = this;
386
- var hasScanned = false;
387
-
388
- return async function handler(req, res) {
389
- // ── Initialize on first request if not already done ──
390
- if (!self._initialized || !self._client) {
391
- var apiKey = options.apiKey;
392
- if (!apiKey) {
393
- console.error(
394
- "[BotVersion SDK] ❌ apiKey is required in nextHandler options.",
395
- );
396
- return res
397
- .status(500)
398
- .json({ error: "BotVersion SDK not configured." });
399
- }
400
-
401
- self._client = new BotVersionClient({
402
- apiKey: apiKey,
403
- platformUrl:
404
- options.platformUrl || "https://chatbusiness-two.vercel.app",
405
- debug: options.debug || false,
406
- timeout: options.timeout || 30000,
407
- });
408
-
409
- global._botVersionClient = self._client;
410
- global._botVersionOptions = options;
411
- self._initialized = true;
412
- self._options = options;
413
-
414
- console.log("[BotVersion SDK] ✅ Initialized from nextHandler");
415
- }
416
-
417
- // Restore from global if hot reload lost the instance
418
- if (!self._client && global._botVersionClient) {
419
- self._client = global._botVersionClient;
420
- self._options = global._botVersionOptions;
421
- }
422
-
423
- // ── Handle the chat request ───────────────────────────────────────────
424
- var session = null;
425
- if (options.getSession) {
426
- session = await options.getSession(req, res);
427
- }
428
-
429
- req.user = (session && session.user) || {};
430
-
431
- var getUserContext =
432
- (self._options && self._options.getUserContext) ||
433
- function (req) {
434
- var user = req.user || {};
435
- var sensitiveKeys = [
436
- "password",
437
- "passwd",
438
- "pwd",
439
- "token",
440
- "accesstoken",
441
- "refreshtoken",
442
- "bearertoken",
443
- "secret",
444
- "privatesecret",
445
- "apikey",
446
- "api_key",
447
- "privatekey",
448
- "private_key",
449
- "signingkey",
450
- "hash",
451
- "passwordhash",
452
- "salt",
453
- "cvv",
454
- "ssn",
455
- "pin",
456
- "creditcard",
457
- "credit_card",
458
- "cardnumber",
459
- "card_number",
460
- "otp",
461
- "mfa",
462
- "totp",
463
- "image",
464
- "avatar",
465
- "photo",
466
- ];
467
-
468
- // Step 1: Flatten nested objects
469
- function flattenObject(obj, prefix) {
470
- prefix = prefix || "";
471
- var result = {};
472
- Object.keys(obj).forEach(function (key) {
473
- var value = obj[key];
474
- var fullKey = prefix ? prefix + "_" + key : key;
475
- if (value === null || value === undefined) return;
476
- if (typeof value === "object" && !Array.isArray(value)) {
477
- var nested = flattenObject(value, fullKey);
478
- Object.keys(nested).forEach(function (k) {
479
- result[k] = nested[k];
480
- });
481
- } else if (typeof value !== "object") {
482
- result[fullKey] = value;
483
- }
484
- });
485
- return result;
486
- }
487
-
488
- var flatUser = flattenObject(user);
489
-
490
- // Step 2: Strip sensitive keys
491
- var context = {};
492
- Object.keys(flatUser).forEach(function (key) {
493
- var isSensitive = sensitiveKeys.some(function (sk) {
494
- return key.toLowerCase().includes(sk);
495
- });
496
- if (!isSensitive) {
497
- context[key] = flatUser[key];
498
- }
499
- });
500
-
501
- // Step 3: Smart aliasing
502
- var idSuffixes = [
503
- "id",
504
- "key",
505
- "code",
506
- "ref",
507
- "slug",
508
- "uuid",
509
- "num",
510
- "no",
511
- ];
512
- var cleanPrefixes = [
513
- "active",
514
- "current",
515
- "selected",
516
- "default",
517
- "my",
518
- "the",
519
- "this",
520
- ];
521
-
522
- Object.keys(context).forEach(function (key) {
523
- var lowerKey = key.toLowerCase();
524
- var isIdField = idSuffixes.some(function (suffix) {
525
- return lowerKey.endsWith(suffix);
526
- });
527
-
528
- if (isIdField && context[key]) {
529
- var cleanKey = key;
530
- cleanPrefixes.forEach(function (prefix) {
531
- var regex = new RegExp("^" + prefix, "i");
532
- if (regex.test(cleanKey)) {
533
- cleanKey = cleanKey.replace(regex, "");
534
- cleanKey = cleanKey.charAt(0).toLowerCase() + cleanKey.slice(1);
535
- }
536
- });
537
-
538
- if (cleanKey !== key && !context[cleanKey]) {
539
- context[cleanKey] = context[key];
540
- }
541
- }
542
- });
543
-
544
- console.log("[BotVersion] userContext being sent:", context);
545
- return context;
546
- };
547
-
548
- return new Promise(function (resolve) {
549
- console.log("[BotVersion] req.body:", JSON.stringify(req.body));
550
- console.log("[BotVersion] chatbotId from body:", req.body.chatbotId);
551
- self._client
552
- .agentChat({
553
- message: req.body.message,
554
- conversationHistory: req.body.conversationHistory || [],
555
- pageContext: req.body.pageContext || {},
556
- userContext: getUserContext(req),
557
- chatbotId: req.body.chatbotId,
558
- publicKey: req.body.publicKey,
559
- })
560
- .then(function (response) {
561
- res.status(200).json(response);
562
- resolve();
563
- })
564
- .catch(function (err) {
565
- console.error("[BotVersion SDK] chat error:", err);
566
- res.status(500).json({ error: "Agent error" });
567
- resolve();
568
- });
569
- });
570
- };
571
- };
572
-
573
- BotVersion.nextHandler = BotVersion.nextHandler.bind(BotVersion);
574
-
575
- BotVersion.appRouterHandler = function () {
576
- throw new Error(
577
- "[BotVersion SDK] appRouterHandler is not supported. " +
578
- "Please run: npx botversion-sdk init --key YOUR_KEY --force " +
579
- "to regenerate the correct route file.",
580
- );
581
- };
582
157
 
583
158
  module.exports = BotVersion;
584
159
  module.exports.default = BotVersion;
585
160
  module.exports.init = BotVersion.init;
586
161
  module.exports.getEndpoints = BotVersion.getEndpoints;
587
162
  module.exports.registerEndpoint = BotVersion.registerEndpoint;
588
- module.exports.chat = BotVersion.chat;
589
- module.exports.nextHandler = BotVersion.nextHandler;
590
- module.exports.appRouterHandler = BotVersion.appRouterHandler;
591
-
592
- // ── Framework detection ──────────────────────────────────────────────────────
593
- function detectFramework(app) {
594
- if (!app) return { supported: false, name: "unknown (no app passed)" };
595
-
596
- var isExpress =
597
- typeof app.use === "function" &&
598
- typeof app.get === "function" &&
599
- typeof app.post === "function" &&
600
- (app._router !== undefined ||
601
- app.stack !== undefined ||
602
- app.lazyrouter !== undefined);
603
-
604
- if (isExpress) return { supported: true, name: "Express" };
605
-
606
- if (typeof app.addHook === "function" && typeof app.route === "function") {
607
- return { supported: false, name: "Fastify" };
608
- }
609
-
610
- if (
611
- typeof app.use === "function" &&
612
- app.context !== undefined &&
613
- typeof app.get !== "function"
614
- ) {
615
- return { supported: false, name: "Koa" };
616
- }
617
-
618
- if (typeof app.route === "function" && typeof app.start === "function") {
619
- return { supported: false, name: "Hapi" };
620
- }
621
-
622
- return { supported: false, name: "unknown" };
623
- }