@mochi.js/core 0.3.0 → 0.8.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/src/proxy-auth.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Proxy authentication via CDP `Fetch.authRequired`.
2
+ * Proxy URL parsing helpers + (legacy) `Fetch.authRequired` installer.
3
3
  *
4
4
  * Background
5
5
  * ----------
@@ -11,16 +11,14 @@
11
11
  * weirdness, observable extension ids) and so is forbidden by mochi's
12
12
  * stealth invariants.
13
13
  *
14
- * The CDP path is invariant-clean: enable `Fetch` with `handleAuthRequests`
15
- * AND a wildcard pattern. Chromium rejects `patterns: []` when
16
- * `handleAuthRequests: true` (`-32602 Can't specify empty patterns with
17
- * handleAuth set`, verified on CfT linux ~2026-05) the original 0160
18
- * design assumed empty patterns would only fire `Fetch.authRequired`
19
- * events, but modern Chromium requires at least one URL pattern when auth
20
- * handling is on. We use `[{ urlPattern: "*" }]` and forward every paused
21
- * request immediately via `Fetch.continueRequest`. The auth challenges
22
- * separately fire `Fetch.authRequired`; we answer those with
23
- * `Fetch.continueWithAuth` carrying the parsed credentials.
14
+ * Task 0266 unifies proxy auth + init-script delivery under a single
15
+ * `Fetch.enable` call (see {@link installInitInjector} in
16
+ * `cdp/init-injector.ts`). `Session` now installs ONE Fetch handler that
17
+ * owns BOTH the document-body splice (for the inject payload) AND
18
+ * `Fetch.authRequired` answering (when proxy creds are present). The
19
+ * legacy {@link installProxyAuth} export below is preserved as a thin
20
+ * delegating wrapper for any out-of-tree caller still wiring it directly,
21
+ * but the session no longer uses it.
24
22
  *
25
23
  * PLAN.md §8.2 invariant check
26
24
  * ----------------------------
@@ -30,11 +28,7 @@
30
28
  * isolated world) are forbidden. `Fetch.enable` operates at the network
31
29
  * layer below page script — it does not produce execution-context-creation
32
30
  * events, does not surface a `chrome.devtools` global, and is not
33
- * detectable from page JavaScript. With `patterns: [{urlPattern: "*"}]`
34
- * every request pauses for one CDP round-trip before continuing — that's
35
- * a measurable but bounded overhead (sub-ms per request on modern
36
- * hardware) and only active on sessions with proxy auth credentials
37
- * (the function early-returns when `auth` is undefined).
31
+ * detectable from page JavaScript.
38
32
  *
39
33
  * Protocols
40
34
  * ---------
@@ -45,10 +39,10 @@
45
39
  * handler covers both.
46
40
  *
47
41
  * @see PLAN.md §8.2 / §10
48
- * @see tasks/0160-proxy-auth-and-ci-fix.md
49
42
  */
50
43
 
51
- import type { MessageRouter, Unsubscribe } from "./cdp/router";
44
+ import { installInitInjector } from "./cdp/init-injector";
45
+ import type { MessageRouter } from "./cdp/router";
52
46
 
53
47
  /** Parsed proxy URL — what `parseProxyUrl` returns. */
54
48
  export interface ParsedProxy {
@@ -148,105 +142,28 @@ export interface ProxyAuthHandle {
148
142
  }
149
143
 
150
144
  /**
151
- * Wire proxy-auth handling into a {@link MessageRouter}. No-op when
152
- * `auth` is undefined saves the `Fetch.enable` round-trip and avoids
153
- * any protocol surface for sessions that don't need it.
145
+ * Wire proxy-auth handling into a {@link MessageRouter}. Thin compatibility
146
+ * shim delegates to {@link installInitInjector} with `payloadCode: null`
147
+ * so the proxy-auth-only call path still works for any out-of-tree caller.
154
148
  *
155
- * Behavior:
149
+ * The Session no longer uses this directly; proxy auth and
150
+ * init-script delivery share a single `Fetch.enable` owner.
151
+ *
152
+ * Behavior (unchanged contract):
156
153
  * - Sends `Fetch.enable { handleAuthRequests: true, patterns: [{
157
- * urlPattern: "*" }] }` once.
154
+ * urlPattern: "*", resourceType: "Document" }, { urlPattern: "*" }] }`.
158
155
  * - On `Fetch.authRequired`, replies with `Fetch.continueWithAuth` and
159
156
  * the parsed creds.
160
157
  * - On `Fetch.requestPaused`, forwards `Fetch.continueRequest`
161
- * immediately so the network model stays unchanged (every request
162
- * still flows; we just take one CDP round-trip to wave it through).
158
+ * immediately (no body splice when `payloadCode` is null).
163
159
  *
164
- * Why wildcard patterns instead of empty: modern Chromium (CfT linux
165
- * ~2026-05) rejects `patterns: []` when `handleAuthRequests: true` is set
166
- * with `-32602 Can't specify empty patterns with handleAuth set`. The
167
- * wildcard plus an immediate-continue handler is the equivalent of
168
- * "auth-only interception" with one extra round-trip per request — only
169
- * active on proxy-authed sessions.
160
+ * @deprecated Prefer {@link installInitInjector} directly. This wrapper is
161
+ * preserved only for backward compatibility.
170
162
  */
171
163
  export async function installProxyAuth(
172
164
  router: MessageRouter,
173
165
  auth: { username: string; password: string },
174
166
  ): Promise<ProxyAuthHandle> {
175
- // Subscribe FIRST so we don't miss the very first authRequired event the
176
- // browser fires after Fetch.enable.
177
- const offAuth: Unsubscribe = router.on("Fetch.authRequired", (params) => {
178
- const requestId = (params as { requestId?: string } | null)?.requestId;
179
- if (typeof requestId !== "string") return;
180
- // Fire-and-forget — failures here are non-fatal (the request will
181
- // simply 407 and the page-level fetch will see it). We log on
182
- // unexpected errors so users can diagnose creds issues.
183
- router
184
- .send("Fetch.continueWithAuth", {
185
- requestId,
186
- authChallengeResponse: {
187
- response: "ProvideCredentials",
188
- username: auth.username,
189
- password: auth.password,
190
- },
191
- })
192
- .catch((err: unknown) => {
193
- if (!isClosedError(err)) {
194
- console.warn("[mochi] Fetch.continueWithAuth failed:", err);
195
- }
196
- });
197
- });
198
-
199
- // Pattern is REQUIRED with handleAuthRequests: true. Modern Chromium
200
- // rejects an empty `patterns` array with `-32602 Can't specify empty
201
- // patterns with handleAuth set` (verified on CfT linux ~2026-05). Use
202
- // a wildcard pattern so every request paus es, then immediately
203
- // forward in the requestPaused handler below — that gets us auth
204
- // challenge interception without altering the user-visible network
205
- // model. The per-request CDP round-trip is real overhead but only
206
- // active when the session has proxy auth credentials (this whole
207
- // function early-returns when `auth` is undefined), so non-proxied
208
- // sessions pay zero cost.
209
- const offPaused: Unsubscribe = router.on("Fetch.requestPaused", (params) => {
210
- const requestId = (params as { requestId?: string } | null)?.requestId;
211
- if (typeof requestId !== "string") return;
212
- router.send("Fetch.continueRequest", { requestId }).catch((err: unknown) => {
213
- if (!isClosedError(err)) {
214
- console.warn("[mochi] Fetch.continueRequest failed:", err);
215
- }
216
- });
217
- });
218
-
219
- await router.send("Fetch.enable", {
220
- handleAuthRequests: true,
221
- patterns: [{ urlPattern: "*" }],
222
- });
223
-
224
- let disposed = false;
225
- return {
226
- async dispose(): Promise<void> {
227
- if (disposed) return;
228
- disposed = true;
229
- offAuth();
230
- offPaused();
231
- try {
232
- await router.send("Fetch.disable");
233
- } catch (err) {
234
- // Closed-pipe failures are expected during session teardown.
235
- if (!isClosedError(err)) {
236
- console.warn("[mochi] Fetch.disable failed:", err);
237
- }
238
- }
239
- },
240
- };
241
- }
242
-
243
- /** True when an error reflects the transport already being closed. */
244
- function isClosedError(err: unknown): boolean {
245
- if (err instanceof Error) {
246
- return (
247
- err.name === "BrowserCrashedError" ||
248
- /transport already closed|pipe closed|browser process exited/i.test(err.message)
249
- );
250
- }
251
- return false;
167
+ const handle = await installInitInjector(router, { payloadCode: null, auth });
168
+ return handle;
252
169
  }