@zintrust/trace 1.2.0 → 1.5.2
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/README.md
CHANGED
|
@@ -39,6 +39,9 @@ TRACE_PROXY_PATH=/zin/trace/write
|
|
|
39
39
|
TRACE_PROXY_KEY_ID= # optional — falls back to APP_NAME
|
|
40
40
|
TRACE_PROXY_SECRET= # optional — falls back to APP_KEY
|
|
41
41
|
TRACE_PROXY_TIMEOUT_MS=30000
|
|
42
|
+
TRACE_PROXY_MIDDLEWARE= # optional — comma-separated route middleware applied on the receiver
|
|
43
|
+
TRACE_PROXY_RATE_LIMIT_MAX=0 # optional — when > 0, adds rateLimit:<max>:<window> automatically
|
|
44
|
+
TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES=0
|
|
42
45
|
TRACE_PRUNE_HOURS=24 # how long entries are kept (default: 24)
|
|
43
46
|
TRACE_SLOW_QUERY_MS=100 # slow-query threshold in ms (default: 100)
|
|
44
47
|
TRACE_LOG_LEVEL=info # minimum log level captured (default: info)
|
|
@@ -61,6 +64,8 @@ When `TRACE_CONTENT_QUEUE_DRIVER` is set, trace writes enqueue through that regi
|
|
|
61
64
|
|
|
62
65
|
When `TRACE_PROXY=true`, the local runtime keeps collecting the same trace payload it would normally send to storage, but it sends the write/update/stale-family operations to `TRACE_PROXY_URL + TRACE_PROXY_PATH` instead of writing directly to the local trace database. The receiver can then persist those entries with the standard `TraceStorage` flow.
|
|
63
66
|
|
|
67
|
+
On the receiver, use `TRACE_PROXY_MIDDLEWARE` for any gateway middleware such as `auth,admin`. If you want a dedicated ingest rate limit without encoding `rateLimit:<max>:<window>` by hand, set `TRACE_PROXY_RATE_LIMIT_MAX` and `TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES`; the gateway appends that parameterized rate-limit middleware automatically.
|
|
68
|
+
|
|
64
69
|
This currently works with any queue driver already registered in ZinTrust. First-class Cloudflare Queue support still requires a dedicated queue driver and queue-runtime registration for that transport.
|
|
65
70
|
|
|
66
71
|
### 2. Enable the plugin in `zintrust.plugins.*`
|
package/dist/build-manifest.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/trace",
|
|
3
|
-
"version": "1.2
|
|
4
|
-
"buildDate": "2026-04-
|
|
3
|
+
"version": "1.5.2",
|
|
4
|
+
"buildDate": "2026-04-25T12:38:43.860Z",
|
|
5
5
|
"buildEnvironment": {
|
|
6
6
|
"node": "v20.20.2",
|
|
7
7
|
"platform": "linux",
|
|
8
8
|
"arch": "x64"
|
|
9
9
|
},
|
|
10
10
|
"git": {
|
|
11
|
-
"commit": "
|
|
11
|
+
"commit": "616b0d5d",
|
|
12
12
|
"branch": "master"
|
|
13
13
|
},
|
|
14
14
|
"package": {
|
|
@@ -83,15 +83,15 @@
|
|
|
83
83
|
},
|
|
84
84
|
"index.js": {
|
|
85
85
|
"size": 3421,
|
|
86
|
-
"sha256": "
|
|
86
|
+
"sha256": "199bd3c4a0618b7b336e3b866169656a0b49c8f527b083be87d44fe092518ad6"
|
|
87
87
|
},
|
|
88
88
|
"ingest/TraceIngestGateway.d.ts": {
|
|
89
89
|
"size": 786,
|
|
90
90
|
"sha256": "3a0a46fa5bbf5367047214533ec0ee92a171ee35a60d25c197eea1eed1ff0f65"
|
|
91
91
|
},
|
|
92
92
|
"ingest/TraceIngestGateway.js": {
|
|
93
|
-
"size":
|
|
94
|
-
"sha256": "
|
|
93
|
+
"size": 10936,
|
|
94
|
+
"sha256": "c95f0e42f1294d8ae2e406a5910f8e74044c2b586e82e726876d75ddfafef27e"
|
|
95
95
|
},
|
|
96
96
|
"migrations/20260331000001_create_zin_trace_entries_table.d.ts": {
|
|
97
97
|
"size": 304,
|
|
@@ -13,6 +13,9 @@ const parseMiddleware = (value) => value
|
|
|
13
13
|
.split(',')
|
|
14
14
|
.map((entry) => entry.trim())
|
|
15
15
|
.filter((entry) => entry.length > 0);
|
|
16
|
+
const isParameterizedRateLimitMiddleware = (value) => {
|
|
17
|
+
return /^rateLimit:\d+:\d+(?:\.\d+)?$/.test(value.trim());
|
|
18
|
+
};
|
|
16
19
|
const trimTrailingSlashes = (value) => {
|
|
17
20
|
let trimmed = value;
|
|
18
21
|
while (trimmed.endsWith('/')) {
|
|
@@ -210,8 +213,46 @@ const resolveSigningWindowMs = (overrides) => {
|
|
|
210
213
|
const resolveNonceTtlMs = (overrides) => {
|
|
211
214
|
return overrides?.nonceTtlMs ?? Env.getInt('TRACE_PROXY_NONCE_TTL_MS', 120000);
|
|
212
215
|
};
|
|
216
|
+
const resolveRateLimitMax = () => {
|
|
217
|
+
return Env.getInt('TRACE_PROXY_RATE_LIMIT_MAX', 0);
|
|
218
|
+
};
|
|
219
|
+
const resolveRateLimitWindowMinutes = () => {
|
|
220
|
+
const windowMinutes = Env.getFloat('TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES', 0);
|
|
221
|
+
return Math.max(windowMinutes, 0);
|
|
222
|
+
};
|
|
223
|
+
const createRateLimitMiddleware = () => {
|
|
224
|
+
const max = resolveRateLimitMax();
|
|
225
|
+
const windowMinutes = resolveRateLimitWindowMinutes();
|
|
226
|
+
if (!Number.isFinite(max) || !Number.isInteger(max)) {
|
|
227
|
+
throw ErrorFactory.createConfigError('TRACE_PROXY_RATE_LIMIT_MAX must be a valid integer', {
|
|
228
|
+
value: max,
|
|
229
|
+
envKey: 'TRACE_PROXY_RATE_LIMIT_MAX',
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
if (!Number.isFinite(windowMinutes)) {
|
|
233
|
+
throw ErrorFactory.createConfigError('TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES must be a valid number', {
|
|
234
|
+
value: windowMinutes,
|
|
235
|
+
envKey: 'TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
if (max <= 0 || windowMinutes <= 0) {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
return `rateLimit:${max}:${windowMinutes}`;
|
|
242
|
+
};
|
|
213
243
|
const resolveMiddleware = (overrides) => {
|
|
214
|
-
|
|
244
|
+
if (overrides?.middleware !== undefined) {
|
|
245
|
+
return overrides.middleware;
|
|
246
|
+
}
|
|
247
|
+
const configured = parseMiddleware(Env.get('TRACE_PROXY_MIDDLEWARE', ''));
|
|
248
|
+
const rateLimitMiddleware = createRateLimitMiddleware();
|
|
249
|
+
if (rateLimitMiddleware === undefined) {
|
|
250
|
+
return configured;
|
|
251
|
+
}
|
|
252
|
+
return [
|
|
253
|
+
...configured.filter((entry) => !isParameterizedRateLimitMiddleware(entry)),
|
|
254
|
+
rateLimitMiddleware,
|
|
255
|
+
];
|
|
215
256
|
};
|
|
216
257
|
const readSettings = (overrides) => {
|
|
217
258
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/trace",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "Trace assistant for ZinTrust: logs requests, queries, exceptions, jobs, and more.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=20.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@zintrust/core": "
|
|
43
|
+
"@zintrust/core": "*"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
|
@@ -56,6 +56,10 @@ const parseMiddleware = (value: string): ReadonlyArray<string> =>
|
|
|
56
56
|
.map((entry) => entry.trim())
|
|
57
57
|
.filter((entry) => entry.length > 0);
|
|
58
58
|
|
|
59
|
+
const isParameterizedRateLimitMiddleware = (value: string): boolean => {
|
|
60
|
+
return /^rateLimit:\d+:\d+(?:\.\d+)?$/.test(value.trim());
|
|
61
|
+
};
|
|
62
|
+
|
|
59
63
|
const trimTrailingSlashes = (value: string): string => {
|
|
60
64
|
let trimmed = value;
|
|
61
65
|
while (trimmed.endsWith('/')) {
|
|
@@ -295,8 +299,58 @@ const resolveNonceTtlMs = (overrides?: TraceIngestGatewayOverrides): number => {
|
|
|
295
299
|
return overrides?.nonceTtlMs ?? Env.getInt('TRACE_PROXY_NONCE_TTL_MS', 120000);
|
|
296
300
|
};
|
|
297
301
|
|
|
302
|
+
const resolveRateLimitMax = (): number => {
|
|
303
|
+
return Env.getInt('TRACE_PROXY_RATE_LIMIT_MAX', 0);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const resolveRateLimitWindowMinutes = (): number => {
|
|
307
|
+
const windowMinutes = Env.getFloat('TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES', 0);
|
|
308
|
+
return Math.max(windowMinutes, 0);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const createRateLimitMiddleware = (): string | undefined => {
|
|
312
|
+
const max = resolveRateLimitMax();
|
|
313
|
+
const windowMinutes = resolveRateLimitWindowMinutes();
|
|
314
|
+
|
|
315
|
+
if (!Number.isFinite(max) || !Number.isInteger(max)) {
|
|
316
|
+
throw ErrorFactory.createConfigError('TRACE_PROXY_RATE_LIMIT_MAX must be a valid integer', {
|
|
317
|
+
value: max,
|
|
318
|
+
envKey: 'TRACE_PROXY_RATE_LIMIT_MAX',
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!Number.isFinite(windowMinutes)) {
|
|
323
|
+
throw ErrorFactory.createConfigError(
|
|
324
|
+
'TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES must be a valid number',
|
|
325
|
+
{
|
|
326
|
+
value: windowMinutes,
|
|
327
|
+
envKey: 'TRACE_PROXY_RATE_LIMIT_WINDOW_MINUTES',
|
|
328
|
+
}
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (max <= 0 || windowMinutes <= 0) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return `rateLimit:${max}:${windowMinutes}`;
|
|
337
|
+
};
|
|
338
|
+
|
|
298
339
|
const resolveMiddleware = (overrides?: TraceIngestGatewayOverrides): ReadonlyArray<string> => {
|
|
299
|
-
|
|
340
|
+
if (overrides?.middleware !== undefined) {
|
|
341
|
+
return overrides.middleware;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const configured = parseMiddleware(Env.get('TRACE_PROXY_MIDDLEWARE', ''));
|
|
345
|
+
const rateLimitMiddleware = createRateLimitMiddleware();
|
|
346
|
+
if (rateLimitMiddleware === undefined) {
|
|
347
|
+
return configured;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return [
|
|
351
|
+
...configured.filter((entry) => !isParameterizedRateLimitMiddleware(entry)),
|
|
352
|
+
rateLimitMiddleware,
|
|
353
|
+
];
|
|
300
354
|
};
|
|
301
355
|
|
|
302
356
|
const readSettings = (overrides?: TraceIngestGatewayOverrides): TraceIngestGatewaySettings => {
|