@hanzo/browser-extension 1.7.5 → 1.9.31
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/LICENSE +9 -18
- package/dist/browser-extension/README.md +1 -1
- package/dist/browser-extension/background-firefox.js +4184 -491
- package/dist/browser-extension/background.js +6938 -2413
- package/dist/browser-extension/browser-control.js +652 -18
- package/dist/browser-extension/chrome/background.js +6938 -2413
- package/dist/browser-extension/chrome/content-script.js +1642 -123
- package/dist/browser-extension/chrome/icon128.png +0 -0
- package/dist/browser-extension/chrome/icon16.png +0 -0
- package/dist/browser-extension/chrome/icon48.png +0 -0
- package/dist/browser-extension/chrome/manifest.json +20 -11
- package/dist/browser-extension/chrome/popup.css +352 -110
- package/dist/browser-extension/chrome/popup.html +159 -48
- package/dist/browser-extension/chrome/popup.js +1418 -46
- package/dist/browser-extension/chrome/sidebar.css +772 -156
- package/dist/browser-extension/chrome/sidebar.html +268 -85
- package/dist/browser-extension/chrome/sidebar.js +25846 -84
- package/dist/browser-extension/cli.js +2 -2
- package/dist/browser-extension/content-script.js +1642 -123
- package/dist/browser-extension/firefox/background.js +4184 -491
- package/dist/browser-extension/firefox/content-script.js +1642 -123
- package/dist/browser-extension/firefox/icon128.png +0 -0
- package/dist/browser-extension/firefox/icon16.png +0 -0
- package/dist/browser-extension/firefox/icon48.png +0 -0
- package/dist/browser-extension/firefox/manifest.json +24 -13
- package/dist/browser-extension/firefox/popup.css +352 -110
- package/dist/browser-extension/firefox/popup.html +159 -48
- package/dist/browser-extension/firefox/popup.js +1418 -46
- package/dist/browser-extension/firefox/sidebar.css +772 -156
- package/dist/browser-extension/firefox/sidebar.html +268 -85
- package/dist/browser-extension/firefox/sidebar.js +25846 -84
- package/dist/browser-extension/icon128.png +0 -0
- package/dist/browser-extension/icon16.png +0 -0
- package/dist/browser-extension/icon48.png +0 -0
- package/dist/browser-extension/manifest.json +20 -11
- package/dist/browser-extension/package.json +1 -1
- package/dist/browser-extension/popup.js +1418 -46
- package/dist/browser-extension/safari/Info.plist +3 -3
- package/dist/browser-extension/safari/background.js +6938 -2413
- package/dist/browser-extension/safari/content-script.js +1642 -123
- package/dist/browser-extension/safari/icon128.png +0 -0
- package/dist/browser-extension/safari/icon16.png +0 -0
- package/dist/browser-extension/safari/icon48.png +0 -0
- package/dist/browser-extension/safari/popup.css +352 -110
- package/dist/browser-extension/safari/popup.html +159 -48
- package/dist/browser-extension/safari/popup.js +1418 -46
- package/dist/browser-extension/safari/sidebar.css +772 -156
- package/dist/browser-extension/safari/sidebar.html +268 -85
- package/dist/browser-extension/safari/sidebar.js +25846 -84
- package/dist/browser-extension/sidebar.js +25846 -84
- package/dist/browser-extension/webgpu-ai.js +78 -5
- package/images/icon128.png +0 -0
- package/images/icon16.png +0 -0
- package/images/icon48.png +0 -0
- package/manifest.json +7 -2
- package/package.json +27 -10
- package/src/chat-widget.tsx +96 -0
- package/src/gui-primitives.tsx +116 -0
- package/src/icon128.png +0 -0
- package/src/icon16.png +0 -0
- package/src/icon32.png +0 -0
- package/src/icon48.png +0 -0
- package/src/manifest-firefox.json +23 -12
- package/src/manifest.json +19 -10
- package/src/package.json +1 -1
- package/src/popup.css +352 -110
- package/src/popup.html +159 -48
- package/src/popup.js +196 -0
- package/src/sidebar.css +772 -156
- package/src/sidebar.html +268 -85
- package/src/sidebar.js +885 -0
- package/.github/workflows/docs.yml +0 -44
- package/.github/workflows/release.yml +0 -55
- package/README.md +0 -126
- package/dist/browser-extension/ai-worker.js +0 -30
- package/dist/browser-extension/cdp-bridge-server.js +0 -444
- package/dist/browser-extension/chrome/icon32.png +0 -0
- package/dist/browser-extension/firefox/icon32.png +0 -0
- package/dist/browser-extension/hanzo-browser-extension-firefox-1.1.0.zip +0 -0
- package/dist/browser-extension/icon32.png +0 -0
- package/dist/browser-extension/popup.css +0 -468
- package/dist/browser-extension/popup.html +0 -191
- package/dist/browser-extension/safari/icon32.png +0 -0
- package/dist/browser-extension/sidebar.css +0 -718
- package/dist/browser-extension/sidebar.html +0 -184
- package/dist/cdp-bridge-server.js +0 -411
- package/dist/hanzo-ai-chrome-1.7.2.zip +0 -0
- package/dist/hanzo-ai-chrome-1.7.3.zip +0 -0
- package/dist/hanzo-ai-firefox-1.7.2.zip +0 -0
- package/dist/hanzo-ai-firefox-1.7.3.zip +0 -0
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.pbxproj +0 -1047
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json +0 -11
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -98
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/universal-icon-1024@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/Contents.json +0 -6
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json +0 -21
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/icon128.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Base.lproj/Main.html +0 -20
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Icon.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Script.js +0 -24
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Style.css +0 -61
- package/dist/safari/Hanzo AI/Shared (App)/ViewController.swift +0 -81
- package/dist/safari/Hanzo AI/Shared (Extension)/SafariWebExtensionHandler.swift +0 -42
- package/dist/safari/Hanzo AI/iOS (App)/AppDelegate.swift +0 -24
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/LaunchScreen.storyboard +0 -36
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/Main.storyboard +0 -38
- package/dist/safari/Hanzo AI/iOS (App)/Info.plist +0 -27
- package/dist/safari/Hanzo AI/iOS (App)/SceneDelegate.swift +0 -18
- package/dist/safari/Hanzo AI/iOS (Extension)/Info.plist +0 -13
- package/dist/safari/Hanzo AI/macOS (App)/AppDelegate.swift +0 -21
- package/dist/safari/Hanzo AI/macOS (App)/Base.lproj/Main.storyboard +0 -124
- package/dist/safari/Hanzo AI/macOS (App)/Info.plist +0 -8
- package/dist/safari/Hanzo AI/macOS (Extension)/Info.plist +0 -13
- package/docs/_config.yml +0 -13
- package/docs/_layouts/default.html +0 -435
- package/docs/index.md +0 -54
- package/install-extension.sh +0 -75
- package/test/claude-integration.test.ts +0 -251
- package/test/server-frameworks-edge-cases.test.ts +0 -145
- package/test/server-frameworks.test.ts +0 -395
- package/test-cdp-connection.js +0 -140
- package/tsconfig.json +0 -19
|
@@ -365,6 +365,405 @@ var BrowserControl = class {
|
|
|
365
365
|
`);
|
|
366
366
|
}
|
|
367
367
|
// ===========================================================================
|
|
368
|
+
// DOM Read/Write/Observe
|
|
369
|
+
// ===========================================================================
|
|
370
|
+
async getHTML(tabId, selector, outer = true) {
|
|
371
|
+
return this.executeScript(tabId, `
|
|
372
|
+
(() => {
|
|
373
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
374
|
+
return el ? (${outer} ? el.outerHTML : el.innerHTML) : null;
|
|
375
|
+
})()
|
|
376
|
+
`);
|
|
377
|
+
}
|
|
378
|
+
async setHTML(tabId, selector, html, outer = false) {
|
|
379
|
+
return this.executeScript(tabId, `
|
|
380
|
+
(() => {
|
|
381
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
382
|
+
if (!el) return false;
|
|
383
|
+
${outer ? `el.outerHTML = ${JSON.stringify(html)};` : `el.innerHTML = ${JSON.stringify(html)};`}
|
|
384
|
+
return true;
|
|
385
|
+
})()
|
|
386
|
+
`);
|
|
387
|
+
}
|
|
388
|
+
async getText(tabId, selector) {
|
|
389
|
+
return this.executeScript(tabId, `
|
|
390
|
+
(() => {
|
|
391
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
392
|
+
return el ? el.textContent : null;
|
|
393
|
+
})()
|
|
394
|
+
`);
|
|
395
|
+
}
|
|
396
|
+
async setText(tabId, selector, text) {
|
|
397
|
+
return this.executeScript(tabId, `
|
|
398
|
+
(() => {
|
|
399
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
400
|
+
if (!el) return false;
|
|
401
|
+
el.textContent = ${JSON.stringify(text)};
|
|
402
|
+
return true;
|
|
403
|
+
})()
|
|
404
|
+
`);
|
|
405
|
+
}
|
|
406
|
+
async getAttribute(tabId, selector, attr) {
|
|
407
|
+
return this.executeScript(tabId, `
|
|
408
|
+
document.querySelector(${JSON.stringify(selector)})?.getAttribute(${JSON.stringify(attr)}) ?? null
|
|
409
|
+
`);
|
|
410
|
+
}
|
|
411
|
+
async setAttribute(tabId, selector, attr, value) {
|
|
412
|
+
return this.executeScript(tabId, `
|
|
413
|
+
(() => {
|
|
414
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
415
|
+
if (!el) return false;
|
|
416
|
+
el.setAttribute(${JSON.stringify(attr)}, ${JSON.stringify(value)});
|
|
417
|
+
return true;
|
|
418
|
+
})()
|
|
419
|
+
`);
|
|
420
|
+
}
|
|
421
|
+
async removeAttribute(tabId, selector, attr) {
|
|
422
|
+
return this.executeScript(tabId, `
|
|
423
|
+
(() => {
|
|
424
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
425
|
+
if (!el) return false;
|
|
426
|
+
el.removeAttribute(${JSON.stringify(attr)});
|
|
427
|
+
return true;
|
|
428
|
+
})()
|
|
429
|
+
`);
|
|
430
|
+
}
|
|
431
|
+
async setStyle(tabId, selector, styles) {
|
|
432
|
+
return this.executeScript(tabId, `
|
|
433
|
+
(() => {
|
|
434
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
435
|
+
if (!el) return false;
|
|
436
|
+
Object.assign(el.style, ${JSON.stringify(styles)});
|
|
437
|
+
return true;
|
|
438
|
+
})()
|
|
439
|
+
`);
|
|
440
|
+
}
|
|
441
|
+
async addClass(tabId, selector, ...classNames) {
|
|
442
|
+
return this.executeScript(tabId, `
|
|
443
|
+
(() => {
|
|
444
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
445
|
+
if (!el) return false;
|
|
446
|
+
el.classList.add(${classNames.map((c) => JSON.stringify(c)).join(",")});
|
|
447
|
+
return true;
|
|
448
|
+
})()
|
|
449
|
+
`);
|
|
450
|
+
}
|
|
451
|
+
async removeClass(tabId, selector, ...classNames) {
|
|
452
|
+
return this.executeScript(tabId, `
|
|
453
|
+
(() => {
|
|
454
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
455
|
+
if (!el) return false;
|
|
456
|
+
el.classList.remove(${classNames.map((c) => JSON.stringify(c)).join(",")});
|
|
457
|
+
return true;
|
|
458
|
+
})()
|
|
459
|
+
`);
|
|
460
|
+
}
|
|
461
|
+
async insertElement(tabId, parentSelector, html, position = "beforeend") {
|
|
462
|
+
return this.executeScript(tabId, `
|
|
463
|
+
(() => {
|
|
464
|
+
const parent = document.querySelector(${JSON.stringify(parentSelector)});
|
|
465
|
+
if (!parent) return false;
|
|
466
|
+
parent.insertAdjacentHTML(${JSON.stringify(position)}, ${JSON.stringify(html)});
|
|
467
|
+
return true;
|
|
468
|
+
})()
|
|
469
|
+
`);
|
|
470
|
+
}
|
|
471
|
+
async removeElement(tabId, selector) {
|
|
472
|
+
return this.executeScript(tabId, `
|
|
473
|
+
(() => {
|
|
474
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
475
|
+
if (!el) return false;
|
|
476
|
+
el.remove();
|
|
477
|
+
return true;
|
|
478
|
+
})()
|
|
479
|
+
`);
|
|
480
|
+
}
|
|
481
|
+
async observeMutations(tabId, selector, options = {}, durationMs = 5e3) {
|
|
482
|
+
const opts = { childList: true, subtree: true, attributes: true, ...options };
|
|
483
|
+
return this.executeScript(tabId, `
|
|
484
|
+
new Promise((resolve) => {
|
|
485
|
+
const target = ${selector === "document" ? "document.documentElement" : `document.querySelector(${JSON.stringify(selector)})`};
|
|
486
|
+
if (!target) { resolve([]); return; }
|
|
487
|
+
const mutations = [];
|
|
488
|
+
const observer = new MutationObserver((records) => {
|
|
489
|
+
for (const r of records) {
|
|
490
|
+
mutations.push({
|
|
491
|
+
type: r.type,
|
|
492
|
+
target: r.target.nodeName + (r.target.id ? '#' + r.target.id : ''),
|
|
493
|
+
added: r.addedNodes.length,
|
|
494
|
+
removed: r.removedNodes.length,
|
|
495
|
+
attribute: r.attributeName || null,
|
|
496
|
+
oldValue: r.oldValue?.substring(0, 200) || null,
|
|
497
|
+
});
|
|
498
|
+
if (mutations.length >= 200) { observer.disconnect(); resolve(mutations); }
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
observer.observe(target, ${JSON.stringify(opts)});
|
|
502
|
+
setTimeout(() => { observer.disconnect(); resolve(mutations); }, ${durationMs});
|
|
503
|
+
})
|
|
504
|
+
`);
|
|
505
|
+
}
|
|
506
|
+
async getComputedStyles(tabId, selector, properties) {
|
|
507
|
+
return this.executeScript(tabId, `
|
|
508
|
+
(() => {
|
|
509
|
+
const el = document.querySelector(${JSON.stringify(selector)});
|
|
510
|
+
if (!el) return null;
|
|
511
|
+
const cs = window.getComputedStyle(el);
|
|
512
|
+
${properties ? `return Object.fromEntries(${JSON.stringify(properties)}.map(p => [p, cs.getPropertyValue(p)]));` : `
|
|
513
|
+
const props = ['display','visibility','opacity','position','width','height','margin','padding',
|
|
514
|
+
'color','background','font-size','font-weight','border','overflow','z-index','transform'];
|
|
515
|
+
return Object.fromEntries(props.map(p => [p, cs.getPropertyValue(p)]));`}
|
|
516
|
+
})()
|
|
517
|
+
`);
|
|
518
|
+
}
|
|
519
|
+
async getBoundingRects(tabId, selector) {
|
|
520
|
+
return this.executeScript(tabId, `
|
|
521
|
+
Array.from(document.querySelectorAll(${JSON.stringify(selector)})).slice(0, 50).map(el => {
|
|
522
|
+
const r = el.getBoundingClientRect();
|
|
523
|
+
return { tag: el.tagName.toLowerCase(), id: el.id, x: r.x, y: r.y, w: r.width, h: r.height };
|
|
524
|
+
})
|
|
525
|
+
`);
|
|
526
|
+
}
|
|
527
|
+
async injectScript(tabId, url) {
|
|
528
|
+
return this.executeScript(tabId, `
|
|
529
|
+
new Promise((resolve) => {
|
|
530
|
+
const s = document.createElement('script');
|
|
531
|
+
s.src = ${JSON.stringify(url)};
|
|
532
|
+
s.onload = () => resolve(true);
|
|
533
|
+
s.onerror = () => resolve(false);
|
|
534
|
+
document.head.appendChild(s);
|
|
535
|
+
})
|
|
536
|
+
`);
|
|
537
|
+
}
|
|
538
|
+
async injectCSS(tabId, css) {
|
|
539
|
+
return this.executeScript(tabId, `
|
|
540
|
+
(() => {
|
|
541
|
+
const style = document.createElement('style');
|
|
542
|
+
style.textContent = ${JSON.stringify(css)};
|
|
543
|
+
document.head.appendChild(style);
|
|
544
|
+
return true;
|
|
545
|
+
})()
|
|
546
|
+
`);
|
|
547
|
+
}
|
|
548
|
+
async getLocalStorage(tabId, key) {
|
|
549
|
+
if (key) {
|
|
550
|
+
return this.executeScript(tabId, `localStorage.getItem(${JSON.stringify(key)})`);
|
|
551
|
+
}
|
|
552
|
+
return this.executeScript(tabId, `JSON.parse(JSON.stringify(localStorage))`);
|
|
553
|
+
}
|
|
554
|
+
async setLocalStorage(tabId, key, value) {
|
|
555
|
+
return this.executeScript(tabId, `
|
|
556
|
+
(() => { localStorage.setItem(${JSON.stringify(key)}, ${JSON.stringify(value)}); return true; })()
|
|
557
|
+
`);
|
|
558
|
+
}
|
|
559
|
+
async getCookies(tabId) {
|
|
560
|
+
return new Promise((resolve) => {
|
|
561
|
+
chrome.tabs.get(tabId, (tab) => {
|
|
562
|
+
if (!tab?.url) {
|
|
563
|
+
resolve([]);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
chrome.cookies.getAll({ url: tab.url }, (cookies) => {
|
|
567
|
+
resolve(cookies || []);
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
async setCookie(tabId, cookie) {
|
|
573
|
+
return new Promise((resolve) => {
|
|
574
|
+
chrome.tabs.get(tabId, (tab) => {
|
|
575
|
+
if (!tab?.url) {
|
|
576
|
+
resolve(false);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
chrome.cookies.set({ url: tab.url, ...cookie }, (c) => resolve(!!c));
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
async deleteCookie(tabId, name) {
|
|
584
|
+
return new Promise((resolve) => {
|
|
585
|
+
chrome.tabs.get(tabId, (tab) => {
|
|
586
|
+
if (!tab?.url) {
|
|
587
|
+
resolve(false);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
chrome.cookies.remove({ url: tab.url, name }, () => resolve(true));
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
async getSessionStorage(tabId, key) {
|
|
595
|
+
if (key) {
|
|
596
|
+
return this.executeScript(tabId, `sessionStorage.getItem(${JSON.stringify(key)})`);
|
|
597
|
+
}
|
|
598
|
+
return this.executeScript(tabId, `JSON.parse(JSON.stringify(sessionStorage))`);
|
|
599
|
+
}
|
|
600
|
+
async setSessionStorage(tabId, key, value) {
|
|
601
|
+
return this.executeScript(tabId, `
|
|
602
|
+
(() => { sessionStorage.setItem(${JSON.stringify(key)}, ${JSON.stringify(value)}); return true; })()
|
|
603
|
+
`);
|
|
604
|
+
}
|
|
605
|
+
async getIndexedDBDatabases(tabId) {
|
|
606
|
+
return this.executeScript(tabId, `
|
|
607
|
+
(async () => {
|
|
608
|
+
const dbs = await indexedDB.databases();
|
|
609
|
+
return dbs.map(db => ({ name: db.name, version: db.version }));
|
|
610
|
+
})()
|
|
611
|
+
`);
|
|
612
|
+
}
|
|
613
|
+
async queryIndexedDB(tabId, dbName, storeName, query) {
|
|
614
|
+
const count = query?.count || 100;
|
|
615
|
+
return this.executeScript(tabId, `
|
|
616
|
+
new Promise((resolve, reject) => {
|
|
617
|
+
const req = indexedDB.open(${JSON.stringify(dbName)});
|
|
618
|
+
req.onerror = () => reject(new Error(req.error?.message || 'Failed to open DB'));
|
|
619
|
+
req.onsuccess = () => {
|
|
620
|
+
const db = req.result;
|
|
621
|
+
try {
|
|
622
|
+
const tx = db.transaction(${JSON.stringify(storeName)}, 'readonly');
|
|
623
|
+
const store = tx.objectStore(${JSON.stringify(storeName)});
|
|
624
|
+
const src = ${query?.index ? `store.index(${JSON.stringify(query.index)})` : "store"};
|
|
625
|
+
const getReq = ${query?.key ? `src.get(${JSON.stringify(query.key)})` : `src.getAll(null, ${count})`};
|
|
626
|
+
getReq.onsuccess = () => resolve(${query?.key ? "[getReq.result]" : "getReq.result"});
|
|
627
|
+
getReq.onerror = () => reject(new Error(getReq.error?.message || 'Query failed'));
|
|
628
|
+
} catch(e) { reject(e); }
|
|
629
|
+
};
|
|
630
|
+
})
|
|
631
|
+
`);
|
|
632
|
+
}
|
|
633
|
+
async getIndexedDBStores(tabId, dbName) {
|
|
634
|
+
return this.executeScript(tabId, `
|
|
635
|
+
new Promise((resolve, reject) => {
|
|
636
|
+
const req = indexedDB.open(${JSON.stringify(dbName)});
|
|
637
|
+
req.onerror = () => reject(new Error(req.error?.message || 'Failed to open DB'));
|
|
638
|
+
req.onsuccess = () => {
|
|
639
|
+
resolve(Array.from(req.result.objectStoreNames));
|
|
640
|
+
};
|
|
641
|
+
})
|
|
642
|
+
`);
|
|
643
|
+
}
|
|
644
|
+
async clearSiteData(tabId, what = ["all"]) {
|
|
645
|
+
const doAll = what.includes("all");
|
|
646
|
+
const results = {};
|
|
647
|
+
if (doAll || what.includes("cookies")) {
|
|
648
|
+
await new Promise((resolve) => {
|
|
649
|
+
chrome.tabs.get(tabId, (tab) => {
|
|
650
|
+
if (!tab?.url) {
|
|
651
|
+
results.cookies = false;
|
|
652
|
+
resolve();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
chrome.cookies.getAll({ url: tab.url }, (cookies) => {
|
|
656
|
+
Promise.all((cookies || []).map(
|
|
657
|
+
(c) => new Promise((r) => chrome.cookies.remove({ url: tab.url, name: c.name }, () => r()))
|
|
658
|
+
)).then(() => {
|
|
659
|
+
results.cookies = true;
|
|
660
|
+
resolve();
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
if (doAll || what.includes("localStorage")) {
|
|
667
|
+
results.localStorage = await this.executeScript(tabId, `(() => { localStorage.clear(); return true; })()`);
|
|
668
|
+
}
|
|
669
|
+
if (doAll || what.includes("sessionStorage")) {
|
|
670
|
+
results.sessionStorage = await this.executeScript(tabId, `(() => { sessionStorage.clear(); return true; })()`);
|
|
671
|
+
}
|
|
672
|
+
if (doAll || what.includes("indexedDB")) {
|
|
673
|
+
results.indexedDB = await this.executeScript(tabId, `
|
|
674
|
+
(async () => {
|
|
675
|
+
const dbs = await indexedDB.databases();
|
|
676
|
+
await Promise.all(dbs.map(db => new Promise((r, j) => {
|
|
677
|
+
const req = indexedDB.deleteDatabase(db.name);
|
|
678
|
+
req.onsuccess = () => r(true);
|
|
679
|
+
req.onerror = () => r(false);
|
|
680
|
+
})));
|
|
681
|
+
return true;
|
|
682
|
+
})()
|
|
683
|
+
`);
|
|
684
|
+
}
|
|
685
|
+
if (doAll || what.includes("cache")) {
|
|
686
|
+
results.cache = await this.executeScript(tabId, `
|
|
687
|
+
(async () => {
|
|
688
|
+
const keys = await caches.keys();
|
|
689
|
+
await Promise.all(keys.map(k => caches.delete(k)));
|
|
690
|
+
return true;
|
|
691
|
+
})()
|
|
692
|
+
`);
|
|
693
|
+
}
|
|
694
|
+
return results;
|
|
695
|
+
}
|
|
696
|
+
async getCacheStorageKeys(tabId) {
|
|
697
|
+
return this.executeScript(tabId, `caches.keys()`);
|
|
698
|
+
}
|
|
699
|
+
async checkWebGPU(tabId) {
|
|
700
|
+
return this.executeScript(tabId, `
|
|
701
|
+
(async () => {
|
|
702
|
+
if (!navigator.gpu) return { available: false, reason: 'WebGPU not supported' };
|
|
703
|
+
try {
|
|
704
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
705
|
+
if (!adapter) return { available: false, reason: 'No GPU adapter' };
|
|
706
|
+
const info = await adapter.requestAdapterInfo();
|
|
707
|
+
return {
|
|
708
|
+
available: true,
|
|
709
|
+
adapter: info.description || info.device || info.vendor || 'Unknown',
|
|
710
|
+
vendor: info.vendor,
|
|
711
|
+
architecture: info.architecture,
|
|
712
|
+
features: [...adapter.features].sort(),
|
|
713
|
+
limits: {
|
|
714
|
+
maxBufferSize: adapter.limits.maxBufferSize,
|
|
715
|
+
maxTextureDimension2D: adapter.limits.maxTextureDimension2D,
|
|
716
|
+
maxComputeWorkgroupSizeX: adapter.limits.maxComputeWorkgroupSizeX,
|
|
717
|
+
maxBindGroups: adapter.limits.maxBindGroups,
|
|
718
|
+
maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
} catch(e) { return { available: false, reason: e.message }; }
|
|
722
|
+
})()
|
|
723
|
+
`);
|
|
724
|
+
}
|
|
725
|
+
async hardReload(tabId) {
|
|
726
|
+
chrome.tabs.reload(tabId, { bypassCache: true });
|
|
727
|
+
}
|
|
728
|
+
async getServiceWorkers(tabId) {
|
|
729
|
+
return this.executeScript(tabId, `
|
|
730
|
+
(async () => {
|
|
731
|
+
const regs = await navigator.serviceWorker?.getRegistrations() || [];
|
|
732
|
+
return regs.map(r => ({
|
|
733
|
+
scope: r.scope,
|
|
734
|
+
active: r.active ? { state: r.active.state, scriptURL: r.active.scriptURL } : null,
|
|
735
|
+
waiting: r.waiting ? { state: r.waiting.state } : null,
|
|
736
|
+
installing: r.installing ? { state: r.installing.state } : null,
|
|
737
|
+
}));
|
|
738
|
+
})()
|
|
739
|
+
`);
|
|
740
|
+
}
|
|
741
|
+
async getPerformanceMetrics(tabId) {
|
|
742
|
+
return this.executeScript(tabId, `
|
|
743
|
+
(() => {
|
|
744
|
+
const nav = performance.getEntriesByType('navigation')[0] || {};
|
|
745
|
+
const paint = performance.getEntriesByType('paint');
|
|
746
|
+
const resources = performance.getEntriesByType('resource');
|
|
747
|
+
return {
|
|
748
|
+
timing: {
|
|
749
|
+
domContentLoaded: Math.round(nav.domContentLoadedEventEnd - nav.startTime),
|
|
750
|
+
load: Math.round(nav.loadEventEnd - nav.startTime),
|
|
751
|
+
ttfb: Math.round(nav.responseStart - nav.startTime),
|
|
752
|
+
domInteractive: Math.round(nav.domInteractive - nav.startTime),
|
|
753
|
+
},
|
|
754
|
+
paint: Object.fromEntries(paint.map(p => [p.name, Math.round(p.startTime)])),
|
|
755
|
+
resourceCount: resources.length,
|
|
756
|
+
transferSize: resources.reduce((s, r) => s + (r.transferSize || 0), 0),
|
|
757
|
+
memory: performance.memory ? {
|
|
758
|
+
usedJSHeapSize: performance.memory.usedJSHeapSize,
|
|
759
|
+
totalJSHeapSize: performance.memory.totalJSHeapSize,
|
|
760
|
+
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit,
|
|
761
|
+
} : null,
|
|
762
|
+
};
|
|
763
|
+
})()
|
|
764
|
+
`);
|
|
765
|
+
}
|
|
766
|
+
// ===========================================================================
|
|
368
767
|
// Navigation
|
|
369
768
|
// ===========================================================================
|
|
370
769
|
async navigateTo(tabId, url) {
|
|
@@ -386,6 +785,74 @@ var BrowserControl = class {
|
|
|
386
785
|
});
|
|
387
786
|
});
|
|
388
787
|
}
|
|
788
|
+
async getURL(tabId) {
|
|
789
|
+
return new Promise((resolve) => {
|
|
790
|
+
chrome.tabs.get(tabId, (tab) => resolve(tab?.url || ""));
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
async getTitle(tabId) {
|
|
794
|
+
return new Promise((resolve) => {
|
|
795
|
+
chrome.tabs.get(tabId, (tab) => resolve(tab?.title || ""));
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
async getTabInfo(tabId) {
|
|
799
|
+
return new Promise((resolve) => {
|
|
800
|
+
chrome.tabs.get(tabId, (tab) => resolve({
|
|
801
|
+
id: tab?.id,
|
|
802
|
+
url: tab?.url,
|
|
803
|
+
title: tab?.title,
|
|
804
|
+
status: tab?.status,
|
|
805
|
+
active: tab?.active,
|
|
806
|
+
index: tab?.index,
|
|
807
|
+
pinned: tab?.pinned,
|
|
808
|
+
incognito: tab?.incognito
|
|
809
|
+
}));
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
async getHistory(tabId, maxResults = 50) {
|
|
813
|
+
return this.executeScript(tabId, `
|
|
814
|
+
(() => {
|
|
815
|
+
const entries = performance.getEntriesByType('navigation');
|
|
816
|
+
return {
|
|
817
|
+
length: history.length,
|
|
818
|
+
scrollRestoration: history.scrollRestoration,
|
|
819
|
+
current: location.href,
|
|
820
|
+
navigation: entries.map(e => ({
|
|
821
|
+
type: e.type,
|
|
822
|
+
redirectCount: e.redirectCount,
|
|
823
|
+
duration: Math.round(e.duration),
|
|
824
|
+
})),
|
|
825
|
+
};
|
|
826
|
+
})()
|
|
827
|
+
`);
|
|
828
|
+
}
|
|
829
|
+
async browserFetch(tabId, url, options) {
|
|
830
|
+
const opts = {
|
|
831
|
+
method: options?.method || "GET",
|
|
832
|
+
headers: options?.headers || {},
|
|
833
|
+
body: options?.body,
|
|
834
|
+
mode: options?.mode || "cors",
|
|
835
|
+
credentials: options?.credentials || "include"
|
|
836
|
+
};
|
|
837
|
+
return this.executeScript(tabId, `
|
|
838
|
+
(async () => {
|
|
839
|
+
const opts = ${JSON.stringify(opts)};
|
|
840
|
+
if (!opts.body) delete opts.body;
|
|
841
|
+
const res = await fetch(${JSON.stringify(url)}, opts);
|
|
842
|
+
const headers = {};
|
|
843
|
+
res.headers.forEach((v, k) => headers[k] = v);
|
|
844
|
+
const ct = res.headers.get('content-type') || '';
|
|
845
|
+
let body;
|
|
846
|
+
if (ct.includes('json')) {
|
|
847
|
+
body = JSON.stringify(await res.json());
|
|
848
|
+
} else {
|
|
849
|
+
body = await res.text();
|
|
850
|
+
}
|
|
851
|
+
if (body.length > 500000) body = body.substring(0, 500000) + '...(truncated)';
|
|
852
|
+
return { status: res.status, statusText: res.statusText, headers, body, url: res.url };
|
|
853
|
+
})()
|
|
854
|
+
`);
|
|
855
|
+
}
|
|
389
856
|
async closeTab(tabId) {
|
|
390
857
|
chrome.tabs.remove(tabId);
|
|
391
858
|
}
|
|
@@ -597,9 +1064,33 @@ var BrowserControl = class {
|
|
|
597
1064
|
case "browser.scroll":
|
|
598
1065
|
sendResponse({ success: await this.scroll(tabId, request.x, request.y, request.selector) });
|
|
599
1066
|
break;
|
|
1067
|
+
case "browser.dblclick":
|
|
1068
|
+
sendResponse({ success: await this.doubleClick(tabId, request.selector) });
|
|
1069
|
+
break;
|
|
600
1070
|
case "browser.hover":
|
|
601
1071
|
sendResponse({ success: await this.hover(tabId, request.selector) });
|
|
602
1072
|
break;
|
|
1073
|
+
case "browser.clear":
|
|
1074
|
+
sendResponse({ success: await this.fill(tabId, request.selector, "") });
|
|
1075
|
+
break;
|
|
1076
|
+
case "browser.check":
|
|
1077
|
+
sendResponse({ success: await this.check(tabId, request.selector, true) });
|
|
1078
|
+
break;
|
|
1079
|
+
case "browser.uncheck":
|
|
1080
|
+
sendResponse({ success: await this.check(tabId, request.selector, false) });
|
|
1081
|
+
break;
|
|
1082
|
+
case "browser.focus":
|
|
1083
|
+
sendResponse({ success: await this.focus(tabId, request.selector) });
|
|
1084
|
+
break;
|
|
1085
|
+
case "browser.blur":
|
|
1086
|
+
sendResponse({ success: await this.blur(tabId, request.selector) });
|
|
1087
|
+
break;
|
|
1088
|
+
case "browser.drag":
|
|
1089
|
+
sendResponse({ success: await this.drag(tabId, request.from || request.selector, request.to) });
|
|
1090
|
+
break;
|
|
1091
|
+
case "browser.scrollIntoView":
|
|
1092
|
+
sendResponse({ success: await this.scrollIntoView(tabId, request.selector) });
|
|
1093
|
+
break;
|
|
603
1094
|
case "browser.pressKey":
|
|
604
1095
|
sendResponse({ success: await this.pressKey(tabId, request.key, request.modifiers) });
|
|
605
1096
|
break;
|
|
@@ -613,6 +1104,43 @@ var BrowserControl = class {
|
|
|
613
1104
|
await this.navigateTo(tabId, request.url);
|
|
614
1105
|
sendResponse({ success: true });
|
|
615
1106
|
break;
|
|
1107
|
+
case "browser.goBack":
|
|
1108
|
+
await this.goBack(tabId);
|
|
1109
|
+
sendResponse({ success: true });
|
|
1110
|
+
break;
|
|
1111
|
+
case "browser.goForward":
|
|
1112
|
+
await this.goForward(tabId);
|
|
1113
|
+
sendResponse({ success: true });
|
|
1114
|
+
break;
|
|
1115
|
+
case "browser.reload":
|
|
1116
|
+
await this.reload(tabId);
|
|
1117
|
+
sendResponse({ success: true });
|
|
1118
|
+
break;
|
|
1119
|
+
case "browser.getURL":
|
|
1120
|
+
sendResponse({ success: true, url: await this.getURL(tabId) });
|
|
1121
|
+
break;
|
|
1122
|
+
case "browser.getTitle":
|
|
1123
|
+
sendResponse({ success: true, title: await this.getTitle(tabId) });
|
|
1124
|
+
break;
|
|
1125
|
+
case "browser.getTabInfo":
|
|
1126
|
+
sendResponse({ success: true, info: await this.getTabInfo(tabId) });
|
|
1127
|
+
break;
|
|
1128
|
+
case "browser.waitForNavigation":
|
|
1129
|
+
sendResponse({ success: await this.waitForNavigation(tabId, request.timeout) });
|
|
1130
|
+
break;
|
|
1131
|
+
case "browser.getHistory":
|
|
1132
|
+
sendResponse({ success: true, history: await this.getHistory(tabId, request.maxResults) });
|
|
1133
|
+
break;
|
|
1134
|
+
case "browser.fetch":
|
|
1135
|
+
sendResponse({ success: true, response: await this.browserFetch(tabId, request.url, request.options) });
|
|
1136
|
+
break;
|
|
1137
|
+
case "browser.createTab":
|
|
1138
|
+
sendResponse({ success: true, tabId: await this.createTab(request.url || "about:blank", request.active !== false) });
|
|
1139
|
+
break;
|
|
1140
|
+
case "browser.closeTab":
|
|
1141
|
+
await this.closeTab(tabId);
|
|
1142
|
+
sendResponse({ success: true });
|
|
1143
|
+
break;
|
|
616
1144
|
case "browser.waitForSelector":
|
|
617
1145
|
sendResponse({ success: await this.waitForSelector(tabId, request.selector, request.timeout) });
|
|
618
1146
|
break;
|
|
@@ -625,6 +1153,66 @@ var BrowserControl = class {
|
|
|
625
1153
|
case "browser.querySelectorAll":
|
|
626
1154
|
sendResponse({ success: true, elements: await this.querySelectorAll(tabId, request.selector) });
|
|
627
1155
|
break;
|
|
1156
|
+
case "browser.getHTML":
|
|
1157
|
+
sendResponse({ success: true, html: await this.getHTML(tabId, request.selector, request.outer !== false) });
|
|
1158
|
+
break;
|
|
1159
|
+
case "browser.setHTML":
|
|
1160
|
+
sendResponse({ success: await this.setHTML(tabId, request.selector, request.html, request.outer) });
|
|
1161
|
+
break;
|
|
1162
|
+
case "browser.getText":
|
|
1163
|
+
sendResponse({ success: true, text: await this.getText(tabId, request.selector) });
|
|
1164
|
+
break;
|
|
1165
|
+
case "browser.setText":
|
|
1166
|
+
sendResponse({ success: await this.setText(tabId, request.selector, request.text) });
|
|
1167
|
+
break;
|
|
1168
|
+
case "browser.getAttribute":
|
|
1169
|
+
sendResponse({ success: true, value: await this.getAttribute(tabId, request.selector, request.attr) });
|
|
1170
|
+
break;
|
|
1171
|
+
case "browser.setAttribute":
|
|
1172
|
+
sendResponse({ success: await this.setAttribute(tabId, request.selector, request.attr, request.value) });
|
|
1173
|
+
break;
|
|
1174
|
+
case "browser.removeAttribute":
|
|
1175
|
+
sendResponse({ success: await this.removeAttribute(tabId, request.selector, request.attr) });
|
|
1176
|
+
break;
|
|
1177
|
+
case "browser.setStyle":
|
|
1178
|
+
sendResponse({ success: await this.setStyle(tabId, request.selector, request.styles) });
|
|
1179
|
+
break;
|
|
1180
|
+
case "browser.addClass":
|
|
1181
|
+
sendResponse({ success: await this.addClass(tabId, request.selector, ...request.classNames || []) });
|
|
1182
|
+
break;
|
|
1183
|
+
case "browser.removeClass":
|
|
1184
|
+
sendResponse({ success: await this.removeClass(tabId, request.selector, ...request.classNames || []) });
|
|
1185
|
+
break;
|
|
1186
|
+
case "browser.insertElement":
|
|
1187
|
+
sendResponse({ success: await this.insertElement(tabId, request.selector, request.html, request.position) });
|
|
1188
|
+
break;
|
|
1189
|
+
case "browser.removeElement":
|
|
1190
|
+
sendResponse({ success: await this.removeElement(tabId, request.selector) });
|
|
1191
|
+
break;
|
|
1192
|
+
case "browser.observeMutations":
|
|
1193
|
+
sendResponse({ success: true, mutations: await this.observeMutations(tabId, request.selector || "document", request.options, request.duration) });
|
|
1194
|
+
break;
|
|
1195
|
+
case "browser.getComputedStyles":
|
|
1196
|
+
sendResponse({ success: true, styles: await this.getComputedStyles(tabId, request.selector, request.properties) });
|
|
1197
|
+
break;
|
|
1198
|
+
case "browser.getBoundingRects":
|
|
1199
|
+
sendResponse({ success: true, rects: await this.getBoundingRects(tabId, request.selector) });
|
|
1200
|
+
break;
|
|
1201
|
+
case "browser.injectScript":
|
|
1202
|
+
sendResponse({ success: await this.injectScript(tabId, request.url) });
|
|
1203
|
+
break;
|
|
1204
|
+
case "browser.injectCSS":
|
|
1205
|
+
sendResponse({ success: await this.injectCSS(tabId, request.css) });
|
|
1206
|
+
break;
|
|
1207
|
+
case "browser.getLocalStorage":
|
|
1208
|
+
sendResponse({ success: true, data: await this.getLocalStorage(tabId, request.key) });
|
|
1209
|
+
break;
|
|
1210
|
+
case "browser.setLocalStorage":
|
|
1211
|
+
sendResponse({ success: await this.setLocalStorage(tabId, request.key, request.value) });
|
|
1212
|
+
break;
|
|
1213
|
+
case "browser.getCookies":
|
|
1214
|
+
sendResponse({ success: true, cookies: await this.getCookies(tabId) });
|
|
1215
|
+
break;
|
|
628
1216
|
default:
|
|
629
1217
|
sendResponse({ success: false, error: `Unknown action: ${request.action}` });
|
|
630
1218
|
}
|
|
@@ -653,29 +1241,75 @@ var BrowserControl = class {
|
|
|
653
1241
|
// ===========================================================================
|
|
654
1242
|
// Script Execution Helper
|
|
655
1243
|
// ===========================================================================
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
1244
|
+
/**
|
|
1245
|
+
* Run JS in a tab and return its result.
|
|
1246
|
+
*
|
|
1247
|
+
* Why MV3 + MAIN world + the Function trampoline?
|
|
1248
|
+
*
|
|
1249
|
+
* - `chrome.scripting.executeScript` only accepts a function (or files);
|
|
1250
|
+
* it does NOT take a code string the way MV2's `tabs.executeScript`
|
|
1251
|
+
* did. To support our existing call sites that pass dynamic code as
|
|
1252
|
+
* a string, we serialise the user's code through `args` and run it
|
|
1253
|
+
* via `Function(codeStr)()` inside the page.
|
|
1254
|
+
* - MAIN world is required so the page's own globals (window.fetch,
|
|
1255
|
+
* CustomElements, framework instances) are visible. ISOLATED world
|
|
1256
|
+
* can't see them.
|
|
1257
|
+
* - Async unwrap: if the caller's code yields a Promise, executeScript
|
|
1258
|
+
* serialises it as `{}` (the value-loss bug). We detect that shape
|
|
1259
|
+
* and re-run wrapping in `Promise.resolve(...)` so the resolved
|
|
1260
|
+
* value comes back instead of the placeholder.
|
|
1261
|
+
*
|
|
1262
|
+
* Falls back to the MV2 string API on browsers / runtimes that still
|
|
1263
|
+
* have it (older Firefox; no Chrome MV2 ships now).
|
|
1264
|
+
*/
|
|
1265
|
+
async executeScript(tabId, code) {
|
|
1266
|
+
const exec = async (wrapped) => {
|
|
1267
|
+
if (chrome.scripting?.executeScript) {
|
|
1268
|
+
const results = await chrome.scripting.executeScript({
|
|
660
1269
|
target: { tabId },
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1270
|
+
world: "MAIN",
|
|
1271
|
+
args: [wrapped],
|
|
1272
|
+
func: (codeStr) => {
|
|
1273
|
+
try {
|
|
1274
|
+
return Function(codeStr)();
|
|
1275
|
+
} catch (e) {
|
|
1276
|
+
return { __hanzo_error: e?.message || String(e) };
|
|
1277
|
+
}
|
|
667
1278
|
}
|
|
668
1279
|
});
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
1280
|
+
return results?.[0]?.result;
|
|
1281
|
+
}
|
|
1282
|
+
const legacy = chrome.tabs.executeScript;
|
|
1283
|
+
if (legacy) {
|
|
1284
|
+
return new Promise((resolve, reject) => {
|
|
1285
|
+
legacy(tabId, { code: wrapped }, (results) => {
|
|
1286
|
+
if (chrome.runtime.lastError) {
|
|
1287
|
+
reject(new Error(chrome.runtime.lastError.message));
|
|
1288
|
+
} else {
|
|
1289
|
+
resolve(results?.[0]);
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
676
1292
|
});
|
|
677
1293
|
}
|
|
678
|
-
|
|
1294
|
+
throw new Error("No executeScript API available (need MV3 scripting permission)");
|
|
1295
|
+
};
|
|
1296
|
+
const trimmed = code.trim();
|
|
1297
|
+
const wrappedCode = /^\s*return\b/.test(trimmed) ? trimmed : `try { return (${trimmed}); } catch(__e) { return { __hanzo_error: __e.message || String(__e) }; }`;
|
|
1298
|
+
let result = await exec(wrappedCode);
|
|
1299
|
+
if (result && typeof result === "object" && "__hanzo_error" in result) {
|
|
1300
|
+
throw new Error(result.__hanzo_error);
|
|
1301
|
+
}
|
|
1302
|
+
const looksEmpty = result !== null && typeof result === "object" && Object.keys(result).length === 0 && !Array.isArray(result);
|
|
1303
|
+
if (looksEmpty) {
|
|
1304
|
+
const promiseWrapped = `return Promise.resolve((function(){ ${wrappedCode} })()).then(function(__v){
|
|
1305
|
+
try { return JSON.parse(JSON.stringify(__v === undefined ? null : __v)); } catch(e) { return String(__v); }
|
|
1306
|
+
});`;
|
|
1307
|
+
result = await exec(promiseWrapped);
|
|
1308
|
+
if (result && typeof result === "object" && "__hanzo_error" in result) {
|
|
1309
|
+
throw new Error(result.__hanzo_error);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
return result;
|
|
679
1313
|
}
|
|
680
1314
|
};
|
|
681
1315
|
export {
|