@zintrust/trace 1.6.6 → 1.7.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/build-manifest.json +10 -22
- package/package.json +3 -4
- package/dist/storage/DebuggerStorage.d.ts +0 -13
- package/dist/storage/DebuggerStorage.js +0 -195
- package/src/TraceConnection.ts +0 -182
- package/src/cli-register.ts +0 -63
- package/src/config.ts +0 -383
- package/src/context.ts +0 -101
- package/src/dashboard/handlers.ts +0 -353
- package/src/dashboard/routes.ts +0 -114
- package/src/dashboard/ui.ts +0 -1262
- package/src/dashboard/zintrust-debuger.svg +0 -30
- package/src/index.ts +0 -102
- package/src/ingest/TraceIngestGateway.ts +0 -414
- package/src/plugin.ts +0 -9
- package/src/register.ts +0 -702
- package/src/storage/ProxyTraceStorage.ts +0 -190
- package/src/storage/TraceContentBudget.ts +0 -493
- package/src/storage/TraceContentRedaction.ts +0 -44
- package/src/storage/TraceEntryFiltering.ts +0 -50
- package/src/storage/TraceServiceTag.ts +0 -56
- package/src/storage/TraceStorage.ts +0 -543
- package/src/storage/TraceWriteDiagnostics.ts +0 -289
- package/src/storage/index.ts +0 -4
- package/src/types.ts +0 -430
- package/src/ui.ts +0 -9
- package/src/utils/authTag.ts +0 -20
- package/src/utils/entryFilter.ts +0 -131
- package/src/utils/familyHash.ts +0 -8
- package/src/utils/redact.ts +0 -112
- package/src/utils/requestFilter.ts +0 -79
- package/src/utils/stackFrame.ts +0 -44
- package/src/watchers/AuthWatcher.ts +0 -53
- package/src/watchers/BatchWatcher.ts +0 -55
- package/src/watchers/CacheWatcher.ts +0 -72
- package/src/watchers/CommandWatcher.ts +0 -58
- package/src/watchers/DumpWatcher.ts +0 -45
- package/src/watchers/EventWatcher.ts +0 -46
- package/src/watchers/ExceptionWatcher.ts +0 -130
- package/src/watchers/GateWatcher.ts +0 -53
- package/src/watchers/HttpClientWatcher.ts +0 -219
- package/src/watchers/HttpWatcher.ts +0 -249
- package/src/watchers/JobWatcher.ts +0 -124
- package/src/watchers/LogWatcher.ts +0 -120
- package/src/watchers/MailWatcher.ts +0 -65
- package/src/watchers/MiddlewareWatcher.ts +0 -54
- package/src/watchers/ModelWatcher.ts +0 -60
- package/src/watchers/NotificationWatcher.ts +0 -60
- package/src/watchers/QueryWatcher.ts +0 -105
- package/src/watchers/RedisWatcher.ts +0 -42
- package/src/watchers/ScheduleWatcher.ts +0 -57
- package/src/watchers/ViewWatcher.ts +0 -40
package/src/config.ts
DELETED
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TraceConfig — defaults and merge helper for @zintrust/trace
|
|
3
|
-
*/
|
|
4
|
-
import type {
|
|
5
|
-
ITraceConfig,
|
|
6
|
-
TraceClientRequestCaptureRule,
|
|
7
|
-
TraceClientRequestWatcherToggle,
|
|
8
|
-
TraceConfigOverrides,
|
|
9
|
-
TraceContentDispatchConfig,
|
|
10
|
-
TraceFilterRule,
|
|
11
|
-
TraceProxyConfig,
|
|
12
|
-
TraceRequestWatcherConfig,
|
|
13
|
-
TraceWatcherToggle,
|
|
14
|
-
} from './types';
|
|
15
|
-
|
|
16
|
-
const mergeStringLists = (base: string[], override?: string[]): string[] => {
|
|
17
|
-
const merged = new Set<string>();
|
|
18
|
-
|
|
19
|
-
for (const value of [...base, ...(override ?? [])]) {
|
|
20
|
-
if (typeof value !== 'string') continue;
|
|
21
|
-
const normalized = value.trim();
|
|
22
|
-
if (normalized !== '') merged.add(normalized);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return [...merged];
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const isObjectValue = (value: unknown): value is Record<string, unknown> => {
|
|
29
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const resolveEnabled = (
|
|
33
|
-
base?: TraceFilterRule,
|
|
34
|
-
override?: TraceFilterRule
|
|
35
|
-
): boolean | undefined => {
|
|
36
|
-
return override?.enabled ?? base?.enabled;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const hasMergedRuleValues = (
|
|
40
|
-
include: string[],
|
|
41
|
-
exclude: string[],
|
|
42
|
-
enabled: boolean | undefined
|
|
43
|
-
): boolean => {
|
|
44
|
-
return include.length > 0 || exclude.length > 0 || enabled !== undefined;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const buildFilterRule = (input: {
|
|
48
|
-
include: string[];
|
|
49
|
-
exclude: string[];
|
|
50
|
-
enabled: boolean | undefined;
|
|
51
|
-
}): TraceFilterRule => {
|
|
52
|
-
return Object.freeze({
|
|
53
|
-
...(input.enabled === undefined ? {} : { enabled: input.enabled }),
|
|
54
|
-
...(input.include.length > 0 ? { include: input.include } : {}),
|
|
55
|
-
...(input.exclude.length > 0 ? { exclude: input.exclude } : {}),
|
|
56
|
-
});
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const mergeFilterRule = (
|
|
60
|
-
base?: TraceFilterRule,
|
|
61
|
-
override?: TraceFilterRule
|
|
62
|
-
): TraceFilterRule | undefined => {
|
|
63
|
-
const include = mergeStringLists(base?.include ?? [], override?.include);
|
|
64
|
-
const exclude = mergeStringLists(base?.exclude ?? [], override?.exclude);
|
|
65
|
-
const enabled = resolveEnabled(base, override);
|
|
66
|
-
|
|
67
|
-
if (!hasMergedRuleValues(include, exclude, enabled)) return undefined;
|
|
68
|
-
|
|
69
|
-
return buildFilterRule({ include, exclude, enabled });
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const mergeWatcherToggle = (
|
|
73
|
-
base?: TraceWatcherToggle,
|
|
74
|
-
override?: TraceWatcherToggle
|
|
75
|
-
): TraceWatcherToggle | undefined => {
|
|
76
|
-
if (override === undefined) return base;
|
|
77
|
-
if (override === false || override === true) return override;
|
|
78
|
-
|
|
79
|
-
const baseRule = isObjectValue(base) ? base : undefined;
|
|
80
|
-
return mergeFilterRule(baseRule, override);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
type ClientRequestCaptureFlags = Pick<
|
|
84
|
-
TraceClientRequestCaptureRule,
|
|
85
|
-
'requestHeaders' | 'requestBody' | 'responseHeaders' | 'responseBody'
|
|
86
|
-
>;
|
|
87
|
-
|
|
88
|
-
const resolveClientRequestCaptureFlags = (
|
|
89
|
-
base?: TraceClientRequestCaptureRule,
|
|
90
|
-
override?: TraceClientRequestCaptureRule
|
|
91
|
-
): ClientRequestCaptureFlags => {
|
|
92
|
-
return {
|
|
93
|
-
requestHeaders: override?.requestHeaders ?? base?.requestHeaders,
|
|
94
|
-
requestBody: override?.requestBody ?? base?.requestBody,
|
|
95
|
-
responseHeaders: override?.responseHeaders ?? base?.responseHeaders,
|
|
96
|
-
responseBody: override?.responseBody ?? base?.responseBody,
|
|
97
|
-
};
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const hasClientRequestCaptureFlags = (flags: ClientRequestCaptureFlags): boolean => {
|
|
101
|
-
return Object.values(flags).some((value) => value !== undefined);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const buildClientRequestCaptureRule = (
|
|
105
|
-
mergedRule: TraceFilterRule | undefined,
|
|
106
|
-
flags: ClientRequestCaptureFlags
|
|
107
|
-
): TraceClientRequestCaptureRule => {
|
|
108
|
-
const baseRule = mergedRule ? { ...mergedRule } : {};
|
|
109
|
-
|
|
110
|
-
return Object.freeze({
|
|
111
|
-
...baseRule,
|
|
112
|
-
...(flags.requestHeaders === undefined ? {} : { requestHeaders: flags.requestHeaders }),
|
|
113
|
-
...(flags.requestBody === undefined ? {} : { requestBody: flags.requestBody }),
|
|
114
|
-
...(flags.responseHeaders === undefined ? {} : { responseHeaders: flags.responseHeaders }),
|
|
115
|
-
...(flags.responseBody === undefined ? {} : { responseBody: flags.responseBody }),
|
|
116
|
-
});
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const mergeClientRequestCaptureRule = (
|
|
120
|
-
base?: TraceClientRequestCaptureRule,
|
|
121
|
-
override?: TraceClientRequestCaptureRule
|
|
122
|
-
): TraceClientRequestCaptureRule | undefined => {
|
|
123
|
-
const mergedRule = mergeFilterRule(base, override);
|
|
124
|
-
const flags = resolveClientRequestCaptureFlags(base, override);
|
|
125
|
-
|
|
126
|
-
if (mergedRule === undefined && !hasClientRequestCaptureFlags(flags)) {
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return buildClientRequestCaptureRule(mergedRule, flags);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const collectClientRequestSourceKeys = (
|
|
134
|
-
base?: TraceClientRequestWatcherToggle,
|
|
135
|
-
override?: Exclude<TraceClientRequestWatcherToggle, boolean>
|
|
136
|
-
): string[] => {
|
|
137
|
-
const overrideSources = override?.sources ?? {};
|
|
138
|
-
const sourceKeys = new Set<string>([
|
|
139
|
-
...Object.keys(isObjectValue(base) ? (base.sources ?? {}) : {}),
|
|
140
|
-
...Object.keys(overrideSources),
|
|
141
|
-
]);
|
|
142
|
-
|
|
143
|
-
return [...sourceKeys];
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const mergeClientRequestSources = (
|
|
147
|
-
base?: TraceClientRequestWatcherToggle,
|
|
148
|
-
override?: Exclude<TraceClientRequestWatcherToggle, boolean>
|
|
149
|
-
): Record<string, TraceClientRequestCaptureRule> | undefined => {
|
|
150
|
-
if (override === undefined) return undefined;
|
|
151
|
-
|
|
152
|
-
const sources: Record<string, TraceClientRequestCaptureRule> = {};
|
|
153
|
-
|
|
154
|
-
for (const key of collectClientRequestSourceKeys(base, override)) {
|
|
155
|
-
const baseSources = isObjectValue(base) ? base.sources : undefined;
|
|
156
|
-
const sourceRule = mergeClientRequestCaptureRule(baseSources?.[key], override.sources?.[key]);
|
|
157
|
-
if (sourceRule !== undefined) {
|
|
158
|
-
sources[key] = sourceRule;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return Object.keys(sources).length === 0 ? undefined : sources;
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const mergeClientRequestWatcherToggle = (
|
|
166
|
-
base?: TraceClientRequestWatcherToggle,
|
|
167
|
-
override?: TraceClientRequestWatcherToggle
|
|
168
|
-
): TraceClientRequestWatcherToggle | undefined => {
|
|
169
|
-
if (override === undefined) return base;
|
|
170
|
-
if (override === false || override === true) return override;
|
|
171
|
-
|
|
172
|
-
const baseConfig = isObjectValue(base) ? base : undefined;
|
|
173
|
-
const merged = mergeClientRequestCaptureRule(baseConfig, override) ?? {};
|
|
174
|
-
const sources = mergeClientRequestSources(base, override);
|
|
175
|
-
|
|
176
|
-
if (sources === undefined) {
|
|
177
|
-
return merged;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return Object.freeze({
|
|
181
|
-
...merged,
|
|
182
|
-
sources,
|
|
183
|
-
});
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const REQUEST_METHOD_KEYS = ['all', 'get', 'post', 'put', 'patch', 'delete'] as const;
|
|
187
|
-
|
|
188
|
-
const mergeRequestWatcherToggle = (
|
|
189
|
-
base?: ITraceConfig['watchers']['request'],
|
|
190
|
-
override?: ITraceConfig['watchers']['request']
|
|
191
|
-
): ITraceConfig['watchers']['request'] | undefined => {
|
|
192
|
-
if (override === undefined) return base;
|
|
193
|
-
if (override === false || override === true) return override;
|
|
194
|
-
|
|
195
|
-
const baseConfig = isObjectValue(base) ? base : undefined;
|
|
196
|
-
const merged: TraceRequestWatcherConfig = mergeFilterRule(baseConfig, override) ?? {};
|
|
197
|
-
|
|
198
|
-
for (const key of REQUEST_METHOD_KEYS) {
|
|
199
|
-
const rule = mergeFilterRule(baseConfig?.[key], override[key]);
|
|
200
|
-
if (rule !== undefined) merged[key] = rule;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return merged;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const mergeWatchers = (
|
|
207
|
-
base: ITraceConfig['watchers'],
|
|
208
|
-
override?: TraceConfigOverrides['watchers']
|
|
209
|
-
): ITraceConfig['watchers'] => {
|
|
210
|
-
if (override === undefined) return { ...base };
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
...base,
|
|
214
|
-
...override,
|
|
215
|
-
request: mergeRequestWatcherToggle(base.request, override.request),
|
|
216
|
-
query: mergeWatcherToggle(base.query, override.query),
|
|
217
|
-
exception: mergeWatcherToggle(base.exception, override.exception),
|
|
218
|
-
log: mergeWatcherToggle(base.log, override.log),
|
|
219
|
-
job: mergeWatcherToggle(base.job, override.job),
|
|
220
|
-
cache: mergeWatcherToggle(base.cache, override.cache),
|
|
221
|
-
schedule: mergeWatcherToggle(base.schedule, override.schedule),
|
|
222
|
-
mail: mergeWatcherToggle(base.mail, override.mail),
|
|
223
|
-
auth: mergeWatcherToggle(base.auth, override.auth),
|
|
224
|
-
event: mergeWatcherToggle(base.event, override.event),
|
|
225
|
-
model: mergeWatcherToggle(base.model, override.model),
|
|
226
|
-
notification: mergeWatcherToggle(base.notification, override.notification),
|
|
227
|
-
redis: mergeWatcherToggle(base.redis, override.redis),
|
|
228
|
-
gate: mergeWatcherToggle(base.gate, override.gate),
|
|
229
|
-
middleware: mergeWatcherToggle(base.middleware, override.middleware),
|
|
230
|
-
command: mergeWatcherToggle(base.command, override.command),
|
|
231
|
-
batch: mergeWatcherToggle(base.batch, override.batch),
|
|
232
|
-
dump: mergeWatcherToggle(base.dump, override.dump),
|
|
233
|
-
view: mergeWatcherToggle(base.view, override.view),
|
|
234
|
-
clientRequest: mergeClientRequestWatcherToggle(base.clientRequest, override.clientRequest),
|
|
235
|
-
};
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
const mergeContentDispatch = (
|
|
239
|
-
base: TraceContentDispatchConfig,
|
|
240
|
-
override?: TraceConfigOverrides['contentDispatch']
|
|
241
|
-
): TraceContentDispatchConfig => {
|
|
242
|
-
const workerOverride = override?.worker;
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
...base,
|
|
246
|
-
...override,
|
|
247
|
-
worker:
|
|
248
|
-
workerOverride === undefined
|
|
249
|
-
? base.worker
|
|
250
|
-
: {
|
|
251
|
-
...base.worker,
|
|
252
|
-
...workerOverride,
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const mergeProxyConfig = (
|
|
258
|
-
base: TraceProxyConfig,
|
|
259
|
-
override?: TraceConfigOverrides['proxy']
|
|
260
|
-
): TraceProxyConfig => {
|
|
261
|
-
if (override === undefined) return base;
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
...base,
|
|
265
|
-
...override,
|
|
266
|
-
};
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
const DEFAULTS: ITraceConfig = Object.freeze({
|
|
270
|
-
enabled: false,
|
|
271
|
-
connection: undefined,
|
|
272
|
-
observeConnection: undefined,
|
|
273
|
-
serviceTag: undefined,
|
|
274
|
-
proxy: {
|
|
275
|
-
enabled: false,
|
|
276
|
-
url: undefined,
|
|
277
|
-
path: '/zin/trace/write',
|
|
278
|
-
keyId: undefined,
|
|
279
|
-
secret: undefined,
|
|
280
|
-
timeoutMs: 30000,
|
|
281
|
-
},
|
|
282
|
-
pruneAfterHours: 24,
|
|
283
|
-
ignoreRoutes: ['/trace', '/health', '/ping'],
|
|
284
|
-
ignorePaths: [],
|
|
285
|
-
slowQueryThreshold: 100,
|
|
286
|
-
captureCachePayloads: false,
|
|
287
|
-
captureQueryBindings: true,
|
|
288
|
-
logMinLevel: 'info',
|
|
289
|
-
contentDispatch: {
|
|
290
|
-
driver: undefined,
|
|
291
|
-
queueName: 'trace-content',
|
|
292
|
-
enqueueTimeoutMs: 25,
|
|
293
|
-
worker: {
|
|
294
|
-
enabled: true,
|
|
295
|
-
intervalMs: 1000,
|
|
296
|
-
maxDurationMs: 250,
|
|
297
|
-
concurrency: 1,
|
|
298
|
-
},
|
|
299
|
-
},
|
|
300
|
-
watchers: {},
|
|
301
|
-
redaction: {
|
|
302
|
-
keys: [
|
|
303
|
-
'password',
|
|
304
|
-
'pass',
|
|
305
|
-
'passwd',
|
|
306
|
-
'token',
|
|
307
|
-
'accessToken',
|
|
308
|
-
'access_token',
|
|
309
|
-
'refreshToken',
|
|
310
|
-
'refresh_token',
|
|
311
|
-
'secret',
|
|
312
|
-
'secretKey',
|
|
313
|
-
'secret_key',
|
|
314
|
-
'apiKey',
|
|
315
|
-
'api_key',
|
|
316
|
-
'auth',
|
|
317
|
-
'authToken',
|
|
318
|
-
'auth_token',
|
|
319
|
-
'authorization',
|
|
320
|
-
'cookie',
|
|
321
|
-
'session',
|
|
322
|
-
'sessionId',
|
|
323
|
-
'session_id',
|
|
324
|
-
'card',
|
|
325
|
-
'cardNumber',
|
|
326
|
-
'card_number',
|
|
327
|
-
'cardToken',
|
|
328
|
-
'card_token',
|
|
329
|
-
'cvv',
|
|
330
|
-
'cvc',
|
|
331
|
-
'pan',
|
|
332
|
-
],
|
|
333
|
-
headers: ['authorization', 'cookie', 'x-api-key', 'x-auth-token'],
|
|
334
|
-
body: ['password', 'token', 'secret', 'apiKey', 'api_key', 'jwt', 'bearer'],
|
|
335
|
-
query: [],
|
|
336
|
-
},
|
|
337
|
-
} satisfies ITraceConfig);
|
|
338
|
-
|
|
339
|
-
const isWatcherEnabled = (config: ITraceConfig, key: keyof ITraceConfig['watchers']): boolean => {
|
|
340
|
-
const override = config.watchers[key];
|
|
341
|
-
if (override === false) return false;
|
|
342
|
-
if (isObjectValue(override) && override.enabled === false) return false;
|
|
343
|
-
return true; // undefined = enabled by default; explicit false = disabled
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const getRedactionFields = (
|
|
347
|
-
config: ITraceConfig,
|
|
348
|
-
key: keyof ITraceConfig['redaction']
|
|
349
|
-
): string[] => {
|
|
350
|
-
if (key === 'keys') {
|
|
351
|
-
return mergeStringLists([], config.redaction.keys);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return mergeStringLists(config.redaction.keys, config.redaction[key]);
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
export const TraceConfig = Object.freeze({
|
|
358
|
-
defaults(): ITraceConfig {
|
|
359
|
-
return DEFAULTS;
|
|
360
|
-
},
|
|
361
|
-
|
|
362
|
-
merge(overrides?: TraceConfigOverrides): ITraceConfig {
|
|
363
|
-
if (overrides === undefined || overrides === null) return DEFAULTS;
|
|
364
|
-
return Object.freeze({
|
|
365
|
-
...DEFAULTS,
|
|
366
|
-
...overrides,
|
|
367
|
-
proxy: mergeProxyConfig(DEFAULTS.proxy, overrides.proxy),
|
|
368
|
-
contentDispatch: mergeContentDispatch(DEFAULTS.contentDispatch, overrides.contentDispatch),
|
|
369
|
-
watchers: mergeWatchers(DEFAULTS.watchers, overrides.watchers),
|
|
370
|
-
redaction: {
|
|
371
|
-
keys: mergeStringLists(DEFAULTS.redaction.keys, overrides.redaction?.keys),
|
|
372
|
-
headers: mergeStringLists(DEFAULTS.redaction.headers, overrides.redaction?.headers),
|
|
373
|
-
body: mergeStringLists(DEFAULTS.redaction.body, overrides.redaction?.body),
|
|
374
|
-
query: mergeStringLists(DEFAULTS.redaction.query, overrides.redaction?.query),
|
|
375
|
-
},
|
|
376
|
-
ignoreRoutes: overrides.ignoreRoutes ?? DEFAULTS.ignoreRoutes,
|
|
377
|
-
ignorePaths: overrides.ignorePaths ?? DEFAULTS.ignorePaths,
|
|
378
|
-
});
|
|
379
|
-
},
|
|
380
|
-
|
|
381
|
-
getRedactionFields,
|
|
382
|
-
isWatcherEnabled,
|
|
383
|
-
});
|
package/src/context.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TraceContext — sealed namespace for batch_id, userId, hostname, and memory.
|
|
3
|
-
* Piggybacks on RequestContext (already available in core) — no new ALS store.
|
|
4
|
-
*/
|
|
5
|
-
type RequestContextProvider = {
|
|
6
|
-
current?: () => unknown;
|
|
7
|
-
peek?: () => unknown;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
// Lazy reference to ZinTrust RequestContext — typed as unknown to stay runtime-agnostic.
|
|
11
|
-
let _reqCtx: RequestContextProvider | undefined;
|
|
12
|
-
|
|
13
|
-
const getRequestContext = (): RequestContextProvider | undefined => {
|
|
14
|
-
return _reqCtx;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const setRequestContextImpl = (impl: RequestContextProvider): void => {
|
|
18
|
-
_reqCtx = impl;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const isPromiseLike = (value: unknown): value is PromiseLike<unknown> => {
|
|
22
|
-
return typeof value === 'object' && value !== null && 'then' in value;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const getCurrentContext = (): Record<string, unknown> | undefined => {
|
|
26
|
-
const provider = getRequestContext();
|
|
27
|
-
if (!provider) return undefined;
|
|
28
|
-
|
|
29
|
-
let currentValue: unknown;
|
|
30
|
-
|
|
31
|
-
if (typeof provider.peek === 'function') {
|
|
32
|
-
currentValue = provider.peek();
|
|
33
|
-
} else if (typeof provider.current === 'function') {
|
|
34
|
-
currentValue = provider.current();
|
|
35
|
-
} else {
|
|
36
|
-
currentValue = undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (isPromiseLike(currentValue)) return undefined;
|
|
40
|
-
if (typeof currentValue !== 'object' || currentValue === null) return undefined;
|
|
41
|
-
|
|
42
|
-
return currentValue as Record<string, unknown>;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const getContextString = (key: 'traceId' | 'userId' | 'path'): string | undefined => {
|
|
46
|
-
const value = getCurrentContext()?.[key];
|
|
47
|
-
if (typeof value === 'string' && value.trim() !== '') return value;
|
|
48
|
-
if (typeof value === 'number') return String(value);
|
|
49
|
-
return undefined;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const getBatchId = (): string => {
|
|
53
|
-
return getContextString('traceId') ?? crypto.randomUUID();
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const getUserId = (): string | undefined => {
|
|
57
|
-
return getContextString('userId');
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const getRequestPath = (): string | undefined => {
|
|
61
|
-
return getContextString('path');
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const getHostname = (): string => {
|
|
65
|
-
// Workers do not expose `os` or `process` — return 'worker' as fallback.
|
|
66
|
-
if (typeof process !== 'undefined' && typeof process.env === 'object') {
|
|
67
|
-
try {
|
|
68
|
-
// Dynamic import avoids the need for a node-singletons wrapper at the type level.
|
|
69
|
-
// Hostname is non-critical; we fall back gracefully.
|
|
70
|
-
const hostname = (process.env as Record<string, string | undefined>)['HOSTNAME'];
|
|
71
|
-
if (typeof hostname === 'string' && hostname.length > 0) return hostname;
|
|
72
|
-
} catch {
|
|
73
|
-
// fall through
|
|
74
|
-
}
|
|
75
|
-
return 'node';
|
|
76
|
-
}
|
|
77
|
-
return 'worker';
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const getMemory = (): number | null => {
|
|
81
|
-
if (typeof process !== 'undefined' && typeof process.memoryUsage === 'function') {
|
|
82
|
-
try {
|
|
83
|
-
return Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
84
|
-
} catch {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return null;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const now = (): number => Date.now();
|
|
92
|
-
|
|
93
|
-
export const TraceContext = Object.freeze({
|
|
94
|
-
getBatchId,
|
|
95
|
-
getUserId,
|
|
96
|
-
getRequestPath,
|
|
97
|
-
getHostname,
|
|
98
|
-
getMemory,
|
|
99
|
-
now,
|
|
100
|
-
setRequestContextImpl,
|
|
101
|
-
});
|