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.
- package/dist/debugger.d.ts +1 -0
- package/dist/hypha-debugger.js +144 -9
- package/dist/hypha-debugger.js.map +1 -1
- package/dist/hypha-debugger.min.js +2 -2
- package/dist/hypha-debugger.min.js.map +1 -1
- package/dist/hypha-debugger.mjs +143 -10
- package/dist/hypha-debugger.mjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/services/navigate.d.ts +14 -0
- package/package.json +1 -1
package/dist/hypha-debugger.mjs
CHANGED
|
@@ -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
|
|
2176
|
-
*
|
|
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 (
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|