@zintrust/trace 0.4.75

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.
Files changed (128) hide show
  1. package/README.md +288 -0
  2. package/dist/build-manifest.json +365 -0
  3. package/dist/cli-register.d.ts +9 -0
  4. package/dist/cli-register.js +32 -0
  5. package/dist/config.d.ts +9 -0
  6. package/dist/config.js +38 -0
  7. package/dist/context.d.ts +18 -0
  8. package/dist/context.js +86 -0
  9. package/dist/dashboard/handlers.d.ts +15 -0
  10. package/dist/dashboard/handlers.js +179 -0
  11. package/dist/dashboard/routes.d.ts +19 -0
  12. package/dist/dashboard/routes.js +50 -0
  13. package/dist/dashboard/ui.d.ts +2 -0
  14. package/dist/dashboard/ui.js +870 -0
  15. package/dist/index.d.ts +35 -0
  16. package/dist/index.js +50 -0
  17. package/dist/migrations/20260331000001_create_zin_debugger_entries_table.d.ts +10 -0
  18. package/dist/migrations/20260331000001_create_zin_debugger_entries_table.js +28 -0
  19. package/dist/migrations/20260331000002_create_zin_debugger_entries_tags_table.d.ts +10 -0
  20. package/dist/migrations/20260331000002_create_zin_debugger_entries_tags_table.js +21 -0
  21. package/dist/migrations/20260331000003_create_zin_debugger_monitoring_table.d.ts +10 -0
  22. package/dist/migrations/20260331000003_create_zin_debugger_monitoring_table.js +17 -0
  23. package/dist/migrations/index.d.ts +6 -0
  24. package/dist/migrations/index.js +4 -0
  25. package/dist/plugin.d.ts +1 -0
  26. package/dist/plugin.js +3 -0
  27. package/dist/register.d.ts +1 -0
  28. package/dist/register.js +140 -0
  29. package/dist/storage/DebuggerStorage.d.ts +13 -0
  30. package/dist/storage/DebuggerStorage.js +195 -0
  31. package/dist/storage/TraceStorage.d.ts +13 -0
  32. package/dist/storage/TraceStorage.js +195 -0
  33. package/dist/storage/index.d.ts +2 -0
  34. package/dist/storage/index.js +1 -0
  35. package/dist/types.d.ts +270 -0
  36. package/dist/types.js +25 -0
  37. package/dist/ui.d.ts +8 -0
  38. package/dist/ui.js +7 -0
  39. package/dist/utils/authTag.d.ts +5 -0
  40. package/dist/utils/authTag.js +18 -0
  41. package/dist/utils/familyHash.d.ts +1 -0
  42. package/dist/utils/familyHash.js +8 -0
  43. package/dist/utils/redact.d.ts +6 -0
  44. package/dist/utils/redact.js +49 -0
  45. package/dist/utils/requestFilter.d.ts +4 -0
  46. package/dist/utils/requestFilter.js +26 -0
  47. package/dist/utils/stackFrame.d.ts +6 -0
  48. package/dist/utils/stackFrame.js +38 -0
  49. package/dist/watchers/AuthWatcher.d.ts +6 -0
  50. package/dist/watchers/AuthWatcher.js +49 -0
  51. package/dist/watchers/BatchWatcher.d.ts +6 -0
  52. package/dist/watchers/BatchWatcher.js +46 -0
  53. package/dist/watchers/CacheWatcher.d.ts +6 -0
  54. package/dist/watchers/CacheWatcher.js +51 -0
  55. package/dist/watchers/CommandWatcher.d.ts +6 -0
  56. package/dist/watchers/CommandWatcher.js +49 -0
  57. package/dist/watchers/DumpWatcher.d.ts +7 -0
  58. package/dist/watchers/DumpWatcher.js +41 -0
  59. package/dist/watchers/EventWatcher.d.ts +6 -0
  60. package/dist/watchers/EventWatcher.js +42 -0
  61. package/dist/watchers/ExceptionWatcher.d.ts +4 -0
  62. package/dist/watchers/ExceptionWatcher.js +103 -0
  63. package/dist/watchers/GateWatcher.d.ts +6 -0
  64. package/dist/watchers/GateWatcher.js +45 -0
  65. package/dist/watchers/HttpClientWatcher.d.ts +6 -0
  66. package/dist/watchers/HttpClientWatcher.js +50 -0
  67. package/dist/watchers/HttpWatcher.d.ts +2 -0
  68. package/dist/watchers/HttpWatcher.js +71 -0
  69. package/dist/watchers/JobWatcher.d.ts +10 -0
  70. package/dist/watchers/JobWatcher.js +108 -0
  71. package/dist/watchers/LogWatcher.d.ts +2 -0
  72. package/dist/watchers/LogWatcher.js +50 -0
  73. package/dist/watchers/MailWatcher.d.ts +6 -0
  74. package/dist/watchers/MailWatcher.js +45 -0
  75. package/dist/watchers/MiddlewareWatcher.d.ts +6 -0
  76. package/dist/watchers/MiddlewareWatcher.js +41 -0
  77. package/dist/watchers/ModelWatcher.d.ts +6 -0
  78. package/dist/watchers/ModelWatcher.js +42 -0
  79. package/dist/watchers/NotificationWatcher.d.ts +6 -0
  80. package/dist/watchers/NotificationWatcher.js +42 -0
  81. package/dist/watchers/QueryWatcher.d.ts +2 -0
  82. package/dist/watchers/QueryWatcher.js +72 -0
  83. package/dist/watchers/RedisWatcher.d.ts +7 -0
  84. package/dist/watchers/RedisWatcher.js +38 -0
  85. package/dist/watchers/ScheduleWatcher.d.ts +6 -0
  86. package/dist/watchers/ScheduleWatcher.js +46 -0
  87. package/dist/watchers/ViewWatcher.d.ts +6 -0
  88. package/dist/watchers/ViewWatcher.js +36 -0
  89. package/package.json +59 -0
  90. package/src/cli-register.ts +63 -0
  91. package/src/config.ts +46 -0
  92. package/src/context.ts +101 -0
  93. package/src/dashboard/handlers.ts +197 -0
  94. package/src/dashboard/routes.ts +101 -0
  95. package/src/dashboard/ui.ts +879 -0
  96. package/src/dashboard/zintrust-debuger.svg +30 -0
  97. package/src/index.ts +88 -0
  98. package/src/plugin.ts +9 -0
  99. package/src/register.ts +219 -0
  100. package/src/storage/TraceStorage.ts +306 -0
  101. package/src/storage/index.ts +2 -0
  102. package/src/types.ts +317 -0
  103. package/src/ui.ts +9 -0
  104. package/src/utils/authTag.ts +20 -0
  105. package/src/utils/familyHash.ts +8 -0
  106. package/src/utils/redact.ts +64 -0
  107. package/src/utils/requestFilter.ts +33 -0
  108. package/src/utils/stackFrame.ts +44 -0
  109. package/src/watchers/AuthWatcher.ts +50 -0
  110. package/src/watchers/BatchWatcher.ts +52 -0
  111. package/src/watchers/CacheWatcher.ts +58 -0
  112. package/src/watchers/CommandWatcher.ts +55 -0
  113. package/src/watchers/DumpWatcher.ts +42 -0
  114. package/src/watchers/EventWatcher.ts +43 -0
  115. package/src/watchers/ExceptionWatcher.ts +114 -0
  116. package/src/watchers/GateWatcher.ts +50 -0
  117. package/src/watchers/HttpClientWatcher.ts +56 -0
  118. package/src/watchers/HttpWatcher.ts +94 -0
  119. package/src/watchers/JobWatcher.ts +121 -0
  120. package/src/watchers/LogWatcher.ts +61 -0
  121. package/src/watchers/MailWatcher.ts +47 -0
  122. package/src/watchers/MiddlewareWatcher.ts +42 -0
  123. package/src/watchers/ModelWatcher.ts +48 -0
  124. package/src/watchers/NotificationWatcher.ts +43 -0
  125. package/src/watchers/QueryWatcher.ts +85 -0
  126. package/src/watchers/RedisWatcher.ts +39 -0
  127. package/src/watchers/ScheduleWatcher.ts +54 -0
  128. package/src/watchers/ViewWatcher.ts +37 -0
@@ -0,0 +1,6 @@
1
+ import type { AuthContent, ITraceWatcher } from '../types';
2
+ declare const emit: (event: AuthContent["event"], userId?: string) => void;
3
+ export declare const AuthWatcher: ITraceWatcher & {
4
+ emit: typeof emit;
5
+ };
6
+ export {};
@@ -0,0 +1,49 @@
1
+ /**
2
+ * AuthWatcher — records login/logout/failed auth events.
3
+ * Credentials are never stored; only the outcome.
4
+ */
5
+ import { TraceContext } from '../context';
6
+ import { EntryType } from '../types';
7
+ import { RequestFilter } from '../utils/requestFilter';
8
+ let _storage = null;
9
+ let _ignoreRoutes = [];
10
+ const emit = (event, userId) => {
11
+ if (!_storage)
12
+ return;
13
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
14
+ return;
15
+ const content = {
16
+ event,
17
+ userId,
18
+ hostname: TraceContext.getHostname(),
19
+ };
20
+ const tags = [];
21
+ if (userId)
22
+ tags.push(`Auth:${userId}`);
23
+ if (event === 'failed')
24
+ tags.push('failed');
25
+ _storage
26
+ .writeEntry({
27
+ uuid: crypto.randomUUID(),
28
+ batchId: TraceContext.getBatchId(),
29
+ type: EntryType.AUTH,
30
+ content,
31
+ tags,
32
+ isLatest: true,
33
+ createdAt: TraceContext.now(),
34
+ })
35
+ .catch(() => undefined);
36
+ };
37
+ export const AuthWatcher = Object.freeze({
38
+ emit,
39
+ register({ storage, config }) {
40
+ if (config.watchers.auth === false)
41
+ return () => undefined;
42
+ _storage = storage;
43
+ _ignoreRoutes = config.ignoreRoutes;
44
+ return () => {
45
+ _storage = null;
46
+ _ignoreRoutes = [];
47
+ };
48
+ },
49
+ });
@@ -0,0 +1,6 @@
1
+ import type { BatchContent, ITraceWatcher } from '../types';
2
+ declare const emit: (name: string, total: number, processed: number, failed: number, status: BatchContent["status"]) => void;
3
+ export declare const BatchWatcher: ITraceWatcher & {
4
+ emit: typeof emit;
5
+ };
6
+ export {};
@@ -0,0 +1,46 @@
1
+ import { TraceContext } from '../context';
2
+ import { EntryType } from '../types';
3
+ import { RequestFilter } from '../utils/requestFilter';
4
+ let _storage = null;
5
+ let _ignoreRoutes = [];
6
+ const emit = (name, total, processed, failed, status) => {
7
+ if (!_storage)
8
+ return;
9
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
10
+ return;
11
+ const tags = [name];
12
+ if (failed > 0)
13
+ tags.push('failed');
14
+ const content = {
15
+ name,
16
+ total,
17
+ processed,
18
+ failed,
19
+ status,
20
+ hostname: TraceContext.getHostname(),
21
+ };
22
+ _storage
23
+ .writeEntry({
24
+ uuid: crypto.randomUUID(),
25
+ batchId: TraceContext.getBatchId(),
26
+ type: EntryType.BATCH,
27
+ content,
28
+ tags,
29
+ isLatest: true,
30
+ createdAt: TraceContext.now(),
31
+ })
32
+ .catch(() => undefined);
33
+ };
34
+ export const BatchWatcher = Object.freeze({
35
+ emit,
36
+ register({ storage, config }) {
37
+ if (config.watchers.batch === false)
38
+ return () => undefined;
39
+ _storage = storage;
40
+ _ignoreRoutes = config.ignoreRoutes;
41
+ return () => {
42
+ _storage = null;
43
+ _ignoreRoutes = [];
44
+ };
45
+ },
46
+ });
@@ -0,0 +1,6 @@
1
+ import type { CacheContent, ITraceWatcher } from '../types';
2
+ declare const emit: (operation: CacheContent["operation"], key: string, duration: number, hit?: boolean) => void;
3
+ export declare const CacheWatcher: ITraceWatcher & {
4
+ emit: typeof emit;
5
+ };
6
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * CacheWatcher — records cache operations.
3
+ * Call CacheWatcher.emit() from within your cache driver instrumentation.
4
+ */
5
+ import { TraceContext } from '../context';
6
+ import { EntryType } from '../types';
7
+ import { AuthTag } from '../utils/authTag';
8
+ import { redactString } from '../utils/redact';
9
+ import { RequestFilter } from '../utils/requestFilter';
10
+ let _storage = null;
11
+ let _redactionFields = [];
12
+ let _ignoreRoutes = [];
13
+ const emit = (operation, key, duration, hit) => {
14
+ if (!_storage)
15
+ return;
16
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
17
+ return;
18
+ const safeKey = redactString(key, _redactionFields);
19
+ const content = {
20
+ operation,
21
+ key: safeKey,
22
+ hit,
23
+ duration,
24
+ hostname: TraceContext.getHostname(),
25
+ };
26
+ _storage
27
+ .writeEntry({
28
+ uuid: crypto.randomUUID(),
29
+ batchId: TraceContext.getBatchId(),
30
+ type: EntryType.CACHE,
31
+ content,
32
+ tags: AuthTag.append([]),
33
+ isLatest: true,
34
+ createdAt: TraceContext.now(),
35
+ })
36
+ .catch(() => undefined);
37
+ };
38
+ export const CacheWatcher = Object.freeze({
39
+ emit,
40
+ register({ storage, config }) {
41
+ if (config.watchers.cache === false)
42
+ return () => undefined;
43
+ _storage = storage;
44
+ _redactionFields = config.redaction.query;
45
+ _ignoreRoutes = config.ignoreRoutes;
46
+ return () => {
47
+ _storage = null;
48
+ _ignoreRoutes = [];
49
+ };
50
+ },
51
+ });
@@ -0,0 +1,6 @@
1
+ import type { ITraceWatcher } from '../types';
2
+ declare const emit: (name: string, args: Record<string, unknown>, exitCode: number, duration: number, output?: string) => void;
3
+ export declare const CommandWatcher: ITraceWatcher & {
4
+ emit: typeof emit;
5
+ };
6
+ export {};
@@ -0,0 +1,49 @@
1
+ import { TraceContext } from '../context';
2
+ import { EntryType } from '../types';
3
+ import { redactObject } from '../utils/redact';
4
+ import { RequestFilter } from '../utils/requestFilter';
5
+ let _storage = null;
6
+ let _redactKeys = [];
7
+ let _ignoreRoutes = [];
8
+ const emit = (name, args, exitCode, duration, output) => {
9
+ if (!_storage)
10
+ return;
11
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
12
+ return;
13
+ const tags = [name];
14
+ if (exitCode !== 0)
15
+ tags.push('failed');
16
+ const content = {
17
+ name,
18
+ arguments: redactObject(args, _redactKeys),
19
+ exitCode,
20
+ duration,
21
+ output,
22
+ hostname: TraceContext.getHostname(),
23
+ };
24
+ _storage
25
+ .writeEntry({
26
+ uuid: crypto.randomUUID(),
27
+ batchId: TraceContext.getBatchId(),
28
+ type: EntryType.COMMAND,
29
+ content,
30
+ tags,
31
+ isLatest: true,
32
+ createdAt: TraceContext.now(),
33
+ })
34
+ .catch(() => undefined);
35
+ };
36
+ export const CommandWatcher = Object.freeze({
37
+ emit,
38
+ register({ storage, config }) {
39
+ if (config.watchers.command === false)
40
+ return () => undefined;
41
+ _storage = storage;
42
+ _redactKeys = config.redaction?.body ?? [];
43
+ _ignoreRoutes = config.ignoreRoutes;
44
+ return () => {
45
+ _storage = null;
46
+ _ignoreRoutes = [];
47
+ };
48
+ },
49
+ });
@@ -0,0 +1,7 @@
1
+ import type { ITraceWatcher } from '../types';
2
+ /** Explicitly opt-in (enabled only when config.watchers.dump === true, not just non-false). */
3
+ declare const emit: (value: unknown, file?: string, line?: number) => void;
4
+ export declare const DumpWatcher: ITraceWatcher & {
5
+ emit: typeof emit;
6
+ };
7
+ export {};
@@ -0,0 +1,41 @@
1
+ import { TraceContext } from '../context';
2
+ import { EntryType } from '../types';
3
+ import { RequestFilter } from '../utils/requestFilter';
4
+ let _storage = null;
5
+ let _enabled = false;
6
+ let _ignoreRoutes = [];
7
+ /** Explicitly opt-in (enabled only when config.watchers.dump === true, not just non-false). */
8
+ const emit = (value, file, line) => {
9
+ if (!_storage || !_enabled)
10
+ return;
11
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
12
+ return;
13
+ const content = { value, file, line, hostname: TraceContext.getHostname() };
14
+ _storage
15
+ .writeEntry({
16
+ uuid: crypto.randomUUID(),
17
+ batchId: TraceContext.getBatchId(),
18
+ type: EntryType.DUMP,
19
+ content,
20
+ tags: [],
21
+ isLatest: true,
22
+ createdAt: TraceContext.now(),
23
+ })
24
+ .catch(() => undefined);
25
+ };
26
+ export const DumpWatcher = Object.freeze({
27
+ emit,
28
+ register({ storage, config }) {
29
+ // DumpWatcher requires explicit opt-in (=== true), not just absence of false
30
+ if (config.watchers.dump !== true)
31
+ return () => undefined;
32
+ _storage = storage;
33
+ _enabled = true;
34
+ _ignoreRoutes = config.ignoreRoutes;
35
+ return () => {
36
+ _storage = null;
37
+ _enabled = false;
38
+ _ignoreRoutes = [];
39
+ };
40
+ },
41
+ });
@@ -0,0 +1,6 @@
1
+ import type { ITraceWatcher } from '../types';
2
+ declare const emit: (name: string, listenerCount: number, payload?: unknown) => void;
3
+ export declare const EventWatcher: ITraceWatcher & {
4
+ emit: typeof emit;
5
+ };
6
+ export {};
@@ -0,0 +1,42 @@
1
+ import { TraceContext } from '../context';
2
+ import { EntryType } from '../types';
3
+ import { AuthTag } from '../utils/authTag';
4
+ import { RequestFilter } from '../utils/requestFilter';
5
+ let _storage = null;
6
+ let _ignoreRoutes = [];
7
+ const emit = (name, listenerCount, payload) => {
8
+ if (!_storage)
9
+ return;
10
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
11
+ return;
12
+ const content = {
13
+ name,
14
+ payload,
15
+ listenerCount,
16
+ hostname: TraceContext.getHostname(),
17
+ };
18
+ _storage
19
+ .writeEntry({
20
+ uuid: crypto.randomUUID(),
21
+ batchId: TraceContext.getBatchId(),
22
+ type: EntryType.EVENT,
23
+ content,
24
+ tags: AuthTag.append([name]),
25
+ isLatest: true,
26
+ createdAt: TraceContext.now(),
27
+ })
28
+ .catch(() => undefined);
29
+ };
30
+ export const EventWatcher = Object.freeze({
31
+ emit,
32
+ register({ storage, config }) {
33
+ if (config.watchers.event === false)
34
+ return () => undefined;
35
+ _storage = storage;
36
+ _ignoreRoutes = config.ignoreRoutes;
37
+ return () => {
38
+ _storage = null;
39
+ _ignoreRoutes = [];
40
+ };
41
+ },
42
+ });
@@ -0,0 +1,4 @@
1
+ import type { ITraceWatcher } from '../types';
2
+ export declare const ExceptionWatcher: ITraceWatcher & {
3
+ capture: (err: unknown) => void;
4
+ };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * ExceptionWatcher — captures unhandled exceptions by hooking into the
3
+ * framework error middleware. Core must call ExceptionWatcher.capture()
4
+ * from within its error handler, or the register() side-effect adds a
5
+ * process-level unhandledRejection/uncaughtException listener as fallback.
6
+ */
7
+ import { TraceContext } from '../context';
8
+ import { EntryType } from '../types';
9
+ import { AuthTag } from '../utils/authTag';
10
+ import { familyHash } from '../utils/familyHash';
11
+ import { RequestFilter } from '../utils/requestFilter';
12
+ import { parseStackFrameLine } from '../utils/stackFrame';
13
+ const getLinePreview = (_file, _line) => {
14
+ return {};
15
+ };
16
+ const buildContent = (err) => {
17
+ const stack = err.stack ?? '';
18
+ const trace = stack
19
+ .split('\n')
20
+ .slice(1)
21
+ .map(parseStackFrameLine)
22
+ .filter((x) => x !== null)
23
+ .slice(0, 20);
24
+ const firstFrame = trace[0];
25
+ return {
26
+ class: err.constructor?.name ?? 'Error',
27
+ file: firstFrame?.file ?? 'unknown',
28
+ line: firstFrame?.line ?? 0,
29
+ message: err.message,
30
+ trace,
31
+ linePreview: firstFrame ? getLinePreview(firstFrame.file, firstFrame.line) : {},
32
+ occurrences: 1,
33
+ hostname: TraceContext.getHostname(),
34
+ userId: TraceContext.getUserId(),
35
+ };
36
+ };
37
+ let _storage = null;
38
+ let _listenerRefCount = 0;
39
+ let _ignoreRoutes = [];
40
+ const handleUncaughtException = (error) => {
41
+ captureException(error);
42
+ };
43
+ const handleUnhandledRejection = (reason) => {
44
+ captureException(reason);
45
+ };
46
+ const registerProcessListeners = () => {
47
+ if (typeof process === 'undefined')
48
+ return;
49
+ process.on('uncaughtException', handleUncaughtException);
50
+ process.on('unhandledRejection', handleUnhandledRejection);
51
+ };
52
+ const unregisterProcessListeners = () => {
53
+ if (typeof process === 'undefined')
54
+ return;
55
+ process.off('uncaughtException', handleUncaughtException);
56
+ process.off('unhandledRejection', handleUnhandledRejection);
57
+ };
58
+ const captureException = (err) => {
59
+ const storage = _storage;
60
+ if (!storage)
61
+ return;
62
+ if (!(err instanceof Error))
63
+ return;
64
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
65
+ return;
66
+ const content = buildContent(err);
67
+ const hash = familyHash(`${content.class}:${content.file}:${content.line}`);
68
+ const uuid = crypto.randomUUID();
69
+ storage
70
+ .writeEntry({
71
+ uuid,
72
+ batchId: TraceContext.getBatchId(),
73
+ familyHash: hash,
74
+ type: EntryType.EXCEPTION,
75
+ content,
76
+ tags: AuthTag.append([content.class]),
77
+ isLatest: true,
78
+ createdAt: TraceContext.now(),
79
+ })
80
+ .then(() => storage.markFamilyStale(hash, uuid))
81
+ .catch(() => undefined);
82
+ };
83
+ export const ExceptionWatcher = Object.freeze({
84
+ capture: captureException,
85
+ register({ storage, config }) {
86
+ if (config.watchers.exception === false)
87
+ return () => undefined;
88
+ _storage = storage;
89
+ _ignoreRoutes = config.ignoreRoutes;
90
+ if (_listenerRefCount === 0) {
91
+ registerProcessListeners();
92
+ }
93
+ _listenerRefCount += 1;
94
+ return () => {
95
+ _listenerRefCount = Math.max(0, _listenerRefCount - 1);
96
+ if (_listenerRefCount === 0) {
97
+ unregisterProcessListeners();
98
+ }
99
+ _storage = null;
100
+ _ignoreRoutes = [];
101
+ };
102
+ },
103
+ });
@@ -0,0 +1,6 @@
1
+ import type { GateContent, ITraceWatcher } from '../types';
2
+ declare const emit: (ability: string, result: GateContent["result"], userId?: string, subject?: string) => void;
3
+ export declare const GateWatcher: ITraceWatcher & {
4
+ emit: typeof emit;
5
+ };
6
+ export {};
@@ -0,0 +1,45 @@
1
+ import { TraceContext } from '../context';
2
+ import { EntryType } from '../types';
3
+ import { RequestFilter } from '../utils/requestFilter';
4
+ let _storage = null;
5
+ let _ignoreRoutes = [];
6
+ const emit = (ability, result, userId, subject) => {
7
+ if (!_storage)
8
+ return;
9
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
10
+ return;
11
+ const tags = [ability, result];
12
+ if (userId)
13
+ tags.push(`Auth:${userId}`);
14
+ const content = {
15
+ ability,
16
+ result,
17
+ userId,
18
+ subject,
19
+ hostname: TraceContext.getHostname(),
20
+ };
21
+ _storage
22
+ .writeEntry({
23
+ uuid: crypto.randomUUID(),
24
+ batchId: TraceContext.getBatchId(),
25
+ type: EntryType.GATE,
26
+ content,
27
+ tags,
28
+ isLatest: true,
29
+ createdAt: TraceContext.now(),
30
+ })
31
+ .catch(() => undefined);
32
+ };
33
+ export const GateWatcher = Object.freeze({
34
+ emit,
35
+ register({ storage, config }) {
36
+ if (config.watchers.gate === false)
37
+ return () => undefined;
38
+ _storage = storage;
39
+ _ignoreRoutes = config.ignoreRoutes;
40
+ return () => {
41
+ _storage = null;
42
+ _ignoreRoutes = [];
43
+ };
44
+ },
45
+ });
@@ -0,0 +1,6 @@
1
+ import type { ITraceWatcher } from '../types';
2
+ declare const emit: (method: string, url: string, requestHeaders: Record<string, string>, responseStatus: number, duration: number) => void;
3
+ export declare const HttpClientWatcher: ITraceWatcher & {
4
+ emit: typeof emit;
5
+ };
6
+ export {};
@@ -0,0 +1,50 @@
1
+ import { TraceContext } from '../context';
2
+ import { EntryType } from '../types';
3
+ import { AuthTag } from '../utils/authTag';
4
+ import { redactHeaders } from '../utils/redact';
5
+ import { RequestFilter } from '../utils/requestFilter';
6
+ let _storage = null;
7
+ let _redactHeaderNames = [];
8
+ let _ignoreRoutes = [];
9
+ const emit = (method, url, requestHeaders, responseStatus, duration) => {
10
+ if (!_storage)
11
+ return;
12
+ if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
13
+ return;
14
+ const tags = AuthTag.append([method.toUpperCase()]);
15
+ if (responseStatus >= 400)
16
+ tags.push('failed');
17
+ const content = {
18
+ method: method.toUpperCase(),
19
+ url,
20
+ requestHeaders: redactHeaders(requestHeaders, _redactHeaderNames),
21
+ responseStatus,
22
+ duration,
23
+ hostname: TraceContext.getHostname(),
24
+ };
25
+ _storage
26
+ .writeEntry({
27
+ uuid: crypto.randomUUID(),
28
+ batchId: TraceContext.getBatchId(),
29
+ type: EntryType.CLIENT_REQUEST,
30
+ content,
31
+ tags,
32
+ isLatest: true,
33
+ createdAt: TraceContext.now(),
34
+ })
35
+ .catch(() => undefined);
36
+ };
37
+ export const HttpClientWatcher = Object.freeze({
38
+ emit,
39
+ register({ storage, config }) {
40
+ if (config.watchers.clientRequest === false)
41
+ return () => undefined;
42
+ _storage = storage;
43
+ _redactHeaderNames = config.redaction?.headers ?? [];
44
+ _ignoreRoutes = config.ignoreRoutes;
45
+ return () => {
46
+ _storage = null;
47
+ _ignoreRoutes = [];
48
+ };
49
+ },
50
+ });
@@ -0,0 +1,2 @@
1
+ import type { ITraceWatcher } from '../types';
2
+ export declare const HttpWatcher: ITraceWatcher;
@@ -0,0 +1,71 @@
1
+ import { TraceContext } from '../context';
2
+ import { EntryType } from '../types';
3
+ import { AuthTag } from '../utils/authTag';
4
+ import { redactHeaders, redactObject } from '../utils/redact';
5
+ import { RequestFilter } from '../utils/requestFilter';
6
+ const normalizeHeaders = (headers) => {
7
+ if (!headers)
8
+ return {};
9
+ return Object.fromEntries(Object.entries(headers).flatMap(([key, value]) => {
10
+ if (typeof value === 'string')
11
+ return [[key, value]];
12
+ if (Array.isArray(value))
13
+ return [[key, value.join(', ')]];
14
+ return [];
15
+ }));
16
+ };
17
+ const buildEntry = (req, res, start, config) => {
18
+ const headers = redactHeaders(normalizeHeaders(req.headers), config.redaction.headers);
19
+ const payload = req.body ? redactObject(req.body, config.redaction.body) : {};
20
+ return {
21
+ method: req.getMethod(),
22
+ uri: req.getPath(),
23
+ headers,
24
+ payload,
25
+ responseStatus: res.getStatus(),
26
+ responseHeaders: {},
27
+ duration: Date.now() - start,
28
+ memory: TraceContext.getMemory(),
29
+ middleware: [],
30
+ hostname: TraceContext.getHostname(),
31
+ userId: TraceContext.getUserId(),
32
+ };
33
+ };
34
+ const shouldIgnore = (req, config) => {
35
+ return RequestFilter.matchesIgnoredPath(req.getPath(), config.ignoreRoutes);
36
+ };
37
+ const isWatcherEnabled = (config) => config.watchers.request !== false;
38
+ export const HttpWatcher = Object.freeze({
39
+ register({ storage, config, registerMiddleware }) {
40
+ if (!isWatcherEnabled(config))
41
+ return () => undefined;
42
+ if (!registerMiddleware)
43
+ return () => undefined;
44
+ const middleware = async (req, res, next) => {
45
+ const request = req;
46
+ const response = res;
47
+ if (shouldIgnore(request, config))
48
+ return next();
49
+ const start = TraceContext.now();
50
+ const batchId = TraceContext.getBatchId();
51
+ await next();
52
+ const content = buildEntry(request, response, start, config);
53
+ const tags = AuthTag.append([]);
54
+ if (content.responseStatus >= 500)
55
+ tags.push('failed');
56
+ storage
57
+ .writeEntry({
58
+ uuid: crypto.randomUUID(),
59
+ batchId,
60
+ type: EntryType.REQUEST,
61
+ content,
62
+ tags,
63
+ isLatest: true,
64
+ createdAt: TraceContext.now(),
65
+ })
66
+ .catch(() => undefined); // fire-and-forget
67
+ };
68
+ registerMiddleware(middleware);
69
+ return () => undefined;
70
+ },
71
+ });
@@ -0,0 +1,10 @@
1
+ import type { ITraceWatcher } from '../types';
2
+ declare const emitDispatch: (name: string, queue: string, connection: string, data?: unknown) => void;
3
+ declare const emitProcessed: (name: string) => void;
4
+ declare const emitFailed: (name: string, error: Error) => void;
5
+ export declare const JobWatcher: ITraceWatcher & {
6
+ onDispatch: typeof emitDispatch;
7
+ onProcessed: typeof emitProcessed;
8
+ onFailed: typeof emitFailed;
9
+ };
10
+ export {};