botversion-sdk 1.0.6 → 1.1.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/client.js +32 -4
- package/index.js +285 -68
- package/interceptor.js +411 -15
- package/package.json +1 -1
- package/scanner.js +1279 -38
package/client.js
CHANGED
|
@@ -80,9 +80,7 @@ BotVersionClient.prototype._flush = function () {
|
|
|
80
80
|
endpoints: toSend,
|
|
81
81
|
})
|
|
82
82
|
.then(function (data) {})
|
|
83
|
-
.catch(function (err) {
|
|
84
|
-
console.error("[botversion] flush failed:", err.message);
|
|
85
|
-
});
|
|
83
|
+
.catch(function (err) {});
|
|
86
84
|
};
|
|
87
85
|
|
|
88
86
|
/**
|
|
@@ -156,6 +154,19 @@ BotVersionClient.prototype._post = function (path, data) {
|
|
|
156
154
|
|
|
157
155
|
res.on("end", function () {
|
|
158
156
|
try {
|
|
157
|
+
if (!responseData || responseData.trim() === "") {
|
|
158
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
159
|
+
resolve({});
|
|
160
|
+
return;
|
|
161
|
+
} else {
|
|
162
|
+
reject(
|
|
163
|
+
new Error(
|
|
164
|
+
"Platform returned " + res.statusCode + ": empty response",
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
159
170
|
var parsed = JSON.parse(responseData);
|
|
160
171
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
161
172
|
resolve(parsed);
|
|
@@ -220,6 +231,19 @@ BotVersionClient.prototype._get = function (path) {
|
|
|
220
231
|
|
|
221
232
|
res.on("end", function () {
|
|
222
233
|
try {
|
|
234
|
+
if (!responseData || responseData.trim() === "") {
|
|
235
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
236
|
+
resolve({});
|
|
237
|
+
return;
|
|
238
|
+
} else {
|
|
239
|
+
reject(
|
|
240
|
+
new Error(
|
|
241
|
+
"Platform returned " + res.statusCode + ": empty response",
|
|
242
|
+
),
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
223
247
|
var parsed = JSON.parse(responseData);
|
|
224
248
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
225
249
|
resolve(parsed);
|
|
@@ -234,7 +258,11 @@ BotVersionClient.prototype._get = function (path) {
|
|
|
234
258
|
);
|
|
235
259
|
}
|
|
236
260
|
} catch (e) {
|
|
237
|
-
|
|
261
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
262
|
+
resolve({});
|
|
263
|
+
} else {
|
|
264
|
+
reject(new Error("Invalid JSON response from platform"));
|
|
265
|
+
}
|
|
238
266
|
}
|
|
239
267
|
});
|
|
240
268
|
});
|
package/index.js
CHANGED
|
@@ -1,10 +1,266 @@
|
|
|
1
|
-
// botversion-sdk/index.js
|
|
2
1
|
"use strict";
|
|
3
2
|
|
|
4
3
|
var scanner = require("./scanner");
|
|
5
4
|
var interceptor = require("./interceptor");
|
|
6
5
|
var BotVersionClient = require("./client");
|
|
7
6
|
|
|
7
|
+
// ── Framework auto-detection ─────────────────────────────────────────────────
|
|
8
|
+
// Reads package.json from the customer's codebase and figures out
|
|
9
|
+
// which backend and frontend frameworks they are using.
|
|
10
|
+
function detectFrameworks(cwd) {
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
|
|
14
|
+
const result = {
|
|
15
|
+
backend: "unknown",
|
|
16
|
+
frontend: "unknown",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
let pkg = {};
|
|
20
|
+
try {
|
|
21
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
22
|
+
if (fs.existsSync(pkgPath)) {
|
|
23
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const deps = Object.assign(
|
|
30
|
+
{},
|
|
31
|
+
pkg.dependencies || {},
|
|
32
|
+
pkg.devDependencies || {},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// ── Backend detection ──────────────────────────────────────────────────────
|
|
36
|
+
if (deps["@nestjs/core"]) {
|
|
37
|
+
result.backend = "nestjs";
|
|
38
|
+
} else if (deps["fastify"]) {
|
|
39
|
+
result.backend = "fastify";
|
|
40
|
+
} else if (deps["@adonisjs/core"]) {
|
|
41
|
+
result.backend = "adonis";
|
|
42
|
+
} else if (deps["@hapi/hapi"] || deps["hapi"]) {
|
|
43
|
+
result.backend = "hapi";
|
|
44
|
+
} else if (deps["koa"]) {
|
|
45
|
+
result.backend = "koa";
|
|
46
|
+
} else if (deps["next"]) {
|
|
47
|
+
result.backend = "nextjs";
|
|
48
|
+
} else if (deps["express"]) {
|
|
49
|
+
result.backend = "express";
|
|
50
|
+
} else if (deps["@remix-run/node"] || deps["@remix-run/serve"]) {
|
|
51
|
+
result.backend = "remix";
|
|
52
|
+
} else if (deps["nuxt"] || deps["nuxt3"] || deps["@nuxt/core"]) {
|
|
53
|
+
result.backend = "nuxt";
|
|
54
|
+
} else if (deps["@sveltejs/kit"]) {
|
|
55
|
+
result.backend = "sveltekit";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Frontend detection ─────────────────────────────────────────────────────
|
|
59
|
+
if (deps["next"]) {
|
|
60
|
+
result.frontend = "nextjs";
|
|
61
|
+
} else if (deps["nuxt"] || deps["nuxt3"] || deps["@nuxt/core"]) {
|
|
62
|
+
result.frontend = "nuxt";
|
|
63
|
+
} else if (deps["@sveltejs/kit"]) {
|
|
64
|
+
result.frontend = "sveltekit";
|
|
65
|
+
} else if (deps["@remix-run/react"]) {
|
|
66
|
+
result.frontend = "remix";
|
|
67
|
+
} else if (deps["@tanstack/router"] || deps["@tanstack/react-router"]) {
|
|
68
|
+
result.frontend = "tanstack";
|
|
69
|
+
} else if (deps["vue"] && (deps["vue-router"] || deps["@nuxtjs/router"])) {
|
|
70
|
+
result.frontend = "vue";
|
|
71
|
+
} else if (deps["@angular/core"]) {
|
|
72
|
+
result.frontend = "angular";
|
|
73
|
+
} else if (deps["svelte"]) {
|
|
74
|
+
result.frontend = "svelte";
|
|
75
|
+
} else if (deps["react"] && deps["react-router-dom"]) {
|
|
76
|
+
result.frontend = "react-router";
|
|
77
|
+
} else if (deps["astro"]) {
|
|
78
|
+
result.frontend = "astro";
|
|
79
|
+
} else if (deps["react"]) {
|
|
80
|
+
result.frontend = "react";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── Run the right backend scanner ────────────────────────────────────────────
|
|
87
|
+
function runBackendScanner(detectedBackend, app, options, cwd) {
|
|
88
|
+
const endpoints = [];
|
|
89
|
+
|
|
90
|
+
switch (detectedBackend) {
|
|
91
|
+
case "express":
|
|
92
|
+
endpoints.push(...scanner.scanExpressRoutes(app, cwd));
|
|
93
|
+
break;
|
|
94
|
+
|
|
95
|
+
case "nextjs": {
|
|
96
|
+
const fs = require("fs");
|
|
97
|
+
const path = require("path");
|
|
98
|
+
|
|
99
|
+
const possibleAppDirs = [
|
|
100
|
+
path.join(cwd, "app"),
|
|
101
|
+
path.join(cwd, "src", "app"),
|
|
102
|
+
];
|
|
103
|
+
const possiblePagesDirs = [
|
|
104
|
+
path.join(cwd, "pages"),
|
|
105
|
+
path.join(cwd, "src", "pages"),
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
for (const dir of possibleAppDirs) {
|
|
109
|
+
if (fs.existsSync(dir)) {
|
|
110
|
+
endpoints.push(...scanner.scanNextJsAppRoutes(dir));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const dir of possiblePagesDirs) {
|
|
114
|
+
if (fs.existsSync(dir)) {
|
|
115
|
+
endpoints.push(...scanner.scanNextJsRoutes(dir));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
case "fastify":
|
|
122
|
+
endpoints.push(...scanner.scanFastifyRoutes(cwd));
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
case "nestjs":
|
|
126
|
+
endpoints.push(...scanner.scanNestJsRoutes(cwd));
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case "koa":
|
|
130
|
+
endpoints.push(...scanner.scanKoaRoutes(cwd));
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case "hapi":
|
|
134
|
+
endpoints.push(...scanner.scanHapiRoutes(cwd));
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case "adonis":
|
|
138
|
+
endpoints.push(...scanner.scanAdonisRoutes(cwd));
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
case "nuxt":
|
|
142
|
+
endpoints.push(...scanner.scanNuxtServerRoutes(cwd));
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case "sveltekit":
|
|
146
|
+
endpoints.push(...scanner.scanSvelteKitServerRoutes(cwd));
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case "remix":
|
|
150
|
+
endpoints.push(...scanner.scanRemixServerRoutes(cwd));
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
default:
|
|
154
|
+
// Unknown backend — try Express as best guess
|
|
155
|
+
if (app) {
|
|
156
|
+
endpoints.push(...scanner.scanExpressRoutes(app, cwd));
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return endpoints;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ── Attach the right runtime interceptor ─────────────────────────────────────
|
|
165
|
+
function attachRuntimeInterceptor(detectedBackend, app, client, options) {
|
|
166
|
+
switch (detectedBackend) {
|
|
167
|
+
case "express":
|
|
168
|
+
interceptor.attachInterceptor(app, client, {
|
|
169
|
+
exclude: options.exclude || [],
|
|
170
|
+
apiPrefix: options.apiPrefix || null,
|
|
171
|
+
debug: options.debug || false,
|
|
172
|
+
});
|
|
173
|
+
break;
|
|
174
|
+
|
|
175
|
+
case "nextjs":
|
|
176
|
+
interceptor.attachNextJsInterceptor(client, {
|
|
177
|
+
exclude: options.exclude || [],
|
|
178
|
+
apiPrefix: options.apiPrefix || "/api",
|
|
179
|
+
debug: options.debug || false,
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case "fastify":
|
|
184
|
+
if (app) {
|
|
185
|
+
interceptor.attachFastifyInterceptor(app, client, {
|
|
186
|
+
exclude: options.exclude || [],
|
|
187
|
+
apiPrefix: options.apiPrefix || null,
|
|
188
|
+
debug: options.debug || false,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
case "koa":
|
|
194
|
+
if (app) {
|
|
195
|
+
interceptor.attachKoaInterceptor(app, client, {
|
|
196
|
+
exclude: options.exclude || [],
|
|
197
|
+
apiPrefix: options.apiPrefix || null,
|
|
198
|
+
debug: options.debug || false,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
case "hapi":
|
|
204
|
+
if (app) {
|
|
205
|
+
interceptor.attachHapiInterceptor(app, client, {
|
|
206
|
+
exclude: options.exclude || [],
|
|
207
|
+
apiPrefix: options.apiPrefix || null,
|
|
208
|
+
debug: options.debug || false,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
|
|
213
|
+
case "nestjs":
|
|
214
|
+
interceptor.attachNestJsInterceptor(client, {
|
|
215
|
+
exclude: options.exclude || [],
|
|
216
|
+
apiPrefix: options.apiPrefix || "/",
|
|
217
|
+
debug: options.debug || false,
|
|
218
|
+
});
|
|
219
|
+
break;
|
|
220
|
+
|
|
221
|
+
case "sveltekit":
|
|
222
|
+
interceptor.attachSvelteKitInterceptor(client, {
|
|
223
|
+
exclude: options.exclude || [],
|
|
224
|
+
apiPrefix: options.apiPrefix || "/",
|
|
225
|
+
debug: options.debug || false,
|
|
226
|
+
});
|
|
227
|
+
break;
|
|
228
|
+
|
|
229
|
+
case "nuxt":
|
|
230
|
+
interceptor.attachNuxtInterceptor(client, {
|
|
231
|
+
exclude: options.exclude || [],
|
|
232
|
+
apiPrefix: options.apiPrefix || "/api",
|
|
233
|
+
debug: options.debug || false,
|
|
234
|
+
});
|
|
235
|
+
break;
|
|
236
|
+
|
|
237
|
+
case "remix":
|
|
238
|
+
interceptor.attachRemixInterceptor(client, {
|
|
239
|
+
exclude: options.exclude || [],
|
|
240
|
+
apiPrefix: options.apiPrefix || "/",
|
|
241
|
+
debug: options.debug || false,
|
|
242
|
+
});
|
|
243
|
+
break;
|
|
244
|
+
|
|
245
|
+
case "adonis":
|
|
246
|
+
interceptor.attachAdonisInterceptor(client, {
|
|
247
|
+
exclude: options.exclude || [],
|
|
248
|
+
apiPrefix: options.apiPrefix || "/",
|
|
249
|
+
debug: options.debug || false,
|
|
250
|
+
});
|
|
251
|
+
break;
|
|
252
|
+
|
|
253
|
+
default:
|
|
254
|
+
// Unknown — fall back to Next.js http server patch as best guess
|
|
255
|
+
interceptor.attachNextJsInterceptor(client, {
|
|
256
|
+
exclude: options.exclude || [],
|
|
257
|
+
apiPrefix: options.apiPrefix || "/",
|
|
258
|
+
debug: options.debug || false,
|
|
259
|
+
});
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
8
264
|
var BotVersion = {
|
|
9
265
|
_client: null,
|
|
10
266
|
_initialized: false,
|
|
@@ -31,12 +287,9 @@ var BotVersion = {
|
|
|
31
287
|
this._client = global._botVersionClient;
|
|
32
288
|
this._options = global._botVersionOptions;
|
|
33
289
|
this._initialized = true;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
apiPrefix: (global._botVersionOptions || {}).apiPrefix || "/api",
|
|
38
|
-
debug: (global._botVersionOptions || {}).debug || false,
|
|
39
|
-
});
|
|
290
|
+
const savedOptions = global._botVersionOptions || {};
|
|
291
|
+
const savedFramework = global._botVersionFramework || "nextjs";
|
|
292
|
+
attachRuntimeInterceptor(savedFramework, app, this._client, savedOptions);
|
|
40
293
|
return;
|
|
41
294
|
}
|
|
42
295
|
|
|
@@ -55,80 +308,44 @@ var BotVersion = {
|
|
|
55
308
|
global._botVersionOptions = options;
|
|
56
309
|
|
|
57
310
|
var self = this;
|
|
311
|
+
var cwd = options.cwd || process.cwd();
|
|
58
312
|
var debug = options.debug || false;
|
|
59
313
|
|
|
60
|
-
// ──
|
|
61
|
-
|
|
62
|
-
// Express
|
|
63
|
-
interceptor.attachInterceptor(app, self._client, {
|
|
64
|
-
exclude: options.exclude || [],
|
|
65
|
-
apiPrefix: options.apiPrefix || null,
|
|
66
|
-
debug: debug,
|
|
67
|
-
});
|
|
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
|
-
});
|
|
75
|
-
}
|
|
314
|
+
// ── Auto-detect frameworks ───────────────────────────────────────────────
|
|
315
|
+
var detected = detectFrameworks(cwd);
|
|
76
316
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
317
|
+
// Allow manual override from options
|
|
318
|
+
var detectedBackend = options.framework || detected.backend;
|
|
319
|
+
var detectedFrontend = options.frontendFramework || detected.frontend;
|
|
80
320
|
|
|
81
|
-
|
|
82
|
-
if (app) {
|
|
83
|
-
var cwd = options.cwd || process.cwd();
|
|
84
|
-
endpoints = scanner.scanExpressRoutes(app, cwd);
|
|
85
|
-
}
|
|
321
|
+
global._botVersionFramework = detectedBackend;
|
|
86
322
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const path = require("path");
|
|
91
|
-
const cwd = process.cwd();
|
|
92
|
-
|
|
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
|
-
}
|
|
108
|
-
}
|
|
323
|
+
if (debug) {
|
|
324
|
+
// Detected: detectedBackend, detectedFrontend
|
|
325
|
+
}
|
|
109
326
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const routes = scanner.scanNextJsRoutes(dir);
|
|
113
|
-
endpoints = endpoints.concat(routes);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
327
|
+
// ── Attach runtime interceptor ───────────────────────────────────────────
|
|
328
|
+
attachRuntimeInterceptor(detectedBackend, app, self._client, options);
|
|
117
329
|
|
|
118
|
-
|
|
330
|
+
// ── Static scan ──────────────────────────────────────────────────────────
|
|
331
|
+
setTimeout(function () {
|
|
332
|
+
// Run backend scanner
|
|
333
|
+
var endpoints = runBackendScanner(detectedBackend, app, options, cwd);
|
|
334
|
+
|
|
335
|
+
// Send endpoints to platform
|
|
119
336
|
if (endpoints.length > 0) {
|
|
120
337
|
self._client.registerEndpoints(endpoints);
|
|
121
338
|
}
|
|
122
339
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
340
|
+
// Run frontend route scanner
|
|
341
|
+
var routePatterns = scanner.scanFrontendRoutes(cwd);
|
|
342
|
+
|
|
126
343
|
if (routePatterns.length > 0) {
|
|
127
|
-
self._client
|
|
128
|
-
|
|
129
|
-
|
|
344
|
+
self._client.registerRoutePatterns(routePatterns).catch(function () {
|
|
345
|
+
// silently ignore — non-critical
|
|
346
|
+
});
|
|
130
347
|
}
|
|
131
|
-
},
|
|
348
|
+
}, 3000);
|
|
132
349
|
},
|
|
133
350
|
|
|
134
351
|
getEndpoints: function () {
|