hypha-debugger 0.1.5 → 0.1.6

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.
@@ -2157,8 +2157,6 @@ function getScriptUrl() {
2157
2157
  }
2158
2158
  /**
2159
2159
  * Inject the debugger loader script into HTML before </body> (or append).
2160
- * Also injects a tiny inline script that reads sessionStorage and passes
2161
- * config to the debugger via a global, so autoStart() picks it up.
2162
2160
  */
2163
2161
  function injectLoader(html, scriptUrl) {
2164
2162
  const loader = `<script src="${scriptUrl}"><\/script>`;
@@ -2172,8 +2170,8 @@ function injectLoader(html, scriptUrl) {
2172
2170
  }
2173
2171
  /**
2174
2172
  * Perform a soft page replacement: fetch HTML, inject debugger script,
2175
- * replace the document. Returns false if it can't be done (caller should
2176
- * fall back to hard navigation).
2173
+ * replace the document via document.write(). If the fetch or write fails,
2174
+ * falls back to hard navigation.
2177
2175
  */
2178
2176
  function softReplace(url, pushState) {
2179
2177
  const scriptUrl = getScriptUrl();
@@ -2211,6 +2209,133 @@ function softReplace(url, pushState) {
2211
2209
  }
2212
2210
  });
2213
2211
  }
2212
+ /** Check if a URL is same-origin as the current page. */
2213
+ function isSameOrigin(url) {
2214
+ try {
2215
+ const target = new URL(url, location.href);
2216
+ return target.origin === location.origin;
2217
+ }
2218
+ catch {
2219
+ return false;
2220
+ }
2221
+ }
2222
+ // ── Global navigation interception ────────────────────────────────────
2223
+ let _interceptInstalled = false;
2224
+ /**
2225
+ * Install global listeners that intercept same-origin link clicks and
2226
+ * form submissions, routing them through soft navigation so the debugger
2227
+ * stays connected.
2228
+ *
2229
+ * Called once from HyphaDebugger.start().
2230
+ */
2231
+ function installNavigationInterceptor() {
2232
+ if (_interceptInstalled)
2233
+ return () => { };
2234
+ _interceptInstalled = true;
2235
+ /**
2236
+ * Click handler: intercept <a> clicks that would navigate to a
2237
+ * same-origin HTML page.
2238
+ */
2239
+ const onClick = (e) => {
2240
+ // Skip if modifier keys (new tab, etc.) or not left-click
2241
+ if (e.defaultPrevented || e.button !== 0)
2242
+ return;
2243
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey)
2244
+ return;
2245
+ // Walk up from target to find the nearest <a>
2246
+ let anchor = null;
2247
+ let el = e.target;
2248
+ while (el) {
2249
+ if (el.tagName === "A") {
2250
+ anchor = el;
2251
+ break;
2252
+ }
2253
+ el = el.parentElement;
2254
+ }
2255
+ if (!anchor)
2256
+ return;
2257
+ const href = anchor.href;
2258
+ if (!href)
2259
+ return;
2260
+ // Skip non-http(s), download links, target=_blank, javascript:, #hash-only
2261
+ if (anchor.target && anchor.target !== "_self")
2262
+ return;
2263
+ if (anchor.hasAttribute("download"))
2264
+ return;
2265
+ if (href.startsWith("javascript:") || href.startsWith("mailto:") || href.startsWith("tel:"))
2266
+ return;
2267
+ // Skip hash-only links (same page anchor)
2268
+ try {
2269
+ const target = new URL(href, location.href);
2270
+ if (target.origin === location.origin &&
2271
+ target.pathname === location.pathname &&
2272
+ target.search === location.search &&
2273
+ target.hash !== location.hash) {
2274
+ return; // Just a hash change, let browser handle it
2275
+ }
2276
+ }
2277
+ catch {
2278
+ return;
2279
+ }
2280
+ // Skip cross-origin
2281
+ if (!isSameOrigin(href))
2282
+ return;
2283
+ // Intercept: prevent default navigation and do soft replace
2284
+ e.preventDefault();
2285
+ const targetUrl = new URL(href, location.href).href;
2286
+ softReplace(targetUrl, targetUrl);
2287
+ };
2288
+ /**
2289
+ * Submit handler: intercept form submissions to same-origin action URLs.
2290
+ * Only handles GET forms (POST forms need the request body which is harder
2291
+ * to replicate via fetch).
2292
+ */
2293
+ const onSubmit = (e) => {
2294
+ if (e.defaultPrevented)
2295
+ return;
2296
+ const form = e.target;
2297
+ const method = (form.method || "GET").toUpperCase();
2298
+ // Only intercept GET forms — POST forms are too complex to replicate
2299
+ if (method !== "GET")
2300
+ return;
2301
+ const action = form.action || location.href;
2302
+ if (!isSameOrigin(action))
2303
+ return;
2304
+ // Build the URL with form data as query params
2305
+ const formData = new FormData(form);
2306
+ const url = new URL(action, location.href);
2307
+ for (const [key, value] of formData.entries()) {
2308
+ if (typeof value === "string") {
2309
+ url.searchParams.set(key, value);
2310
+ }
2311
+ }
2312
+ // Skip if target is _blank or similar
2313
+ if (form.target && form.target !== "_self")
2314
+ return;
2315
+ e.preventDefault();
2316
+ softReplace(url.href, url.href);
2317
+ };
2318
+ /**
2319
+ * Popstate handler: intercept browser back/forward (bfcache miss).
2320
+ * When the browser navigates via back/forward and there's no bfcache,
2321
+ * we can catch it via popstate and do a soft load of the target URL.
2322
+ */
2323
+ const onPopState = () => {
2324
+ // The URL has already changed when popstate fires.
2325
+ // Do a soft load of the current URL (which is the target of back/forward).
2326
+ softReplace(location.href);
2327
+ };
2328
+ document.addEventListener("click", onClick, true); // capture phase
2329
+ document.addEventListener("submit", onSubmit, true);
2330
+ window.addEventListener("popstate", onPopState);
2331
+ // Return cleanup function
2332
+ return () => {
2333
+ document.removeEventListener("click", onClick, true);
2334
+ document.removeEventListener("submit", onSubmit, true);
2335
+ window.removeEventListener("popstate", onPopState);
2336
+ _interceptInstalled = false;
2337
+ };
2338
+ }
2214
2339
  // ── navigate ──────────────────────────────────────────────────────────
2215
2340
  function navigate(url) {
2216
2341
  try {
@@ -2261,7 +2386,7 @@ function goBack() {
2261
2386
  window.history.back();
2262
2387
  return {
2263
2388
  success: true,
2264
- message: "Navigated back (re-click bookmarklet to reconnect if needed)",
2389
+ message: "Navigated back (debugger will auto-reconnect via popstate)",
2265
2390
  };
2266
2391
  }
2267
2392
  catch (err) {
@@ -2273,7 +2398,7 @@ function goBack() {
2273
2398
  }
2274
2399
  goBack.__schema__ = {
2275
2400
  name: "goBack",
2276
- description: "Navigate back in browser history. The debugger may disconnect — re-click the bookmarklet to reconnect.",
2401
+ description: "Navigate back in browser history. The debugger auto-reconnects for same-origin pages.",
2277
2402
  parameters: {
2278
2403
  type: "object",
2279
2404
  properties: {},
@@ -2284,7 +2409,7 @@ function goForward() {
2284
2409
  window.history.forward();
2285
2410
  return {
2286
2411
  success: true,
2287
- message: "Navigated forward (re-click bookmarklet to reconnect if needed)",
2412
+ message: "Navigated forward (debugger will auto-reconnect via popstate)",
2288
2413
  };
2289
2414
  }
2290
2415
  catch (err) {
@@ -2296,7 +2421,7 @@ function goForward() {
2296
2421
  }
2297
2422
  goForward.__schema__ = {
2298
2423
  name: "goForward",
2299
- description: "Navigate forward in browser history. The debugger may disconnect — re-click the bookmarklet to reconnect.",
2424
+ description: "Navigate forward in browser history. The debugger auto-reconnects for same-origin pages.",
2300
2425
  parameters: {
2301
2426
  type: "object",
2302
2427
  properties: {},
@@ -5438,6 +5563,7 @@ class HyphaDebugger {
5438
5563
  this.server = null;
5439
5564
  this.serviceInfo = null;
5440
5565
  this.boundBeforeUnload = null;
5566
+ this.cleanupInterceptor = null;
5441
5567
  const requireToken = config.require_token ?? false;
5442
5568
  // Always append random suffix unless user provided a custom id.
5443
5569
  let serviceId = config.service_id ?? "web-debugger";
@@ -5501,6 +5627,9 @@ class HyphaDebugger {
5501
5627
  this.saveConfigToStorage();
5502
5628
  this.boundBeforeUnload = () => this.saveConfigToStorage();
5503
5629
  window.addEventListener("beforeunload", this.boundBeforeUnload);
5630
+ // Intercept same-origin link clicks, form submits, and popstate
5631
+ // so the debugger survives user-initiated navigation
5632
+ this.cleanupInterceptor = installNavigationInterceptor();
5504
5633
  return session;
5505
5634
  }
5506
5635
  catch (err) {
@@ -5516,11 +5645,15 @@ class HyphaDebugger {
5516
5645
  }
5517
5646
  }
5518
5647
  async destroy() {
5519
- // Remove beforeunload listener
5648
+ // Remove event listeners
5520
5649
  if (this.boundBeforeUnload) {
5521
5650
  window.removeEventListener("beforeunload", this.boundBeforeUnload);
5522
5651
  this.boundBeforeUnload = null;
5523
5652
  }
5653
+ if (this.cleanupInterceptor) {
5654
+ this.cleanupInterceptor();
5655
+ this.cleanupInterceptor = null;
5656
+ }
5524
5657
  try {
5525
5658
  if (this.serviceInfo && this.server) {
5526
5659
  await this.server.unregisterService(this.serviceInfo.id);
@@ -5909,5 +6042,5 @@ if (typeof window !== "undefined") {
5909
6042
  }
5910
6043
  }
5911
6044
 
5912
- export { AICursor, HyphaDebugger, PageController, clickElement$1 as clickElement, clickElementByIndex, disposeController, executeScript, fillInput, generateSkillMd, getBrowserState, getComputedStyles, getConsoleLogs, getElementBounds, getHtml, getPageInfo, getReactTree, goBack, goForward, inputText, installConsoleCapture, navigate, queryDom, reload, removeHighlights, scroll, scrollTo, selectOption, startDebugger, takeScreenshot, wrapFn };
6045
+ export { AICursor, HyphaDebugger, PageController, clickElement$1 as clickElement, clickElementByIndex, disposeController, executeScript, fillInput, generateSkillMd, getBrowserState, getComputedStyles, getConsoleLogs, getElementBounds, getHtml, getPageInfo, getReactTree, goBack, goForward, inputText, installConsoleCapture, installNavigationInterceptor, navigate, queryDom, reload, removeHighlights, scroll, scrollTo, selectOption, softReplace, startDebugger, takeScreenshot, wrapFn };
5913
6046
  //# sourceMappingURL=hypha-debugger.mjs.map