@glubean/browser 0.1.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.
@@ -0,0 +1,176 @@
1
+ /**
2
+ * CDP Network domain listener for auto-tracing in-page requests.
3
+ *
4
+ * Intercepts XHR, fetch, and document requests inside the browser page and
5
+ * emits them as Glubean trace events so they appear in the same timeline as
6
+ * `ctx.http` calls.
7
+ *
8
+ * @module network
9
+ */
10
+ const SKIP_PROTOCOLS = ["data:", "chrome-extension:", "devtools:", "blob:"];
11
+ /** Default content-type prefixes to include in traces. */
12
+ const DEFAULT_INCLUDE = ["application/json", "text/html"];
13
+ /** Default URL paths to skip (browser-initiated noise). */
14
+ const DEFAULT_EXCLUDE_PATHS = [
15
+ "/favicon.ico",
16
+ "/favicon.png",
17
+ "/apple-touch-icon.png",
18
+ "/apple-touch-icon-precomposed.png",
19
+ ];
20
+ /** Max response body size to capture (bytes). Larger bodies are truncated. */
21
+ const MAX_BODY_BYTES = 64 * 1024;
22
+ /** @internal Exported for testing. */
23
+ export function shouldSkipProtocol(url) {
24
+ for (const proto of SKIP_PROTOCOLS) {
25
+ if (url.startsWith(proto))
26
+ return true;
27
+ }
28
+ return false;
29
+ }
30
+ /** @internal Exported for testing. */
31
+ export function shouldSkipPath(url, excludePaths) {
32
+ try {
33
+ const pathname = new URL(url).pathname;
34
+ for (const p of excludePaths) {
35
+ if (pathname === p)
36
+ return true;
37
+ }
38
+ }
39
+ catch {
40
+ // invalid URL, don't skip
41
+ }
42
+ return false;
43
+ }
44
+ /** @internal Exported for testing. */
45
+ export function shouldInclude(contentType, include) {
46
+ const ct = contentType.toLowerCase();
47
+ for (const prefix of include) {
48
+ if (ct.startsWith(prefix))
49
+ return true;
50
+ }
51
+ return false;
52
+ }
53
+ /**
54
+ * Attach a CDP Network listener to the page that emits Glubean trace events
55
+ * for every in-page network request.
56
+ *
57
+ * Returns a cleanup function that detaches the listener.
58
+ */
59
+ export async function attachNetworkTracer(page, options) {
60
+ const { trace, filter, include = DEFAULT_INCLUDE, excludePaths = DEFAULT_EXCLUDE_PATHS } = options;
61
+ const cdp = await page.createCDPSession();
62
+ await cdp.send("Network.enable");
63
+ const pending = new Map();
64
+ // ── requestWillBeSent: capture request info ─────────────────────────
65
+ const onRequestWillBeSent = (params) => {
66
+ pending.set(params.requestId, {
67
+ method: params.request.method,
68
+ url: params.request.url,
69
+ startMs: params.timestamp * 1000,
70
+ requestBody: params.request.postData,
71
+ });
72
+ };
73
+ // ── responseReceived: capture response metadata (body not yet ready) ─
74
+ const onResponseReceived = (params) => {
75
+ const req = pending.get(params.requestId);
76
+ if (!req)
77
+ return;
78
+ req.status = params.response.status;
79
+ req.contentType = params.response.mimeType || "";
80
+ req.responseTimestamp = params.timestamp;
81
+ };
82
+ // ── loadingFinished: body available, emit trace ─────────────────────
83
+ const onLoadingFinished = async (params) => {
84
+ const req = pending.get(params.requestId);
85
+ if (!req || req.status === undefined)
86
+ return;
87
+ pending.delete(params.requestId);
88
+ // Always skip non-HTTP protocols
89
+ if (shouldSkipProtocol(req.url))
90
+ return;
91
+ const contentType = req.contentType;
92
+ const status = req.status;
93
+ // Apply filter: custom predicate > default path + include checks
94
+ if (filter) {
95
+ if (!filter({ url: req.url, contentType, status }))
96
+ return;
97
+ }
98
+ else {
99
+ if (shouldSkipPath(req.url, excludePaths))
100
+ return;
101
+ if (!shouldInclude(contentType, include))
102
+ return;
103
+ }
104
+ const duration = Math.round((req.responseTimestamp ?? params.timestamp) * 1000 - req.startMs);
105
+ // Always capture response body for traced requests. Body filtering
106
+ // and redaction happen downstream in the runner/CLI pipeline.
107
+ let responseBody;
108
+ try {
109
+ const result = await cdp.send("Network.getResponseBody", {
110
+ requestId: params.requestId,
111
+ });
112
+ if (!result.base64Encoded) {
113
+ const raw = result.body.length > MAX_BODY_BYTES
114
+ ? result.body.slice(0, MAX_BODY_BYTES) + "…[truncated]"
115
+ : result.body;
116
+ try {
117
+ responseBody = JSON.parse(raw);
118
+ }
119
+ catch {
120
+ responseBody = raw;
121
+ }
122
+ }
123
+ }
124
+ catch {
125
+ // Body not available (cached, redirected, etc.) — skip silently.
126
+ }
127
+ // Parse request body as JSON if possible.
128
+ let requestBody;
129
+ if (req.requestBody) {
130
+ try {
131
+ requestBody = JSON.parse(req.requestBody);
132
+ }
133
+ catch {
134
+ requestBody = req.requestBody;
135
+ }
136
+ }
137
+ trace({
138
+ name: `[browser] ${req.method} ${shortPath(req.url)}`,
139
+ method: req.method,
140
+ url: req.url,
141
+ status,
142
+ duration,
143
+ ...(requestBody !== undefined && { requestBody }),
144
+ ...(responseBody !== undefined && { responseBody }),
145
+ });
146
+ };
147
+ const onLoadingFailed = (params) => {
148
+ pending.delete(params.requestId);
149
+ };
150
+ cdp.on("Network.requestWillBeSent", onRequestWillBeSent);
151
+ cdp.on("Network.responseReceived", onResponseReceived);
152
+ cdp.on("Network.loadingFinished", onLoadingFinished);
153
+ cdp.on("Network.loadingFailed", onLoadingFailed);
154
+ return async () => {
155
+ cdp.off("Network.requestWillBeSent", onRequestWillBeSent);
156
+ cdp.off("Network.responseReceived", onResponseReceived);
157
+ cdp.off("Network.loadingFinished", onLoadingFinished);
158
+ cdp.off("Network.loadingFailed", onLoadingFailed);
159
+ try {
160
+ await cdp.detach();
161
+ }
162
+ catch {
163
+ // page may already be closed
164
+ }
165
+ };
166
+ }
167
+ function shortPath(url) {
168
+ try {
169
+ const u = new URL(url);
170
+ return u.pathname + u.search;
171
+ }
172
+ catch {
173
+ return url;
174
+ }
175
+ }
176
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}