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