@detiq/crawler 1.1.9 → 1.1.10
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/accessibility-auditor.js +54 -74
- package/dist/ai-login.js +227 -249
- package/dist/auth-token-rotator.js +57 -67
- package/dist/auth.js +299 -336
- package/dist/browser-extension.js +104 -142
- package/dist/captcha-solver.js +98 -125
- package/dist/color-contrast.js +77 -89
- package/dist/content-chunker.js +126 -126
- package/dist/content-dedup.js +54 -71
- package/dist/cookie-consent.js +68 -83
- package/dist/cookie-jar.js +53 -60
- package/dist/crawler.js +4494 -5353
- package/dist/custom-js-injector.js +45 -61
- package/dist/dark-mode-capture.js +67 -81
- package/dist/dead-link-detector.js +47 -55
- package/dist/element-hash.js +8 -12
- package/dist/email-otp.js +154 -195
- package/dist/engine-router.js +87 -98
- package/dist/export/bulk-export.js +70 -55
- package/dist/export/vector-export.js +77 -105
- package/dist/extractors/advanced.js +249 -266
- package/dist/extractors/artifact-envelope.js +59 -58
- package/dist/failure-analysis-agent.js +37 -33
- package/dist/feature-flag-detector.js +51 -66
- package/dist/form-detector.js +55 -75
- package/dist/frontier.js +76 -101
- package/dist/gap-pass.js +68 -89
- package/dist/graphql-detector.js +62 -72
- package/dist/http-engine.js +282 -330
- package/dist/incremental-diff.js +54 -87
- package/dist/index.js +67 -28
- package/dist/input-fuzzer.js +93 -97
- package/dist/interactive-probe.js +715 -827
- package/dist/job-queue.js +2151 -2169
- package/dist/js-coverage.js +52 -56
- package/dist/js-router-detector.js +86 -117
- package/dist/language-detector.js +51 -63
- package/dist/link-classifier.js +32 -50
- package/dist/live-view.js +91 -108
- package/dist/locator-extractor.js +151 -187
- package/dist/logger.js +9 -7
- package/dist/markdown-extract.js +48 -61
- package/dist/native-crawler.js +780 -946
- package/dist/network-observer.js +135 -156
- package/dist/network-throttle.js +29 -40
- package/dist/openapi-generator.js +77 -92
- package/dist/pdf-extractor.js +72 -90
- package/dist/performance-metrics.js +80 -102
- package/dist/phash.js +125 -144
- package/dist/pii-scrubber.js +65 -68
- package/dist/playwright-recorder.js +70 -58
- package/dist/policies/crawl-safety.js +31 -34
- package/dist/profiles/domain-profile.js +130 -125
- package/dist/proxy.js +146 -172
- package/dist/rate-limiter.js +58 -65
- package/dist/redirect-tracker.js +60 -64
- package/dist/redis-frontier.js +116 -136
- package/dist/redis-queue.js +157 -144
- package/dist/response-cache.js +127 -140
- package/dist/responsive-detector.js +42 -46
- package/dist/retry-policy.js +46 -50
- package/dist/robots-txt.js +69 -83
- package/dist/screen-reader.js +45 -70
- package/dist/screenshot-masker.js +59 -77
- package/dist/seo-checker.js +88 -107
- package/dist/session-capture.js +192 -212
- package/dist/session-export.js +107 -133
- package/dist/sitemap-writer.js +38 -41
- package/dist/sitemap.js +141 -202
- package/dist/skyvern.js +123 -148
- package/dist/sqs-queue.js +42 -42
- package/dist/sso-token.js +74 -90
- package/dist/stealth/human-behavior.js +156 -173
- package/dist/telemetry.js +68 -82
- package/dist/test-executor.js +309 -335
- package/dist/tls-fingerprint.js +77 -88
- package/dist/url-pattern-completer.js +71 -90
- package/dist/visual-diff-verdict.js +23 -38
- package/dist/webhook-notifier.js +71 -69
- package/dist/websocket-detector.js +56 -68
- package/dist/worker.js +29 -38
- package/package.json +1 -1
|
@@ -1,186 +1,169 @@
|
|
|
1
|
-
// Gaussian random using Box-Muller transform
|
|
2
1
|
function gaussian(mean, sigma) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
const u1 = Math.random();
|
|
3
|
+
const u2 = Math.random();
|
|
4
|
+
const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
5
|
+
return mean + z * sigma;
|
|
7
6
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
async function humanDelay(min = 80, max = 450) {
|
|
8
|
+
const mean = (min + max) / 2;
|
|
9
|
+
const sigma = (max - min) / 6;
|
|
10
|
+
const delay = Math.max(min, Math.min(max, gaussian(mean, sigma)));
|
|
11
|
+
await new Promise((r) => setTimeout(r, Math.round(delay)));
|
|
13
12
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
await mouse.move(x + (Math.random() - 0.5) * 2, y + (Math.random() - 0.5) * 2);
|
|
29
|
-
if (i < steps)
|
|
30
|
-
await humanDelay(8, 25);
|
|
31
|
-
}
|
|
32
|
-
// Small overshoot correction
|
|
33
|
-
await mouse.move(targetX + (Math.random() - 0.5) * 4, targetY + (Math.random() - 0.5) * 4);
|
|
34
|
-
await humanDelay(30, 80);
|
|
35
|
-
await mouse.move(targetX, targetY);
|
|
13
|
+
async function simulateMouseMovement(page, targetX, targetY) {
|
|
14
|
+
try {
|
|
15
|
+
const mouse = page.mouse;
|
|
16
|
+
const steps = 8 + Math.floor(Math.random() * 8);
|
|
17
|
+
const startX = targetX - 100 + Math.random() * 200;
|
|
18
|
+
const startY = targetY - 100 + Math.random() * 200;
|
|
19
|
+
const cpX = (startX + targetX) / 2 + (Math.random() - 0.5) * 80;
|
|
20
|
+
const cpY = (startY + targetY) / 2 + (Math.random() - 0.5) * 80;
|
|
21
|
+
for (let i = 0; i <= steps; i++) {
|
|
22
|
+
const t = i / steps;
|
|
23
|
+
const x = (1 - t) * (1 - t) * startX + 2 * (1 - t) * t * cpX + t * t * targetX;
|
|
24
|
+
const y = (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * cpY + t * t * targetY;
|
|
25
|
+
await mouse.move(x + (Math.random() - 0.5) * 2, y + (Math.random() - 0.5) * 2);
|
|
26
|
+
if (i < steps) await humanDelay(8, 25);
|
|
36
27
|
}
|
|
37
|
-
|
|
28
|
+
await mouse.move(targetX + (Math.random() - 0.5) * 4, targetY + (Math.random() - 0.5) * 4);
|
|
29
|
+
await humanDelay(30, 80);
|
|
30
|
+
await mouse.move(targetX, targetY);
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
38
33
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
catch { /* non-fatal */ }
|
|
34
|
+
async function simulateMouseHover(page, selector) {
|
|
35
|
+
try {
|
|
36
|
+
const el = await page.$(selector);
|
|
37
|
+
if (!el) return;
|
|
38
|
+
const box = await el.boundingBox();
|
|
39
|
+
if (!box) return;
|
|
40
|
+
const cx = box.x + box.width / 2;
|
|
41
|
+
const cy = box.y + box.height / 2;
|
|
42
|
+
await simulateMouseMovement(page, cx, cy);
|
|
43
|
+
await humanDelay(100, 300);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
53
46
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
async function simulateClick(page, selector) {
|
|
48
|
+
try {
|
|
49
|
+
await simulateMouseHover(page, selector);
|
|
50
|
+
await humanDelay(50, 150);
|
|
51
|
+
await page.click(selector, { delay: Math.round(gaussian(80, 30)) });
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
61
54
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
imageData.data[i] = Math.max(0, Math.min(255, imageData.data[i] + (Math.random() < 0.01 ? (Math.random() > 0.5 ? 1 : -1) : 0)));
|
|
72
|
-
}
|
|
73
|
-
ctx.putImageData(imageData, 0, 0);
|
|
74
|
-
}
|
|
75
|
-
return origToDataURL.apply(this, args);
|
|
76
|
-
};
|
|
77
|
-
// WebGL parameter spoofing
|
|
78
|
-
const origGetParam = WebGLRenderingContext.prototype.getParameter;
|
|
79
|
-
WebGLRenderingContext.prototype.getParameter = function (parameter) {
|
|
80
|
-
const UNMASKED_VENDOR_WEBGL = 0x9245;
|
|
81
|
-
const UNMASKED_RENDERER_WEBGL = 0x9246;
|
|
82
|
-
if (parameter === UNMASKED_VENDOR_WEBGL)
|
|
83
|
-
return 'Intel Inc.';
|
|
84
|
-
if (parameter === UNMASKED_RENDERER_WEBGL)
|
|
85
|
-
return 'Intel Iris OpenGL Engine';
|
|
86
|
-
return origGetParam.call(this, parameter);
|
|
87
|
-
};
|
|
88
|
-
// Hardware concurrency — pick from common values
|
|
89
|
-
const cpuCounts = [4, 6, 8, 8];
|
|
90
|
-
Object.defineProperty(navigator, 'hardwareConcurrency', {
|
|
91
|
-
get: () => cpuCounts[Math.floor(Math.random() * cpuCounts.length)],
|
|
92
|
-
configurable: true,
|
|
93
|
-
});
|
|
94
|
-
// Device memory
|
|
95
|
-
Object.defineProperty(navigator, 'deviceMemory', {
|
|
96
|
-
get: () => [4, 8][Math.floor(Math.random() * 2)],
|
|
97
|
-
configurable: true,
|
|
98
|
-
});
|
|
99
|
-
}).catch(() => { });
|
|
100
|
-
}
|
|
101
|
-
export async function infiniteScrollToBottom(page, opts = {}) {
|
|
102
|
-
const { maxScrolls = 50, scrollDelay = 400, stabilizeMs = 600 } = opts;
|
|
103
|
-
let scrollCount = 0;
|
|
104
|
-
let stableScrollCount = 0;
|
|
105
|
-
let previousHeight = 0;
|
|
106
|
-
try {
|
|
107
|
-
while (scrollCount < maxScrolls) {
|
|
108
|
-
previousHeight = await page.evaluate(() => document.documentElement.scrollHeight).catch(() => 0);
|
|
109
|
-
const scrollBy = 500 + Math.floor(Math.random() * 300);
|
|
110
|
-
await page.evaluate((by) => window.scrollBy(0, by), scrollBy).catch(() => { });
|
|
111
|
-
await humanDelay(scrollDelay - 100, scrollDelay + 200);
|
|
112
|
-
// Wait for potential lazy-loaded content
|
|
113
|
-
await page.waitForTimeout(stabilizeMs).catch(() => { });
|
|
114
|
-
scrollCount++;
|
|
115
|
-
const newHeight = await page.evaluate(() => document.documentElement.scrollHeight).catch(() => 0);
|
|
116
|
-
if (newHeight - previousHeight < 10) {
|
|
117
|
-
stableScrollCount++;
|
|
118
|
-
if (stableScrollCount >= 3)
|
|
119
|
-
break; // no meaningful new content after 3 consecutive non-growing scrolls
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
stableScrollCount = 0;
|
|
123
|
-
}
|
|
124
|
-
previousHeight = newHeight;
|
|
55
|
+
async function injectFingerprintOverrides(page) {
|
|
56
|
+
await page.addInitScript(() => {
|
|
57
|
+
const origToDataURL = HTMLCanvasElement.prototype.toDataURL;
|
|
58
|
+
HTMLCanvasElement.prototype.toDataURL = function(...args) {
|
|
59
|
+
const ctx = this.getContext("2d");
|
|
60
|
+
if (ctx) {
|
|
61
|
+
const imageData = ctx.getImageData(0, 0, this.width, this.height);
|
|
62
|
+
for (let i = 0; i < imageData.data.length; i += 4) {
|
|
63
|
+
imageData.data[i] = Math.max(0, Math.min(255, imageData.data[i] + (Math.random() < 0.01 ? Math.random() > 0.5 ? 1 : -1 : 0)));
|
|
125
64
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
65
|
+
ctx.putImageData(imageData, 0, 0);
|
|
66
|
+
}
|
|
67
|
+
return origToDataURL.apply(this, args);
|
|
68
|
+
};
|
|
69
|
+
const origGetParam = WebGLRenderingContext.prototype.getParameter;
|
|
70
|
+
WebGLRenderingContext.prototype.getParameter = function(parameter) {
|
|
71
|
+
const UNMASKED_VENDOR_WEBGL = 37445;
|
|
72
|
+
const UNMASKED_RENDERER_WEBGL = 37446;
|
|
73
|
+
if (parameter === UNMASKED_VENDOR_WEBGL) return "Intel Inc.";
|
|
74
|
+
if (parameter === UNMASKED_RENDERER_WEBGL) return "Intel Iris OpenGL Engine";
|
|
75
|
+
return origGetParam.call(this, parameter);
|
|
76
|
+
};
|
|
77
|
+
const cpuCounts = [4, 6, 8, 8];
|
|
78
|
+
Object.defineProperty(navigator, "hardwareConcurrency", {
|
|
79
|
+
get: () => cpuCounts[Math.floor(Math.random() * cpuCounts.length)],
|
|
80
|
+
configurable: true
|
|
81
|
+
});
|
|
82
|
+
Object.defineProperty(navigator, "deviceMemory", {
|
|
83
|
+
get: () => [4, 8][Math.floor(Math.random() * 2)],
|
|
84
|
+
configurable: true
|
|
85
|
+
});
|
|
86
|
+
}).catch(() => {
|
|
87
|
+
});
|
|
129
88
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
89
|
+
async function infiniteScrollToBottom(page, opts = {}) {
|
|
90
|
+
const { maxScrolls = 50, scrollDelay = 400, stabilizeMs = 600 } = opts;
|
|
91
|
+
let scrollCount = 0;
|
|
92
|
+
let stableScrollCount = 0;
|
|
93
|
+
let previousHeight = 0;
|
|
94
|
+
try {
|
|
95
|
+
while (scrollCount < maxScrolls) {
|
|
96
|
+
previousHeight = await page.evaluate(() => document.documentElement.scrollHeight).catch(() => 0);
|
|
97
|
+
const scrollBy = 500 + Math.floor(Math.random() * 300);
|
|
98
|
+
await page.evaluate((by) => window.scrollBy(0, by), scrollBy).catch(() => {
|
|
99
|
+
});
|
|
100
|
+
await humanDelay(scrollDelay - 100, scrollDelay + 200);
|
|
101
|
+
await page.waitForTimeout(stabilizeMs).catch(() => {
|
|
102
|
+
});
|
|
103
|
+
scrollCount++;
|
|
104
|
+
const newHeight = await page.evaluate(() => document.documentElement.scrollHeight).catch(() => 0);
|
|
105
|
+
if (newHeight - previousHeight < 10) {
|
|
106
|
+
stableScrollCount++;
|
|
107
|
+
if (stableScrollCount >= 3) break;
|
|
108
|
+
} else {
|
|
109
|
+
stableScrollCount = 0;
|
|
110
|
+
}
|
|
111
|
+
previousHeight = newHeight;
|
|
152
112
|
}
|
|
153
|
-
|
|
154
|
-
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
return { scrollCount, heightReached: previousHeight };
|
|
155
116
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
walkShadow(document.body);
|
|
173
|
-
return parts
|
|
174
|
-
.join('\n')
|
|
175
|
-
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
176
|
-
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
177
|
-
.replace(/<[^>]+>/g, ' ')
|
|
178
|
-
.replace(/\s+/g, ' ')
|
|
179
|
-
.trim()
|
|
180
|
-
.slice(0, 10_000);
|
|
181
|
-
}).catch(() => '');
|
|
182
|
-
}
|
|
183
|
-
catch {
|
|
184
|
-
return '';
|
|
117
|
+
async function extractIframeContent(page) {
|
|
118
|
+
const results = [];
|
|
119
|
+
try {
|
|
120
|
+
const iframeSrcs = await page.evaluate(
|
|
121
|
+
() => Array.from(document.querySelectorAll("iframe")).map((f) => f.getAttribute("src") ?? f.getAttribute("data-src") ?? "")
|
|
122
|
+
).catch(() => []);
|
|
123
|
+
for (const frame of page.frames()) {
|
|
124
|
+
const src = frame.url();
|
|
125
|
+
if (!src || src === "about:blank" || src === page.url()) continue;
|
|
126
|
+
try {
|
|
127
|
+
const content = await frame.content().catch(() => "");
|
|
128
|
+
if (!content || content.length < 50) continue;
|
|
129
|
+
const text = content.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
130
|
+
results.push({ src, content: content.slice(0, 1e4), markdown: text.slice(0, 5e3) });
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
185
133
|
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
return results;
|
|
137
|
+
}
|
|
138
|
+
async function pierceShadowDom(page) {
|
|
139
|
+
try {
|
|
140
|
+
return await page.evaluate(() => {
|
|
141
|
+
const parts = [];
|
|
142
|
+
function walkShadow(root) {
|
|
143
|
+
const shadow = root.shadowRoot;
|
|
144
|
+
if (shadow) {
|
|
145
|
+
parts.push(shadow.innerHTML ?? "");
|
|
146
|
+
for (const child of [...shadow.querySelectorAll("*")]) walkShadow(child);
|
|
147
|
+
}
|
|
148
|
+
for (const child of Array.from(root.querySelectorAll(":not(script):not(style)"))) {
|
|
149
|
+
if (child.shadowRoot) walkShadow(child);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
walkShadow(document.body);
|
|
153
|
+
return parts.join("\n").replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 1e4);
|
|
154
|
+
}).catch(() => "");
|
|
155
|
+
} catch {
|
|
156
|
+
return "";
|
|
157
|
+
}
|
|
186
158
|
}
|
|
159
|
+
export {
|
|
160
|
+
extractIframeContent,
|
|
161
|
+
humanDelay,
|
|
162
|
+
infiniteScrollToBottom,
|
|
163
|
+
injectFingerprintOverrides,
|
|
164
|
+
pierceShadowDom,
|
|
165
|
+
simulateClick,
|
|
166
|
+
simulateMouseHover,
|
|
167
|
+
simulateMouseMovement
|
|
168
|
+
};
|
|
169
|
+
//# sourceMappingURL=human-behavior.js.map
|
package/dist/telemetry.js
CHANGED
|
@@ -1,91 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
* Lightweight OTel-compatible tracing for Zeta crawler.
|
|
3
|
-
*
|
|
4
|
-
* Emits spans as structured JSON logs compatible with OpenTelemetry format.
|
|
5
|
-
* Drop-in upgrade path: replace this module with @opentelemetry/sdk-node when needed.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* const span = startSpan('crawl.url', { url, engine });
|
|
9
|
-
* // ... do work ...
|
|
10
|
-
* endSpan(span, { screensFound: 3 });
|
|
11
|
-
*/
|
|
12
|
-
import { randomBytes } from 'node:crypto';
|
|
13
|
-
// Active trace context — per async execution context (simplified: per-process global)
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
14
2
|
let currentTraceId = null;
|
|
15
3
|
let currentSpanId = null;
|
|
16
4
|
function genId(bytes) {
|
|
17
|
-
|
|
5
|
+
return randomBytes(bytes).toString("hex");
|
|
18
6
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
currentSpanId = spanId;
|
|
27
|
-
return { traceId, spanId, parentSpanId, name, startTimeMs: Date.now(), attributes };
|
|
7
|
+
function startSpan(name, attributes = {}) {
|
|
8
|
+
const traceId = currentTraceId ?? genId(16);
|
|
9
|
+
const spanId = genId(8);
|
|
10
|
+
const parentSpanId = currentSpanId ?? void 0;
|
|
11
|
+
if (!currentTraceId) currentTraceId = traceId;
|
|
12
|
+
currentSpanId = spanId;
|
|
13
|
+
return { traceId, spanId, parentSpanId, name, startTimeMs: Date.now(), attributes };
|
|
28
14
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
error: finished.error,
|
|
60
|
-
...finished.attributes,
|
|
61
|
-
}));
|
|
62
|
-
return finished;
|
|
15
|
+
function endSpan(span, extraAttributes = {}, error) {
|
|
16
|
+
const endTimeMs = Date.now();
|
|
17
|
+
const durationMs = endTimeMs - span.startTimeMs;
|
|
18
|
+
const finished = {
|
|
19
|
+
...span,
|
|
20
|
+
attributes: { ...span.attributes, ...extraAttributes },
|
|
21
|
+
endTimeMs,
|
|
22
|
+
durationMs,
|
|
23
|
+
status: error ? "error" : "ok",
|
|
24
|
+
error: error?.message
|
|
25
|
+
};
|
|
26
|
+
if (!span.parentSpanId) {
|
|
27
|
+
currentTraceId = null;
|
|
28
|
+
currentSpanId = span.parentSpanId ?? null;
|
|
29
|
+
} else {
|
|
30
|
+
currentSpanId = span.parentSpanId ?? null;
|
|
31
|
+
}
|
|
32
|
+
console.log(JSON.stringify({
|
|
33
|
+
ts: new Date(endTimeMs).toISOString(),
|
|
34
|
+
level: error ? "error" : "info",
|
|
35
|
+
msg: `span:${finished.name}`,
|
|
36
|
+
trace_id: finished.traceId,
|
|
37
|
+
span_id: finished.spanId,
|
|
38
|
+
parent_span_id: finished.parentSpanId,
|
|
39
|
+
duration_ms: durationMs,
|
|
40
|
+
status: finished.status,
|
|
41
|
+
error: finished.error,
|
|
42
|
+
...finished.attributes
|
|
43
|
+
}));
|
|
44
|
+
return finished;
|
|
63
45
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
throw err;
|
|
75
|
-
}
|
|
46
|
+
async function withSpan(name, attributes, fn) {
|
|
47
|
+
const span = startSpan(name, attributes);
|
|
48
|
+
try {
|
|
49
|
+
const result = await fn(span);
|
|
50
|
+
endSpan(span);
|
|
51
|
+
return result;
|
|
52
|
+
} catch (err) {
|
|
53
|
+
endSpan(span, {}, err);
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
76
56
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
counters.set(key, (counters.get(key) ?? 0) + value);
|
|
57
|
+
const counters = /* @__PURE__ */ new Map();
|
|
58
|
+
function increment(metric, value = 1, tags = {}) {
|
|
59
|
+
const key = metric + (Object.keys(tags).length ? ":" + Object.entries(tags).map(([k, v]) => `${k}=${v}`).join(",") : "");
|
|
60
|
+
counters.set(key, (counters.get(key) ?? 0) + value);
|
|
82
61
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return snapshot;
|
|
62
|
+
function flushMetrics() {
|
|
63
|
+
const snapshot = Object.fromEntries(counters);
|
|
64
|
+
if (Object.keys(snapshot).length > 0) {
|
|
65
|
+
console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level: "info", msg: "metrics:flush", ...snapshot }));
|
|
66
|
+
counters.clear();
|
|
67
|
+
}
|
|
68
|
+
return snapshot;
|
|
91
69
|
}
|
|
70
|
+
export {
|
|
71
|
+
endSpan,
|
|
72
|
+
flushMetrics,
|
|
73
|
+
increment,
|
|
74
|
+
startSpan,
|
|
75
|
+
withSpan
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=telemetry.js.map
|