@shieldpress/tracker 1.0.0 → 1.1.0
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/dist/express.d.mts +1 -1
- package/dist/express.d.ts +1 -1
- package/dist/express.js +89 -4
- package/dist/express.js.map +1 -1
- package/dist/express.mjs +89 -4
- package/dist/express.mjs.map +1 -1
- package/dist/index.d.mts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +89 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +89 -4
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs.d.mts +1 -1
- package/dist/nextjs.d.ts +1 -1
- package/dist/nextjs.js +89 -4
- package/dist/nextjs.js.map +1 -1
- package/dist/nextjs.mjs +89 -4
- package/dist/nextjs.mjs.map +1 -1
- package/dist/{tracker-BJBniOSu.d.mts → tracker-BXvPYi1R.d.mts} +7 -0
- package/dist/{tracker-BJBniOSu.d.ts → tracker-BXvPYi1R.d.ts} +7 -0
- package/package.json +1 -1
package/dist/express.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { T as TrackerConfig, S as ShieldPressTracker } from './tracker-
|
|
2
|
+
import { T as TrackerConfig, S as ShieldPressTracker } from './tracker-BXvPYi1R.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Express middleware that tracks HTTP requests, errors, and security threats.
|
package/dist/express.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { T as TrackerConfig, S as ShieldPressTracker } from './tracker-
|
|
2
|
+
import { T as TrackerConfig, S as ShieldPressTracker } from './tracker-BXvPYi1R.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Express middleware that tracks HTTP requests, errors, and security threats.
|
package/dist/express.js
CHANGED
|
@@ -35,6 +35,10 @@ __export(express_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(express_exports);
|
|
37
37
|
|
|
38
|
+
// src/tracker.ts
|
|
39
|
+
var import_http2 = __toESM(require("http"));
|
|
40
|
+
var import_https2 = __toESM(require("https"));
|
|
41
|
+
|
|
38
42
|
// src/config.ts
|
|
39
43
|
var DEFAULT_API_URL = "https://api.shieldpress.net";
|
|
40
44
|
function resolveConfig(config) {
|
|
@@ -111,8 +115,8 @@ function getPublicIpCached() {
|
|
|
111
115
|
if (Date.now() - publicIpLastFetch > 6e5) {
|
|
112
116
|
publicIpLastFetch = Date.now();
|
|
113
117
|
try {
|
|
114
|
-
const
|
|
115
|
-
const req =
|
|
118
|
+
const http3 = require("https");
|
|
119
|
+
const req = http3.get("https://api.ipify.org", { timeout: 3e3 }, (res) => {
|
|
116
120
|
let data = "";
|
|
117
121
|
res.on("data", (chunk) => {
|
|
118
122
|
data += chunk;
|
|
@@ -1148,23 +1152,49 @@ function sanitizePayload(payload) {
|
|
|
1148
1152
|
}
|
|
1149
1153
|
return p;
|
|
1150
1154
|
}
|
|
1151
|
-
var
|
|
1155
|
+
var _Reporter = class _Reporter {
|
|
1152
1156
|
constructor(apiUrl, apiKey, logger) {
|
|
1153
1157
|
this.queue = [];
|
|
1154
1158
|
this.sending = false;
|
|
1159
|
+
this.consecutiveFailures = 0;
|
|
1160
|
+
this.backoffUntil = 0;
|
|
1155
1161
|
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
1156
1162
|
this.apiKey = apiKey;
|
|
1157
1163
|
this.logger = logger;
|
|
1158
1164
|
}
|
|
1159
1165
|
async send(payload) {
|
|
1160
1166
|
try {
|
|
1167
|
+
if (this.queue.length >= _Reporter.MAX_QUEUE_SIZE) {
|
|
1168
|
+
const dropped = this.queue.length - _Reporter.MAX_QUEUE_SIZE + 1;
|
|
1169
|
+
this.queue.splice(0, dropped);
|
|
1170
|
+
this.logger.warn(`Queue full, dropped ${dropped} oldest report(s)`);
|
|
1171
|
+
}
|
|
1161
1172
|
this.queue.push(payload);
|
|
1162
1173
|
if (this.sending) return true;
|
|
1174
|
+
const now = Date.now();
|
|
1175
|
+
if (now < this.backoffUntil) {
|
|
1176
|
+
this.logger.info(`Backoff active, will retry in ${Math.round((this.backoffUntil - now) / 1e3)}s`);
|
|
1177
|
+
return true;
|
|
1178
|
+
}
|
|
1163
1179
|
this.sending = true;
|
|
1164
1180
|
try {
|
|
1165
1181
|
while (this.queue.length > 0) {
|
|
1182
|
+
if (Date.now() < this.backoffUntil) break;
|
|
1166
1183
|
const batch = this.queue.splice(0, 10);
|
|
1167
|
-
|
|
1184
|
+
try {
|
|
1185
|
+
await this.transmit(batch);
|
|
1186
|
+
this.consecutiveFailures = 0;
|
|
1187
|
+
this.backoffUntil = 0;
|
|
1188
|
+
} catch (err) {
|
|
1189
|
+
this.queue.unshift(...batch);
|
|
1190
|
+
this.consecutiveFailures++;
|
|
1191
|
+
const delay = Math.min(1e3 * Math.pow(2, this.consecutiveFailures - 1), _Reporter.MAX_BACKOFF_MS);
|
|
1192
|
+
this.backoffUntil = Date.now() + delay;
|
|
1193
|
+
this.logger.warn(
|
|
1194
|
+
`Transmit failed (attempt ${this.consecutiveFailures}), backing off ${Math.round(delay / 1e3)}s: ${err instanceof Error ? err.message : err}`
|
|
1195
|
+
);
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1168
1198
|
}
|
|
1169
1199
|
return true;
|
|
1170
1200
|
} finally {
|
|
@@ -1187,6 +1217,8 @@ var Reporter = class {
|
|
|
1187
1217
|
`Payload too large (${body.length} bytes), truncating arrays (key=${maskApiKey(this.apiKey)})`
|
|
1188
1218
|
);
|
|
1189
1219
|
for (const p of sanitized) {
|
|
1220
|
+
p._truncated = true;
|
|
1221
|
+
p._originalSize = body.length;
|
|
1190
1222
|
if (p.security) {
|
|
1191
1223
|
p.security.events = p.security.events.slice(0, 10);
|
|
1192
1224
|
}
|
|
@@ -1254,6 +1286,11 @@ var Reporter = class {
|
|
|
1254
1286
|
});
|
|
1255
1287
|
}
|
|
1256
1288
|
};
|
|
1289
|
+
/** Max queued reports before dropping oldest */
|
|
1290
|
+
_Reporter.MAX_QUEUE_SIZE = 100;
|
|
1291
|
+
/** Max backoff delay in ms (60 seconds) */
|
|
1292
|
+
_Reporter.MAX_BACKOFF_MS = 6e4;
|
|
1293
|
+
var Reporter = _Reporter;
|
|
1257
1294
|
|
|
1258
1295
|
// src/tracker.ts
|
|
1259
1296
|
var ShieldPressTracker = class {
|
|
@@ -1262,6 +1299,7 @@ var ShieldPressTracker = class {
|
|
|
1262
1299
|
this.lagTimer = null;
|
|
1263
1300
|
this.started = false;
|
|
1264
1301
|
this.headersChecked = false;
|
|
1302
|
+
this.autoHooked = false;
|
|
1265
1303
|
this.config = resolveConfig(userConfig);
|
|
1266
1304
|
this.logger = createLogger(this.config.debug);
|
|
1267
1305
|
this.reporter = new Reporter(this.config.apiUrl, this.config.apiKey, this.logger);
|
|
@@ -1289,6 +1327,9 @@ var ShieldPressTracker = class {
|
|
|
1289
1327
|
this.errorCollector.capture(err, { type: "unhandledRejection" });
|
|
1290
1328
|
});
|
|
1291
1329
|
}
|
|
1330
|
+
if (this.config.httpTracking) {
|
|
1331
|
+
this.autoHookHttpServers();
|
|
1332
|
+
}
|
|
1292
1333
|
if (this.config.systemMetrics) {
|
|
1293
1334
|
collectSystemMetrics();
|
|
1294
1335
|
}
|
|
@@ -1408,6 +1449,50 @@ var ShieldPressTracker = class {
|
|
|
1408
1449
|
}
|
|
1409
1450
|
return this.reporter.send(payload);
|
|
1410
1451
|
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Monkey-patch http.Server and https.Server so every incoming request
|
|
1454
|
+
* is automatically tracked without requiring the user to install middleware.
|
|
1455
|
+
* Safe to call multiple times — patches only once.
|
|
1456
|
+
*/
|
|
1457
|
+
autoHookHttpServers() {
|
|
1458
|
+
if (this.autoHooked) return;
|
|
1459
|
+
this.autoHooked = true;
|
|
1460
|
+
const tracker = this;
|
|
1461
|
+
const patchEmit = (ServerProto) => {
|
|
1462
|
+
const originalEmit = ServerProto.emit;
|
|
1463
|
+
ServerProto.emit = function(event, ...args) {
|
|
1464
|
+
if (event === "request") {
|
|
1465
|
+
const req = args[0];
|
|
1466
|
+
const res = args[1];
|
|
1467
|
+
const start = process.hrtime.bigint();
|
|
1468
|
+
const url = req.url || "/";
|
|
1469
|
+
res.on("finish", () => {
|
|
1470
|
+
try {
|
|
1471
|
+
const durationMs = Number(process.hrtime.bigint() - start) / 1e6;
|
|
1472
|
+
const parsedPath = url.split("?")[0] || "/";
|
|
1473
|
+
tracker.recordRequest({
|
|
1474
|
+
path: parsedPath,
|
|
1475
|
+
method: req.method || "GET",
|
|
1476
|
+
statusCode: res.statusCode,
|
|
1477
|
+
durationMs: Math.round(durationMs * 100) / 100
|
|
1478
|
+
});
|
|
1479
|
+
} catch {
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
return originalEmit.apply(this, [event, ...args]);
|
|
1484
|
+
};
|
|
1485
|
+
};
|
|
1486
|
+
try {
|
|
1487
|
+
patchEmit(import_http2.default.Server.prototype);
|
|
1488
|
+
patchEmit(import_https2.default.Server.prototype);
|
|
1489
|
+
this.logger.info("Auto-hooked http/https servers for request tracking");
|
|
1490
|
+
} catch (err) {
|
|
1491
|
+
this.logger.warn(
|
|
1492
|
+
`Failed to auto-hook HTTP servers: ${err instanceof Error ? err.message : err}`
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1411
1496
|
shouldIgnorePath(path) {
|
|
1412
1497
|
return this.config.ignorePaths.some((pattern) => {
|
|
1413
1498
|
if (pattern.endsWith("/*")) {
|