botversion-sdk 1.0.5 → 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/bin/init.js +3 -3
- package/cli/generator.js +3 -3
- package/cli/writer.js +1 -1
- package/client.js +38 -223
- package/index.js +285 -211
- package/interceptor.js +451 -118
- package/package.json +4 -3
- package/scanner.js +1281 -100
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
|
|
@@ -120,11 +18,9 @@ function attachInterceptor(app, client, options) {
|
|
|
120
18
|
"/_next",
|
|
121
19
|
"/static",
|
|
122
20
|
"/public",
|
|
21
|
+
"/admin",
|
|
123
22
|
].concat(options.exclude || []);
|
|
124
23
|
|
|
125
|
-
// Track which endpoints we have already reported
|
|
126
|
-
const reportedEndpoints = new Set();
|
|
127
|
-
|
|
128
24
|
app.use(function botVersionInterceptor(req, res, next) {
|
|
129
25
|
const path = req.path || req.url || "";
|
|
130
26
|
const shouldIgnore = ignorePaths.some(function (p) {
|
|
@@ -182,23 +78,12 @@ function attachInterceptor(app, client, options) {
|
|
|
182
78
|
requestBody: jsonSchema,
|
|
183
79
|
detectedBy: "runtime",
|
|
184
80
|
})
|
|
185
|
-
.catch(function (err) {
|
|
186
|
-
if (options.debug) {
|
|
187
|
-
console.warn(
|
|
188
|
-
"[BotVersion SDK] Failed to report endpoint:",
|
|
189
|
-
err.message,
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
81
|
+
.catch(function (err) {});
|
|
193
82
|
});
|
|
194
83
|
}
|
|
195
84
|
|
|
196
85
|
next();
|
|
197
86
|
});
|
|
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
87
|
}
|
|
203
88
|
|
|
204
89
|
/**
|
|
@@ -286,8 +171,456 @@ function buildBodyStructure(body) {
|
|
|
286
171
|
return structure;
|
|
287
172
|
}
|
|
288
173
|
|
|
174
|
+
function attachNextJsInterceptor(client, options) {
|
|
175
|
+
try {
|
|
176
|
+
const http = require("http");
|
|
177
|
+
const originalEmit = http.Server.prototype.emit;
|
|
178
|
+
|
|
179
|
+
http.Server.prototype.emit = function (event, req, res) {
|
|
180
|
+
if (event === "request") {
|
|
181
|
+
const path = req.url ? req.url.split("?")[0] : "";
|
|
182
|
+
const method = req.method ? req.method.toUpperCase() : "";
|
|
183
|
+
|
|
184
|
+
const shouldIgnore = (options.exclude || [])
|
|
185
|
+
.concat([
|
|
186
|
+
"/health",
|
|
187
|
+
"/favicon.ico",
|
|
188
|
+
"/_next",
|
|
189
|
+
"/static",
|
|
190
|
+
"/public",
|
|
191
|
+
"/admin",
|
|
192
|
+
])
|
|
193
|
+
.some(function (p) {
|
|
194
|
+
return path.startsWith(p);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const isApiPath = path.startsWith(options.apiPrefix || "/api");
|
|
198
|
+
|
|
199
|
+
if (!shouldIgnore && isApiPath) {
|
|
200
|
+
const normalizedPath = normalizePath(path);
|
|
201
|
+
const endpointKey = method + ":" + normalizedPath;
|
|
202
|
+
|
|
203
|
+
if (!reportedEndpoints.has(endpointKey)) {
|
|
204
|
+
reportedEndpoints.add(endpointKey);
|
|
205
|
+
|
|
206
|
+
// Skip file uploads — too large and not JSON
|
|
207
|
+
const contentType = req.headers["content-type"] || "";
|
|
208
|
+
if (contentType.includes("multipart/form-data")) {
|
|
209
|
+
client
|
|
210
|
+
.updateEndpoint({
|
|
211
|
+
method: method,
|
|
212
|
+
path: normalizedPath,
|
|
213
|
+
requestBody: null,
|
|
214
|
+
detectedBy: "runtime",
|
|
215
|
+
})
|
|
216
|
+
.catch(function () {});
|
|
217
|
+
return originalEmit.apply(this, arguments);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Helper to send body structure to platform
|
|
221
|
+
function reportBody(bodyObj) {
|
|
222
|
+
try {
|
|
223
|
+
let parsedBody = null;
|
|
224
|
+
const structure = buildBodyStructure(bodyObj);
|
|
225
|
+
if (structure) {
|
|
226
|
+
parsedBody = {
|
|
227
|
+
type: "object",
|
|
228
|
+
properties: Object.fromEntries(
|
|
229
|
+
Object.entries(structure).map(([key, type]) => [
|
|
230
|
+
key,
|
|
231
|
+
{
|
|
232
|
+
type:
|
|
233
|
+
type === "null" || type === "[redacted]"
|
|
234
|
+
? "string"
|
|
235
|
+
: type,
|
|
236
|
+
},
|
|
237
|
+
]),
|
|
238
|
+
),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
client
|
|
242
|
+
.updateEndpoint({
|
|
243
|
+
method: method,
|
|
244
|
+
path: normalizedPath,
|
|
245
|
+
requestBody: parsedBody,
|
|
246
|
+
detectedBy: "runtime",
|
|
247
|
+
})
|
|
248
|
+
.catch(function () {});
|
|
249
|
+
} catch (e) {}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// APP ROUTER — req.body is a Web Stream, read it early
|
|
253
|
+
// and put it back as a new stream so App Router can still read it
|
|
254
|
+
if (
|
|
255
|
+
typeof req.body !== "undefined" &&
|
|
256
|
+
req.body &&
|
|
257
|
+
typeof req.body.getReader === "function"
|
|
258
|
+
) {
|
|
259
|
+
try {
|
|
260
|
+
const reader = req.body.getReader();
|
|
261
|
+
const chunks = [];
|
|
262
|
+
function readChunk() {
|
|
263
|
+
reader
|
|
264
|
+
.read()
|
|
265
|
+
.then(function (result) {
|
|
266
|
+
if (result.done) {
|
|
267
|
+
const fullBuffer = Buffer.concat(
|
|
268
|
+
chunks.map(function (c) {
|
|
269
|
+
return Buffer.from(c);
|
|
270
|
+
}),
|
|
271
|
+
);
|
|
272
|
+
// Put the body back as a new ReadableStream
|
|
273
|
+
const { ReadableStream } = require("stream/web");
|
|
274
|
+
req.body = new ReadableStream({
|
|
275
|
+
start: function (controller) {
|
|
276
|
+
controller.enqueue(fullBuffer);
|
|
277
|
+
controller.close();
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
// Parse and report
|
|
281
|
+
try {
|
|
282
|
+
const parsed = JSON.parse(fullBuffer.toString());
|
|
283
|
+
reportBody(parsed);
|
|
284
|
+
} catch (e) {}
|
|
285
|
+
} else {
|
|
286
|
+
chunks.push(result.value);
|
|
287
|
+
readChunk();
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
.catch(function () {});
|
|
291
|
+
}
|
|
292
|
+
readChunk();
|
|
293
|
+
} catch (e) {}
|
|
294
|
+
return originalEmit.apply(this, arguments);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// PAGES ROUTER — req.body is populated by Next.js after parsing
|
|
298
|
+
// We intercept res.end because by then req.body is fully available
|
|
299
|
+
const originalResEnd = res.end.bind(res);
|
|
300
|
+
res.end = function (chunk, encoding, callback) {
|
|
301
|
+
reportBody(req.body || null);
|
|
302
|
+
return originalResEnd(chunk, encoding, callback);
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return originalEmit.apply(this, arguments);
|
|
309
|
+
};
|
|
310
|
+
} catch (err) {}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Attaches a Fastify hook that silently intercepts every request
|
|
315
|
+
* and reports new endpoints to BotVersion platform.
|
|
316
|
+
*/
|
|
317
|
+
function attachFastifyInterceptor(fastify, client, options) {
|
|
318
|
+
options = options || {};
|
|
319
|
+
|
|
320
|
+
const ignorePaths = [
|
|
321
|
+
"/health",
|
|
322
|
+
"/favicon.ico",
|
|
323
|
+
"/_next",
|
|
324
|
+
"/static",
|
|
325
|
+
"/public",
|
|
326
|
+
"/admin",
|
|
327
|
+
].concat(options.exclude || []);
|
|
328
|
+
|
|
329
|
+
fastify.addHook("onRequest", async function (request, reply) {
|
|
330
|
+
const path = request.url ? request.url.split("?")[0] : "";
|
|
331
|
+
const method = request.method ? request.method.toUpperCase() : "";
|
|
332
|
+
|
|
333
|
+
const shouldIgnore = ignorePaths.some(function (p) {
|
|
334
|
+
return path.startsWith(p);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
if (shouldIgnore) return;
|
|
338
|
+
|
|
339
|
+
if (options.apiPrefix && !path.startsWith(options.apiPrefix)) return;
|
|
340
|
+
|
|
341
|
+
const normalizedPath = normalizePath(path);
|
|
342
|
+
const endpointKey = method + ":" + normalizedPath;
|
|
343
|
+
|
|
344
|
+
if (!reportedEndpoints.has(endpointKey)) {
|
|
345
|
+
reportedEndpoints.add(endpointKey);
|
|
346
|
+
|
|
347
|
+
setImmediate(function () {
|
|
348
|
+
client
|
|
349
|
+
.updateEndpoint({
|
|
350
|
+
method,
|
|
351
|
+
path: normalizedPath,
|
|
352
|
+
requestBody: null,
|
|
353
|
+
detectedBy: "runtime-fastify",
|
|
354
|
+
})
|
|
355
|
+
.catch(function () {});
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Use onSend hook to capture body after parsing
|
|
361
|
+
fastify.addHook("preHandler", async function (request, reply) {
|
|
362
|
+
const path = request.url ? request.url.split("?")[0] : "";
|
|
363
|
+
const method = request.method ? request.method.toUpperCase() : "";
|
|
364
|
+
const normalizedPath = normalizePath(path);
|
|
365
|
+
const bodyKey = method + ":" + normalizedPath + ":body";
|
|
366
|
+
|
|
367
|
+
if (reportedEndpoints.has(bodyKey)) return;
|
|
368
|
+
reportedEndpoints.add(bodyKey);
|
|
369
|
+
|
|
370
|
+
const body = request.body;
|
|
371
|
+
if (!body) return;
|
|
372
|
+
|
|
373
|
+
const bodyStructure = buildBodyStructure(body);
|
|
374
|
+
if (!bodyStructure) return;
|
|
375
|
+
|
|
376
|
+
const jsonSchema = {
|
|
377
|
+
type: "object",
|
|
378
|
+
properties: Object.fromEntries(
|
|
379
|
+
Object.entries(bodyStructure).map(function ([key, type]) {
|
|
380
|
+
return [
|
|
381
|
+
key,
|
|
382
|
+
{
|
|
383
|
+
type: type === "null" || type === "[redacted]" ? "string" : type,
|
|
384
|
+
},
|
|
385
|
+
];
|
|
386
|
+
}),
|
|
387
|
+
),
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
setImmediate(function () {
|
|
391
|
+
client
|
|
392
|
+
.updateEndpoint({
|
|
393
|
+
method,
|
|
394
|
+
path: normalizedPath,
|
|
395
|
+
requestBody: jsonSchema,
|
|
396
|
+
detectedBy: "runtime-fastify",
|
|
397
|
+
})
|
|
398
|
+
.catch(function () {});
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Attaches a Koa middleware that silently intercepts every request
|
|
405
|
+
* and reports new endpoints to BotVersion platform.
|
|
406
|
+
*/
|
|
407
|
+
function attachKoaInterceptor(app, client, options) {
|
|
408
|
+
options = options || {};
|
|
409
|
+
|
|
410
|
+
const ignorePaths = [
|
|
411
|
+
"/health",
|
|
412
|
+
"/favicon.ico",
|
|
413
|
+
"/_next",
|
|
414
|
+
"/static",
|
|
415
|
+
"/public",
|
|
416
|
+
"/admin",
|
|
417
|
+
].concat(options.exclude || []);
|
|
418
|
+
|
|
419
|
+
// IMPORTANT: This interceptor must be added AFTER koa-bodyparser middleware
|
|
420
|
+
// so that ctx.request.body is already populated when we read it.
|
|
421
|
+
app.use(async function botVersionKoaInterceptor(ctx, next) {
|
|
422
|
+
await next();
|
|
423
|
+
|
|
424
|
+
const path = ctx.path || "";
|
|
425
|
+
const method = ctx.method ? ctx.method.toUpperCase() : "";
|
|
426
|
+
|
|
427
|
+
const shouldIgnore = ignorePaths.some(function (p) {
|
|
428
|
+
return path.startsWith(p);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
if (shouldIgnore) return;
|
|
432
|
+
|
|
433
|
+
if (options.apiPrefix && !path.startsWith(options.apiPrefix)) return;
|
|
434
|
+
|
|
435
|
+
const normalizedPath = normalizePath(path);
|
|
436
|
+
const endpointKey = method + ":" + normalizedPath;
|
|
437
|
+
|
|
438
|
+
const body = ctx.request.body;
|
|
439
|
+
const bodyStructure = buildBodyStructure(body);
|
|
440
|
+
const bodyKey =
|
|
441
|
+
endpointKey +
|
|
442
|
+
":" +
|
|
443
|
+
Object.keys(bodyStructure || {})
|
|
444
|
+
.sort()
|
|
445
|
+
.join(",");
|
|
446
|
+
|
|
447
|
+
if (!reportedEndpoints.has(bodyKey)) {
|
|
448
|
+
reportedEndpoints.add(bodyKey);
|
|
449
|
+
|
|
450
|
+
const jsonSchema = bodyStructure
|
|
451
|
+
? {
|
|
452
|
+
type: "object",
|
|
453
|
+
properties: Object.fromEntries(
|
|
454
|
+
Object.entries(bodyStructure).map(function ([key, type]) {
|
|
455
|
+
return [
|
|
456
|
+
key,
|
|
457
|
+
{
|
|
458
|
+
type:
|
|
459
|
+
type === "null" || type === "[redacted]"
|
|
460
|
+
? "string"
|
|
461
|
+
: type,
|
|
462
|
+
},
|
|
463
|
+
];
|
|
464
|
+
}),
|
|
465
|
+
),
|
|
466
|
+
}
|
|
467
|
+
: null;
|
|
468
|
+
|
|
469
|
+
setImmediate(function () {
|
|
470
|
+
client
|
|
471
|
+
.updateEndpoint({
|
|
472
|
+
method,
|
|
473
|
+
path: normalizedPath,
|
|
474
|
+
requestBody: jsonSchema,
|
|
475
|
+
detectedBy: "runtime-koa",
|
|
476
|
+
})
|
|
477
|
+
.catch(function () {});
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Attaches a Hapi lifecycle extension that silently intercepts every request
|
|
485
|
+
* and reports new endpoints to BotVersion platform.
|
|
486
|
+
*/
|
|
487
|
+
function attachHapiInterceptor(server, client, options) {
|
|
488
|
+
options = options || {};
|
|
489
|
+
|
|
490
|
+
const ignorePaths = [
|
|
491
|
+
"/health",
|
|
492
|
+
"/favicon.ico",
|
|
493
|
+
"/_next",
|
|
494
|
+
"/static",
|
|
495
|
+
"/public",
|
|
496
|
+
"/admin",
|
|
497
|
+
].concat(options.exclude || []);
|
|
498
|
+
|
|
499
|
+
server.ext("onPostAuth", function (request, h) {
|
|
500
|
+
const path = request.path || "";
|
|
501
|
+
const method = request.method ? request.method.toUpperCase() : "";
|
|
502
|
+
|
|
503
|
+
const shouldIgnore = ignorePaths.some(function (p) {
|
|
504
|
+
return path.startsWith(p);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (shouldIgnore) return h.continue;
|
|
508
|
+
|
|
509
|
+
if (options.apiPrefix && !path.startsWith(options.apiPrefix))
|
|
510
|
+
return h.continue;
|
|
511
|
+
|
|
512
|
+
const normalizedPath = normalizePath(path);
|
|
513
|
+
const endpointKey = method + ":" + normalizedPath;
|
|
514
|
+
|
|
515
|
+
const body = request.payload;
|
|
516
|
+
const bodyStructure = buildBodyStructure(body);
|
|
517
|
+
const bodyKey =
|
|
518
|
+
endpointKey +
|
|
519
|
+
":" +
|
|
520
|
+
Object.keys(bodyStructure || {})
|
|
521
|
+
.sort()
|
|
522
|
+
.join(",");
|
|
523
|
+
|
|
524
|
+
if (!reportedEndpoints.has(bodyKey)) {
|
|
525
|
+
reportedEndpoints.add(bodyKey);
|
|
526
|
+
|
|
527
|
+
const jsonSchema = bodyStructure
|
|
528
|
+
? {
|
|
529
|
+
type: "object",
|
|
530
|
+
properties: Object.fromEntries(
|
|
531
|
+
Object.entries(bodyStructure).map(function ([key, type]) {
|
|
532
|
+
return [
|
|
533
|
+
key,
|
|
534
|
+
{
|
|
535
|
+
type:
|
|
536
|
+
type === "null" || type === "[redacted]"
|
|
537
|
+
? "string"
|
|
538
|
+
: type,
|
|
539
|
+
},
|
|
540
|
+
];
|
|
541
|
+
}),
|
|
542
|
+
),
|
|
543
|
+
}
|
|
544
|
+
: null;
|
|
545
|
+
|
|
546
|
+
setImmediate(function () {
|
|
547
|
+
client
|
|
548
|
+
.updateEndpoint({
|
|
549
|
+
method,
|
|
550
|
+
path: normalizedPath,
|
|
551
|
+
requestBody: jsonSchema,
|
|
552
|
+
detectedBy: "runtime-hapi",
|
|
553
|
+
})
|
|
554
|
+
.catch(function () {});
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return h.continue;
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Attaches a NestJS interceptor by patching the underlying http server.
|
|
564
|
+
* NestJS runs on top of Express or Fastify under the hood so we can
|
|
565
|
+
* patch the Node http server the same way we do for Next.js.
|
|
566
|
+
*/
|
|
567
|
+
function attachNestJsInterceptor(client, options) {
|
|
568
|
+
// NestJS runs on Node http server under the hood
|
|
569
|
+
// So we reuse the same approach as Next.js
|
|
570
|
+
attachNextJsInterceptor(client, {
|
|
571
|
+
exclude: (options || {}).exclude || [],
|
|
572
|
+
apiPrefix: (options || {}).apiPrefix || "/",
|
|
573
|
+
debug: (options || {}).debug || false,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* SvelteKit, Nuxt and Remix all run on Node http server
|
|
579
|
+
* so we reuse the Next.js interceptor approach for all of them.
|
|
580
|
+
*/
|
|
581
|
+
function attachSvelteKitInterceptor(client, options) {
|
|
582
|
+
attachNextJsInterceptor(client, {
|
|
583
|
+
exclude: (options || {}).exclude || [],
|
|
584
|
+
apiPrefix: (options || {}).apiPrefix || "/",
|
|
585
|
+
debug: (options || {}).debug || false,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function attachNuxtInterceptor(client, options) {
|
|
590
|
+
attachNextJsInterceptor(client, {
|
|
591
|
+
exclude: (options || {}).exclude || [],
|
|
592
|
+
apiPrefix: (options || {}).apiPrefix || "/api",
|
|
593
|
+
debug: (options || {}).debug || false,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function attachRemixInterceptor(client, options) {
|
|
598
|
+
attachNextJsInterceptor(client, {
|
|
599
|
+
exclude: (options || {}).exclude || [],
|
|
600
|
+
apiPrefix: (options || {}).apiPrefix || "/",
|
|
601
|
+
debug: (options || {}).debug || false,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function attachAdonisInterceptor(client, options) {
|
|
606
|
+
attachNextJsInterceptor(client, {
|
|
607
|
+
exclude: (options || {}).exclude || [],
|
|
608
|
+
apiPrefix: (options || {}).apiPrefix || "/",
|
|
609
|
+
debug: (options || {}).debug || false,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
289
613
|
module.exports = {
|
|
290
614
|
attachInterceptor,
|
|
615
|
+
attachNextJsInterceptor,
|
|
616
|
+
attachFastifyInterceptor,
|
|
617
|
+
attachKoaInterceptor,
|
|
618
|
+
attachHapiInterceptor,
|
|
619
|
+
attachNestJsInterceptor,
|
|
620
|
+
attachSvelteKitInterceptor,
|
|
621
|
+
attachNuxtInterceptor,
|
|
622
|
+
attachRemixInterceptor,
|
|
623
|
+
attachAdonisInterceptor,
|
|
291
624
|
normalizePath,
|
|
292
625
|
buildBodyStructure,
|
|
293
626
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botversion-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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
|
-
"
|
|
20
|
-
"
|
|
19
|
+
"@babel/parser": "^7.29.3",
|
|
20
|
+
"@babel/traverse": "^7.29.0",
|
|
21
|
+
"express": "^4.18.2"
|
|
21
22
|
}
|
|
22
23
|
}
|