botversion-sdk 2.0.0 → 2.1.1
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/scan-backend.js +75 -0
- package/bin/scan-frontend.js +24 -7
- package/client.js +1 -1
- package/index.js +2 -1
- package/interceptor.js +174 -100
- package/package.json +4 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var path = require("path");
|
|
5
|
+
var fs = require("fs");
|
|
6
|
+
var sdk = require("../index");
|
|
7
|
+
var BotVersionClient = require("../client");
|
|
8
|
+
|
|
9
|
+
// Auto-load env files from the customer's project, same precedence Next.js
|
|
10
|
+
// uses: .env first, then .env.local (which is allowed to override .env).
|
|
11
|
+
// dotenv.config() never overwrites a variable already set in process.env,
|
|
12
|
+
// so if the customer already exported the key in their shell/CI, that
|
|
13
|
+
// still takes priority over anything in these files.
|
|
14
|
+
(function loadEnvFiles() {
|
|
15
|
+
var dotenv;
|
|
16
|
+
try {
|
|
17
|
+
dotenv = require("dotenv");
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return; // dotenv not installed for some reason — skip silently
|
|
20
|
+
}
|
|
21
|
+
var cwd = process.cwd();
|
|
22
|
+
[".env", ".env.local"].forEach(function (file) {
|
|
23
|
+
var filePath = path.join(cwd, file);
|
|
24
|
+
if (fs.existsSync(filePath)) {
|
|
25
|
+
dotenv.config({ path: filePath });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
})();
|
|
29
|
+
|
|
30
|
+
var apiKey = process.env.BOTVERSION_API_KEY;
|
|
31
|
+
var platformUrl =
|
|
32
|
+
process.env.BOTVERSION_PLATFORM_URL || "https://console.botversion.com";
|
|
33
|
+
|
|
34
|
+
if (!apiKey) {
|
|
35
|
+
console.error(
|
|
36
|
+
"[BotVersion] BOTVERSION_API_KEY environment variable is not set. Skipping backend endpoint scan.",
|
|
37
|
+
);
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var cwd = process.cwd();
|
|
42
|
+
var client = new BotVersionClient({ apiKey: apiKey, platformUrl: platformUrl });
|
|
43
|
+
|
|
44
|
+
var detected = sdk.detectFrameworks(cwd);
|
|
45
|
+
var projectType = sdk.classifyInstallation(detected.backend, detected.frontend);
|
|
46
|
+
|
|
47
|
+
var shouldScanBackend =
|
|
48
|
+
projectType === "fullstack" ||
|
|
49
|
+
projectType === "backend-only" ||
|
|
50
|
+
projectType === "mixed";
|
|
51
|
+
|
|
52
|
+
if (!shouldScanBackend) {
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// No live `app` instance exists at build time — pass null.
|
|
57
|
+
// All scanner functions read directly from the filesystem via cwd.
|
|
58
|
+
var endpoints = sdk.runBackendScanner(detected.backend, null, {}, cwd);
|
|
59
|
+
|
|
60
|
+
if (endpoints.length === 0) {
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
client
|
|
65
|
+
.registerEndpointsNow(endpoints)
|
|
66
|
+
.then(function () {
|
|
67
|
+
process.exit(0);
|
|
68
|
+
})
|
|
69
|
+
.catch(function (err) {
|
|
70
|
+
console.error(
|
|
71
|
+
"[BotVersion] Failed to report backend endpoints:",
|
|
72
|
+
err.message,
|
|
73
|
+
);
|
|
74
|
+
process.exit(0);
|
|
75
|
+
});
|
package/bin/scan-frontend.js
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
+
var path = require("path");
|
|
5
|
+
var fs = require("fs");
|
|
4
6
|
var scanner = require("../scanner");
|
|
5
7
|
var BotVersionClient = require("../client");
|
|
6
8
|
var sdk = require("../index");
|
|
7
9
|
|
|
10
|
+
// Auto-load env files from the customer's project, same precedence Next.js
|
|
11
|
+
// uses: .env first, then .env.local (which is allowed to override .env).
|
|
12
|
+
// dotenv.config() never overwrites a variable already set in process.env,
|
|
13
|
+
// so if the customer already exported the key in their shell/CI, that
|
|
14
|
+
// still takes priority over anything in these files.
|
|
15
|
+
(function loadEnvFiles() {
|
|
16
|
+
var dotenv;
|
|
17
|
+
try {
|
|
18
|
+
dotenv = require("dotenv");
|
|
19
|
+
} catch (e) {
|
|
20
|
+
return; // dotenv not installed for some reason — skip silently
|
|
21
|
+
}
|
|
22
|
+
var cwd = process.cwd();
|
|
23
|
+
[".env", ".env.local"].forEach(function (file) {
|
|
24
|
+
var filePath = path.join(cwd, file);
|
|
25
|
+
if (fs.existsSync(filePath)) {
|
|
26
|
+
dotenv.config({ path: filePath });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
})();
|
|
30
|
+
|
|
8
31
|
var apiKey = process.env.BOTVERSION_API_KEY;
|
|
9
32
|
var platformUrl =
|
|
10
|
-
process.env.BOTVERSION_PLATFORM_URL || "https://botversion.com";
|
|
33
|
+
process.env.BOTVERSION_PLATFORM_URL || "https://console.botversion.com";
|
|
11
34
|
|
|
12
35
|
if (!apiKey) {
|
|
13
36
|
console.error(
|
|
@@ -32,12 +55,6 @@ client
|
|
|
32
55
|
sdkLanguage: "javascript",
|
|
33
56
|
})
|
|
34
57
|
.then(function () {
|
|
35
|
-
console.log(
|
|
36
|
-
"[BotVersion] Reported " +
|
|
37
|
-
routePatterns.length +
|
|
38
|
-
" frontend routes. projectType=" +
|
|
39
|
-
projectType,
|
|
40
|
-
);
|
|
41
58
|
process.exit(0);
|
|
42
59
|
})
|
|
43
60
|
.catch(function (err) {
|
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 || "https://botversion.com";
|
|
14
|
+
this.platformUrl = options.platformUrl || "https://console.botversion.com";
|
|
15
15
|
this.debug = options.debug || false;
|
|
16
16
|
this.timeout = options.timeout || 30000;
|
|
17
17
|
|
package/index.js
CHANGED
|
@@ -421,7 +421,7 @@ var BotVersion = {
|
|
|
421
421
|
|
|
422
422
|
this._client = new BotVersionClient({
|
|
423
423
|
apiKey: options.apiKey,
|
|
424
|
-
platformUrl: options.platformUrl || "https://botversion.com",
|
|
424
|
+
platformUrl: options.platformUrl || "https://console.botversion.com",
|
|
425
425
|
debug: options.debug || false,
|
|
426
426
|
timeout: options.timeout || 30000,
|
|
427
427
|
});
|
|
@@ -504,3 +504,4 @@ module.exports.getEndpoints = BotVersion.getEndpoints;
|
|
|
504
504
|
module.exports.registerEndpoint = BotVersion.registerEndpoint;
|
|
505
505
|
module.exports.detectFrameworks = detectFrameworks;
|
|
506
506
|
module.exports.classifyInstallation = classifyInstallation;
|
|
507
|
+
module.exports.runBackendScanner = runBackendScanner;
|
package/interceptor.js
CHANGED
|
@@ -92,34 +92,43 @@ function attachInterceptor(app, client, options) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
const method = req.method.toUpperCase();
|
|
95
|
-
const normalizedPath = normalizePath(path);
|
|
96
|
-
const endpointKey = method + ":" + normalizedPath;
|
|
97
|
-
|
|
98
|
-
const bodyStructure = buildBodyStructure(req.body);
|
|
99
|
-
const bodyKey =
|
|
100
|
-
endpointKey +
|
|
101
|
-
":" +
|
|
102
|
-
Object.keys(bodyStructure || {})
|
|
103
|
-
.sort()
|
|
104
|
-
.join(",");
|
|
105
|
-
|
|
106
|
-
if (!reportedEndpoints.has(bodyKey)) {
|
|
107
|
-
reportedEndpoints.add(bodyKey);
|
|
108
|
-
|
|
109
|
-
const jsonSchema = structureToJsonSchema(bodyStructure);
|
|
110
95
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
96
|
+
// Split batched tRPC paths into separate clean paths + matching body
|
|
97
|
+
// slots, so each real procedure is registered as its own endpoint.
|
|
98
|
+
const rawPaths = splitBatchPath(path);
|
|
99
|
+
const bodySlots =
|
|
100
|
+
rawPaths.length > 1 ? splitBatchBody(req.body) : [req.body];
|
|
101
|
+
|
|
102
|
+
rawPaths.forEach(function (singlePath, i) {
|
|
103
|
+
const normalizedPath = normalizePath(singlePath);
|
|
104
|
+
const endpointKey = method + ":" + normalizedPath;
|
|
105
|
+
const slotBody = bodySlots[i] || null;
|
|
106
|
+
|
|
107
|
+
const bodyStructure = buildBodyStructure(slotBody);
|
|
108
|
+
const bodyKey =
|
|
109
|
+
endpointKey +
|
|
110
|
+
":" +
|
|
111
|
+
Object.keys(bodyStructure || {})
|
|
112
|
+
.sort()
|
|
113
|
+
.join(",");
|
|
114
|
+
|
|
115
|
+
if (!reportedEndpoints.has(bodyKey)) {
|
|
116
|
+
reportedEndpoints.add(bodyKey);
|
|
117
|
+
|
|
118
|
+
const jsonSchema = structureToJsonSchema(bodyStructure);
|
|
119
|
+
|
|
120
|
+
setImmediate(function () {
|
|
121
|
+
client
|
|
122
|
+
.updateEndpoint({
|
|
123
|
+
method: method,
|
|
124
|
+
path: normalizedPath,
|
|
125
|
+
requestBody: jsonSchema,
|
|
126
|
+
detectedBy: "runtime",
|
|
127
|
+
})
|
|
128
|
+
.catch(function (err) {});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
123
132
|
|
|
124
133
|
next();
|
|
125
134
|
});
|
|
@@ -184,6 +193,74 @@ function unwrapTrpcJsonEnvelope(obj) {
|
|
|
184
193
|
return obj;
|
|
185
194
|
}
|
|
186
195
|
|
|
196
|
+
/**
|
|
197
|
+
* tRPC batches multiple procedure calls into one URL, e.g.
|
|
198
|
+
* /api/trpc/me.get,getUserTopBanners,bookingUnconfirmedCount
|
|
199
|
+
* The procedure names appear comma-separated, only in the LAST path segment.
|
|
200
|
+
* This splits that into separate, clean paths — one per real procedure —
|
|
201
|
+
* so each gets registered as its own endpoint instead of one garbled,
|
|
202
|
+
* comma-joined path. Non-batched paths are returned unchanged as a
|
|
203
|
+
* single-item array.
|
|
204
|
+
*/
|
|
205
|
+
function splitBatchPath(rawPath) {
|
|
206
|
+
// tRPC is always mounted at a fixed base containing "/trpc/" — e.g.
|
|
207
|
+
// "/api/trpc/". Everything after that marker is the comma-separated
|
|
208
|
+
// procedure list, and each comma-separated piece is ALREADY a complete
|
|
209
|
+
// procedure path in its own right (it may contain further "/" for
|
|
210
|
+
// nested routers, e.g. "me/get") — it must never have another
|
|
211
|
+
// procedure's prefix re-attached to it.
|
|
212
|
+
const trpcMarker = "/trpc/";
|
|
213
|
+
const markerIndex = rawPath.indexOf(trpcMarker);
|
|
214
|
+
if (markerIndex === -1) return [rawPath];
|
|
215
|
+
|
|
216
|
+
const base = rawPath.slice(0, markerIndex + trpcMarker.length);
|
|
217
|
+
const tail = rawPath.slice(markerIndex + trpcMarker.length);
|
|
218
|
+
|
|
219
|
+
if (!tail.includes(",")) return [rawPath];
|
|
220
|
+
|
|
221
|
+
return tail
|
|
222
|
+
.split(",")
|
|
223
|
+
.map(function (proc) {
|
|
224
|
+
return proc.trim();
|
|
225
|
+
})
|
|
226
|
+
.filter(Boolean)
|
|
227
|
+
.map(function (proc) {
|
|
228
|
+
return base + proc;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Splits a batched tRPC request body/input — { "0": {...}, "1": {...} } —
|
|
234
|
+
* into an array of individual bodies, in the same order as the batch keys,
|
|
235
|
+
* which lines up with the same order splitBatchPath() returns procedure
|
|
236
|
+
* names in. Each slot is unwrapped from its own { json: {...} } envelope.
|
|
237
|
+
* Returns a single-item array unchanged if the body isn't actually batched,
|
|
238
|
+
* so callers can always treat the result uniformly.
|
|
239
|
+
*/
|
|
240
|
+
function splitBatchBody(bodyObj) {
|
|
241
|
+
if (!bodyObj || typeof bodyObj !== "object") return [bodyObj || null];
|
|
242
|
+
|
|
243
|
+
const keys = Object.keys(bodyObj);
|
|
244
|
+
const isBatch =
|
|
245
|
+
keys.length > 0 &&
|
|
246
|
+
keys.every(function (k) {
|
|
247
|
+
return /^\d+$/.test(k);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
if (!isBatch) return [bodyObj];
|
|
251
|
+
|
|
252
|
+
return keys
|
|
253
|
+
.sort(function (a, b) {
|
|
254
|
+
return Number(a) - Number(b);
|
|
255
|
+
})
|
|
256
|
+
.map(function (k) {
|
|
257
|
+
const entry = bodyObj[k];
|
|
258
|
+
return entry && typeof entry === "object"
|
|
259
|
+
? unwrapTrpcJsonEnvelope(entry)
|
|
260
|
+
: entry || null;
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
187
264
|
/**
|
|
188
265
|
* Extract just the structure of a request body
|
|
189
266
|
* (keys and value types — never actual values for security)
|
|
@@ -315,18 +392,36 @@ function attachNextJsInterceptor(client, options) {
|
|
|
315
392
|
return; // handled directly — don't pass through to the app
|
|
316
393
|
}
|
|
317
394
|
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
395
|
+
const DEV_ASSET_MARKERS = [
|
|
396
|
+
"/node_modules",
|
|
397
|
+
"/.svelte-kit",
|
|
398
|
+
"/@fs",
|
|
399
|
+
"/@vite",
|
|
400
|
+
"/@id",
|
|
401
|
+
"/debug-cors",
|
|
402
|
+
"/src/lib",
|
|
403
|
+
"/src/routes",
|
|
404
|
+
];
|
|
405
|
+
const STATIC_ASSET_EXT =
|
|
406
|
+
/\.(svelte|vue|css|scss|map|woff2?|ttf|eot|ico|png|jpe?g|gif|svg|webp|mjs)(\?.*)?$/i;
|
|
407
|
+
|
|
408
|
+
const shouldIgnore =
|
|
409
|
+
(options.exclude || [])
|
|
410
|
+
.concat([
|
|
411
|
+
"/health",
|
|
412
|
+
"/favicon.ico",
|
|
413
|
+
"/_next",
|
|
414
|
+
"/static",
|
|
415
|
+
"/public",
|
|
416
|
+
"/admin",
|
|
417
|
+
])
|
|
418
|
+
.some(function (p) {
|
|
419
|
+
return path.startsWith(p);
|
|
420
|
+
}) ||
|
|
421
|
+
DEV_ASSET_MARKERS.some(function (m) {
|
|
422
|
+
return path.includes(m);
|
|
423
|
+
}) ||
|
|
424
|
+
STATIC_ASSET_EXT.test(path);
|
|
330
425
|
|
|
331
426
|
const isApiPath = path.startsWith(options.apiPrefix || "/api");
|
|
332
427
|
|
|
@@ -354,25 +449,28 @@ function attachNextJsInterceptor(client, options) {
|
|
|
354
449
|
// Helper to send body structure to platform
|
|
355
450
|
function reportBody(bodyObj) {
|
|
356
451
|
try {
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
// 2. The URL contains an "input" query parameter
|
|
363
|
-
// 3. The path looks like a tRPC endpoint (/trpc/)
|
|
364
|
-
// This prevents accidentally treating regular REST query params
|
|
365
|
-
// (e.g. ?filter=active&limit=10) as a body schema.
|
|
452
|
+
// Split the (possibly batched) tRPC path into separate,
|
|
453
|
+
// clean paths — one per real procedure — instead of one
|
|
454
|
+
// garbled comma-joined path.
|
|
455
|
+
const splitPaths = splitBatchPath(path).map(normalizePath);
|
|
456
|
+
|
|
366
457
|
const isTrpcPath = path.includes("/trpc/");
|
|
367
458
|
const hasInputParam = req.url && req.url.includes("input=");
|
|
368
459
|
|
|
460
|
+
// ── tRPC GET input extraction ──────────────────────────────────────
|
|
461
|
+
// tRPC GET requests send their input as a URL-encoded JSON query
|
|
462
|
+
// parameter called "input". We only attempt this extraction when
|
|
463
|
+
// the body is empty, the URL has an "input" param, and the path
|
|
464
|
+
// looks like a tRPC endpoint — so regular REST query params are
|
|
465
|
+
// never mistaken for a body schema.
|
|
466
|
+
let bodySlots = null;
|
|
467
|
+
|
|
369
468
|
if (
|
|
370
469
|
isTrpcPath &&
|
|
371
470
|
hasInputParam &&
|
|
372
471
|
(!bodyObj || Object.keys(bodyObj).length === 0)
|
|
373
472
|
) {
|
|
374
473
|
try {
|
|
375
|
-
// Use URL constructor to safely parse query params
|
|
376
474
|
const urlObj = new URL(req.url, "http://localhost");
|
|
377
475
|
const inputParam = urlObj.searchParams.get("input");
|
|
378
476
|
|
|
@@ -381,7 +479,6 @@ function attachNextJsInterceptor(client, options) {
|
|
|
381
479
|
try {
|
|
382
480
|
decoded = JSON.parse(decodeURIComponent(inputParam));
|
|
383
481
|
} catch (e) {
|
|
384
|
-
// input param exists but is not valid JSON — skip
|
|
385
482
|
decoded = null;
|
|
386
483
|
}
|
|
387
484
|
|
|
@@ -390,44 +487,10 @@ function attachNextJsInterceptor(client, options) {
|
|
|
390
487
|
typeof decoded === "object" &&
|
|
391
488
|
!Array.isArray(decoded)
|
|
392
489
|
) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
const isBatch =
|
|
398
|
-
keys.length > 0 && keys.every((k) => /^\d+$/.test(k));
|
|
399
|
-
|
|
400
|
-
if (isBatch) {
|
|
401
|
-
// Merge all batch entries into one flat object
|
|
402
|
-
// so we capture the full schema across all procedures
|
|
403
|
-
const merged = {};
|
|
404
|
-
keys.forEach((k) => {
|
|
405
|
-
const entry = decoded[k];
|
|
406
|
-
if (entry && typeof entry === "object") {
|
|
407
|
-
// Unwrap { json: {...} } envelope if present
|
|
408
|
-
const unwrapped =
|
|
409
|
-
entry.json && typeof entry.json === "object"
|
|
410
|
-
? entry.json
|
|
411
|
-
: entry;
|
|
412
|
-
Object.assign(merged, unwrapped);
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
if (Object.keys(merged).length > 0) {
|
|
416
|
-
bodyObj = merged;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
// Format 2 — single with superjson: { json: {...}, meta: {...} }
|
|
420
|
-
else if (
|
|
421
|
-
decoded.json &&
|
|
422
|
-
typeof decoded.json === "object"
|
|
423
|
-
) {
|
|
424
|
-
bodyObj = decoded.json;
|
|
425
|
-
}
|
|
426
|
-
// Format 3 — already unwrapped: { group: {...}, limit: 10 }
|
|
427
|
-
// Only use this if it doesn't look like a tRPC envelope
|
|
428
|
-
else if (!decoded.meta && !decoded.json) {
|
|
429
|
-
bodyObj = decoded;
|
|
430
|
-
}
|
|
490
|
+
// Splits batched input into one slot per procedure,
|
|
491
|
+
// in path order — never merges different procedures'
|
|
492
|
+
// fields together.
|
|
493
|
+
bodySlots = splitBatchBody(decoded);
|
|
431
494
|
}
|
|
432
495
|
}
|
|
433
496
|
} catch (e) {
|
|
@@ -435,18 +498,29 @@ function attachNextJsInterceptor(client, options) {
|
|
|
435
498
|
}
|
|
436
499
|
}
|
|
437
500
|
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
501
|
+
// Normal (non-GET-input) body — split the same way if the
|
|
502
|
+
// path turned out to be a batch.
|
|
503
|
+
if (!bodySlots) {
|
|
504
|
+
bodySlots =
|
|
505
|
+
splitPaths.length > 1 ? splitBatchBody(bodyObj) : [bodyObj];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Report each real procedure as its own endpoint, with only
|
|
509
|
+
// its own fields — never another procedure's fields mixed in.
|
|
510
|
+
splitPaths.forEach(function (singlePath, i) {
|
|
511
|
+
const slotBody = bodySlots[i] || null;
|
|
512
|
+
const parsedBody = structureToJsonSchema(
|
|
513
|
+
buildBodyStructure(slotBody),
|
|
514
|
+
);
|
|
515
|
+
client
|
|
516
|
+
.updateEndpoint({
|
|
517
|
+
method: method,
|
|
518
|
+
path: singlePath,
|
|
519
|
+
requestBody: parsedBody,
|
|
520
|
+
detectedBy: "runtime",
|
|
521
|
+
})
|
|
522
|
+
.catch(function () {});
|
|
523
|
+
});
|
|
450
524
|
} catch (e) {}
|
|
451
525
|
}
|
|
452
526
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botversion-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "BotVersion SDK — auto-detect and register your API endpoints",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"botversion-scan-frontend": "
|
|
7
|
+
"botversion-scan-frontend-routes": "bin/scan-frontend.js",
|
|
8
|
+
"botversion-scan-endpoints": "bin/scan-backend.js"
|
|
8
9
|
},
|
|
9
10
|
"keywords": [
|
|
10
11
|
"botversion",
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"@babel/parser": "^7.29.3",
|
|
20
21
|
"@babel/traverse": "^7.29.0",
|
|
22
|
+
"dotenv": "^16.4.5",
|
|
21
23
|
"express": "^4.18.2"
|
|
22
24
|
}
|
|
23
25
|
}
|