nothing-browser 0.1.1 → 0.1.3

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/piggy.js CHANGED
@@ -7279,6 +7279,24 @@ class PiggyClient {
7279
7279
  }
7280
7280
  }
7281
7281
  }
7282
+ if (event.event === "dialog") {
7283
+ const key = `dialog:${event.tabId ?? "default"}`;
7284
+ const handlers = this.globalEventHandlers.get(key);
7285
+ if (handlers) {
7286
+ for (const h of handlers) {
7287
+ try {
7288
+ h({
7289
+ dialogType: event.dialogType,
7290
+ message: event.message,
7291
+ defaultValue: event.defaultValue,
7292
+ tabId: event.tabId
7293
+ });
7294
+ } catch (e) {
7295
+ logger_default.error(`dialog handler error: ${e}`);
7296
+ }
7297
+ }
7298
+ }
7299
+ }
7282
7300
  }
7283
7301
  onEvent(eventName, tabId, handler) {
7284
7302
  const key = `${eventName}:${tabId}`;
@@ -7426,14 +7444,14 @@ class PiggyClient {
7426
7444
  async setCookie(name, value, domain, path = "/", tabId = "default") {
7427
7445
  await this.send("cookie.set", { name, value, domain, path, tabId });
7428
7446
  }
7429
- async getCookie(name, tabId = "default") {
7430
- return this.send("cookie.get", { name, tabId });
7447
+ async getCookie(name, domain = "", tabId = "default") {
7448
+ return this.send("cookie.get", { name, domain, tabId });
7431
7449
  }
7432
- async deleteCookie(name, tabId = "default") {
7433
- await this.send("cookie.delete", { name, tabId });
7450
+ async deleteCookie(name, domain, tabId = "default") {
7451
+ await this.send("cookie.delete", { name, domain, tabId });
7434
7452
  }
7435
- async listCookies(tabId = "default") {
7436
- return this.send("cookie.list", { tabId });
7453
+ async listCookies(domain = "", tabId = "default") {
7454
+ return this.send("cookie.list", { domain, tabId });
7437
7455
  }
7438
7456
  async addInterceptRule(action, pattern, options = {}, tabId = "default") {
7439
7457
  await this.send("intercept.rule.add", { action, pattern, ...options, tabId });
@@ -27293,7 +27311,7 @@ function finalize(ctx, schema) {
27293
27311
  result.$schema = "http://json-schema.org/draft-07/schema#";
27294
27312
  } else if (ctx.target === "draft-04") {
27295
27313
  result.$schema = "http://json-schema.org/draft-04/schema#";
27296
- } else if (ctx.target === "openapi-3.0") {} else {}
27314
+ } else if (ctx.target === "openapi-3.0") {}
27297
27315
  if (ctx.external?.uri) {
27298
27316
  const id = ctx.external.registry.get(schema)?.id;
27299
27317
  if (!id)
@@ -28617,142 +28635,479 @@ async function storeRecord(storeName, data) {
28617
28635
  return { stored, skipped };
28618
28636
  }
28619
28637
 
28620
- // piggy/register/index.ts
28621
- var globalClient = null;
28622
- var humanMode = false;
28623
- function setClient(c) {
28624
- globalClient = c;
28638
+ // piggy/find/index.ts
28639
+ class FindClient {
28640
+ client;
28641
+ constructor(client) {
28642
+ this.client = client;
28643
+ }
28644
+ css(selector, tabId = "default") {
28645
+ return this.client.send("find.css", { selector, tabId });
28646
+ }
28647
+ all(selector, tabId = "default") {
28648
+ return this.client.send("find.all", { selector, tabId });
28649
+ }
28650
+ first(selector, tabId = "default") {
28651
+ return this.client.send("find.first", { selector, tabId });
28652
+ }
28653
+ byText(text, tabId = "default") {
28654
+ return this.client.send("find.byText", { text, tabId });
28655
+ }
28656
+ byAttr(attr, value, tabId = "default") {
28657
+ return this.client.send("find.byAttr", { attr, value, tabId });
28658
+ }
28659
+ byTag(tag, tabId = "default") {
28660
+ return this.client.send("find.byTag", { tag, tabId });
28661
+ }
28662
+ byPlaceholder(text, tabId = "default") {
28663
+ return this.client.send("find.byPlaceholder", { text, tabId });
28664
+ }
28665
+ byRole(role, name, tabId = "default") {
28666
+ return this.client.send("find.byRole", { role, name, tabId });
28667
+ }
28668
+ children(selector, tabId = "default") {
28669
+ return this.client.send("find.children", { selector, tabId });
28670
+ }
28671
+ parent(selector, tabId = "default") {
28672
+ return this.client.send("find.parent", { selector, tabId });
28673
+ }
28674
+ closest(selector, ancestor, tabId = "default") {
28675
+ return this.client.send("find.closest", { selector, ancestor, tabId });
28676
+ }
28677
+ count(selector, tabId = "default") {
28678
+ return this.client.send("find.count", { selector, tabId });
28679
+ }
28680
+ exists(selector, tabId = "default") {
28681
+ return this.client.send("find.exists", { selector, tabId });
28682
+ }
28683
+ visible(selector, tabId = "default") {
28684
+ return this.client.send("find.visible", { selector, tabId });
28685
+ }
28686
+ enabled(selector, tabId = "default") {
28687
+ return this.client.send("find.enabled", { selector, tabId });
28688
+ }
28689
+ checked(selector, tabId = "default") {
28690
+ return this.client.send("find.checked", { selector, tabId });
28691
+ }
28625
28692
  }
28626
- function setHumanMode(v) {
28627
- humanMode = v;
28693
+ function createFindAPI(client) {
28694
+ return new FindClient(client);
28628
28695
  }
28629
- async function retry(label, fn, retries = 2, backoff = 150) {
28630
- let last;
28631
- for (let i = 0;i <= retries; i++) {
28632
- try {
28633
- return await fn();
28634
- } catch (e) {
28635
- last = e;
28636
- if (i < retries) {
28637
- logger_default.warn(`[${label}] retry ${i + 1}/${retries}: ${e.message}`);
28638
- await new Promise((r) => setTimeout(r, backoff * (i + 1)));
28639
- }
28640
- }
28696
+
28697
+ // piggy/provide/index.ts
28698
+ class ProvideClient {
28699
+ client;
28700
+ constructor(client) {
28701
+ this.client = client;
28702
+ }
28703
+ text(opts, tabId = "default") {
28704
+ return this.client.send("provide.text", { ...opts, tabId });
28705
+ }
28706
+ textAll(opts, tabId = "default") {
28707
+ return this.client.send("provide.textAll", { ...opts, tabId });
28708
+ }
28709
+ attr(opts, tabId = "default") {
28710
+ return this.client.send("provide.attr", { ...opts, tabId });
28711
+ }
28712
+ attrAll(opts, tabId = "default") {
28713
+ return this.client.send("provide.attrAll", { ...opts, tabId });
28714
+ }
28715
+ html(opts, tabId = "default") {
28716
+ return this.client.send("provide.html", { ...opts, tabId });
28717
+ }
28718
+ table(opts, tabId = "default") {
28719
+ return this.client.send("provide.table", { ...opts, tabId });
28720
+ }
28721
+ list(opts, tabId = "default") {
28722
+ return this.client.send("provide.list", { ...opts, tabId });
28723
+ }
28724
+ links(opts, tabId = "default") {
28725
+ return this.client.send("provide.links", { ...opts, tabId });
28726
+ }
28727
+ images(opts, tabId = "default") {
28728
+ return this.client.send("provide.images", { ...opts, tabId });
28729
+ }
28730
+ form(opts, tabId = "default") {
28731
+ return this.client.send("provide.form", { ...opts, tabId });
28732
+ }
28733
+ page(tabId = "default") {
28734
+ return this.client.send("provide.page", { tabId });
28735
+ }
28736
+ div(opts, tabId = "default") {
28737
+ return this.client.send("provide.div", { ...opts, tabId });
28738
+ }
28739
+ meta(tabId = "default") {
28740
+ return this.client.send("provide.meta", { tabId });
28741
+ }
28742
+ select(opts, tabId = "default") {
28743
+ return this.client.send("provide.select", { ...opts, tabId });
28744
+ }
28745
+ json(opts, tabId = "default") {
28746
+ return this.client.send("provide.json", { ...opts, tabId });
28641
28747
  }
28642
- throw last;
28643
28748
  }
28644
- function createSiteObject(name, registeredUrl, client, tabId, pool) {
28645
- let _currentUrl = registeredUrl;
28646
- let _modifyRuleCounter = 0;
28647
- function withTab(fn) {
28648
- return pool ? pool.withTab(fn) : fn(tabId);
28749
+ function createProvideAPI(client) {
28750
+ return new ProvideClient(client);
28751
+ }
28752
+
28753
+ // piggy/session/index.ts
28754
+ class SessionClient {
28755
+ client;
28756
+ constructor(client) {
28757
+ this.client = client;
28649
28758
  }
28650
- const _eventListeners = new Map;
28651
- const _unsubNavigate = client.onEvent("navigate", tabId, (url2) => {
28652
- _currentUrl = url2;
28653
- const handlers = _eventListeners.get("navigate");
28654
- if (handlers) {
28655
- for (const h of handlers) {
28656
- try {
28657
- h(url2);
28658
- } catch (e) {
28659
- logger_default.error(`[${name}] navigate handler error: ${e}`);
28660
- }
28661
- }
28662
- }
28663
- });
28664
- const withErrScreen = async (fn, label) => {
28665
- try {
28666
- return await fn();
28667
- } catch (err) {
28668
- const p = `./error-${name}-${Date.now()}.png`;
28669
- try {
28670
- await client.screenshot(p, tabId);
28671
- logger_default.error(`[${name}] ${label} failed → ${p}`);
28672
- } catch {
28673
- logger_default.error(`[${name}] ${label} failed (no screenshot)`);
28674
- }
28675
- throw err;
28676
- }
28677
- };
28678
- const site = {
28679
- _name: name,
28680
- _tabId: tabId,
28681
- _pool: pool ?? null,
28682
- poolStats: () => pool?.stats ?? null,
28683
- navigate: (url2, opts) => {
28684
- const target = url2 ?? registeredUrl;
28685
- return withTab((t2) => retry(name, async () => {
28686
- logger_default.network(`[${name}] navigating → ${target}`);
28687
- await client.navigate(target, t2);
28688
- _currentUrl = target;
28689
- }, opts?.retries ?? 2));
28690
- },
28691
- reload: () => withTab((t2) => client.reload(t2)),
28692
- goBack: () => withTab((t2) => client.goBack(t2)),
28693
- goForward: () => withTab((t2) => client.goForward(t2)),
28694
- waitForNavigation: () => withTab((t2) => client.waitForNavigation(t2)),
28695
- title: () => withTab(async (t2) => {
28696
- const title = await client.getTitle(t2);
28697
- logger_default.info(`[${name}] title: ${title}`);
28698
- return title;
28699
- }),
28700
- url: () => _currentUrl,
28701
- content: () => withTab((t2) => client.content(t2)),
28702
- wait: (ms) => {
28703
- const actual = humanMode ? ms + Math.floor(Math.random() * 600) - 300 : ms;
28704
- return new Promise((r) => setTimeout(r, Math.max(0, actual)));
28705
- },
28706
- waitForSelector: (selector, timeout = 30000) => withTab((t2) => {
28707
- logger_default.debug(`[${name}] waitForSelector: ${selector}`);
28708
- return client.waitForSelector(selector, timeout, t2);
28709
- }),
28710
- waitForVisible: (selector, timeout = 30000) => withTab((t2) => client.waitForSelector(selector, timeout, t2)),
28711
- waitForResponse: (pattern, timeout = 30000) => withTab((t2) => client.waitForResponse(pattern, timeout, t2)),
28712
- addInitScript: async (js) => {
28713
- const code = typeof js === "function" ? `(${js.toString()})();` : js;
28714
- await withTab((t2) => client.addInitScript(code, t2));
28715
- logger_default.success(`[${name}] init script added`);
28716
- return site;
28717
- },
28718
- on: (event, handler) => {
28719
- if (!_eventListeners.has(event))
28720
- _eventListeners.set(event, new Set);
28721
- _eventListeners.get(event).add(handler);
28722
- logger_default.debug(`[${name}] on('${event}') registered`);
28723
- return () => {
28724
- _eventListeners.get(event)?.delete(handler);
28725
- logger_default.debug(`[${name}] on('${event}') unsubscribed`);
28726
- };
28727
- },
28728
- off: (event, handler) => {
28729
- _eventListeners.get(event)?.delete(handler);
28730
- },
28731
- click: (selector, opts) => withErrScreen(() => withTab((t2) => retry(name, async () => {
28732
- if (humanMode)
28733
- await new Promise((r) => setTimeout(r, 80 + Math.random() * 140));
28734
- await client.waitForSelector(selector, opts?.timeout ?? 15000, t2);
28735
- const ok = await client.click(selector, t2);
28736
- if (!ok)
28737
- throw new Error(`click failed: ${selector}`);
28738
- logger_default.success(`[${name}] clicked: ${selector}`);
28739
- return ok;
28740
- }, opts?.retries ?? 2)), `click(${selector})`),
28741
- doubleClick: (selector) => withErrScreen(() => withTab(async (t2) => {
28742
- if (humanMode)
28743
- await new Promise((r) => setTimeout(r, 80 + Math.random() * 120));
28744
- return client.doubleClick(selector, t2);
28745
- }), `dblclick(${selector})`),
28746
- hover: (selector) => withErrScreen(() => withTab(async (t2) => {
28747
- if (humanMode)
28748
- await new Promise((r) => setTimeout(r, 50 + Math.random() * 100));
28749
- return client.hover(selector, t2);
28750
- }), `hover(${selector})`),
28751
- type: (selector, text, opts) => withErrScreen(() => withTab(async (t2) => {
28752
- await client.waitForSelector(selector, 30000, t2);
28753
- if (humanMode) {
28754
- const human = new HumanClient(client);
28755
- await human.type({
28759
+ reload(tabId = "default") {
28760
+ return this.client.send("session.reload", { tabId });
28761
+ }
28762
+ paths() {
28763
+ return this.client.send("session.paths", {});
28764
+ }
28765
+ cookiesPath() {
28766
+ return this.client.send("session.cookies.path", {});
28767
+ }
28768
+ profilePath() {
28769
+ return this.client.send("session.profile.path", {});
28770
+ }
28771
+ wsPath() {
28772
+ return this.client.send("session.ws.path", {});
28773
+ }
28774
+ pingsPath() {
28775
+ return this.client.send("session.pings.path", {});
28776
+ }
28777
+ setWsSave(enabled) {
28778
+ return this.client.send("session.ws.save", { enabled });
28779
+ }
28780
+ setPingsSave(enabled) {
28781
+ return this.client.send("session.pings.save", { enabled });
28782
+ }
28783
+ async export(tabId = "default") {
28784
+ const raw = await this.client.send("session.export", { tabId });
28785
+ return typeof raw === "string" ? JSON.parse(raw) : raw;
28786
+ }
28787
+ import(data, tabId = "default") {
28788
+ return this.client.send("session.import", {
28789
+ data: JSON.stringify(data),
28790
+ tabId
28791
+ });
28792
+ }
28793
+ setCookie(opts, tabId = "default") {
28794
+ return this.client.send("cookie.set", { ...opts, tabId });
28795
+ }
28796
+ deleteCookie(opts, tabId = "default") {
28797
+ return this.client.send("cookie.delete", { ...opts, tabId });
28798
+ }
28799
+ }
28800
+ function createSessionAPI(client) {
28801
+ return new SessionClient(client);
28802
+ }
28803
+
28804
+ // piggy/dialog/index.ts
28805
+ class DialogClient {
28806
+ client;
28807
+ constructor(client) {
28808
+ this.client = client;
28809
+ }
28810
+ accept(tabId = "default", text) {
28811
+ return this.client.send("dialog.accept", { tabId, ...text !== undefined ? { text } : {} });
28812
+ }
28813
+ dismiss(tabId = "default") {
28814
+ return this.client.send("dialog.dismiss", { tabId });
28815
+ }
28816
+ status(tabId = "default") {
28817
+ return this.client.send("dialog.status", { tabId });
28818
+ }
28819
+ setAutoAction(tabId = "default", action) {
28820
+ return this.client.send("dialog.onDialog", { tabId, action });
28821
+ }
28822
+ upload(selector, filePath, tabId = "default") {
28823
+ return this.client.send("upload", { selector, path: filePath, tabId });
28824
+ }
28825
+ onDialog(tabId, handler) {
28826
+ return this.client.onEvent("dialog", tabId, handler);
28827
+ }
28828
+ waitAndAccept(tabId = "default", text, timeoutMs = 30000) {
28829
+ return new Promise((resolve2, reject) => {
28830
+ const timer = setTimeout(() => {
28831
+ unsub();
28832
+ reject(new Error(`dialog.waitAndAccept: timed out after ${timeoutMs}ms`));
28833
+ }, timeoutMs);
28834
+ const unsub = this.onDialog(tabId, async (data) => {
28835
+ clearTimeout(timer);
28836
+ unsub();
28837
+ await this.accept(tabId, text);
28838
+ resolve2(data);
28839
+ });
28840
+ });
28841
+ }
28842
+ waitAndDismiss(tabId = "default", timeoutMs = 30000) {
28843
+ return new Promise((resolve2, reject) => {
28844
+ const timer = setTimeout(() => {
28845
+ unsub();
28846
+ reject(new Error(`dialog.waitAndDismiss: timed out after ${timeoutMs}ms`));
28847
+ }, timeoutMs);
28848
+ const unsub = this.onDialog(tabId, async (data) => {
28849
+ clearTimeout(timer);
28850
+ unsub();
28851
+ await this.dismiss(tabId);
28852
+ resolve2(data);
28853
+ });
28854
+ });
28855
+ }
28856
+ }
28857
+ function createDialogAPI(client) {
28858
+ return new DialogClient(client);
28859
+ }
28860
+
28861
+ // piggy/iframe/index.ts
28862
+ class IframeClient {
28863
+ client;
28864
+ constructor(client) {
28865
+ this.client = client;
28866
+ }
28867
+ list(tabId = "default") {
28868
+ return this.client.send("iframe.list", { tabId });
28869
+ }
28870
+ evaluate(opts, tabId = "default") {
28871
+ return this.client.send("iframe.evaluate", { ...opts, tabId });
28872
+ }
28873
+ click(opts, tabId = "default") {
28874
+ return this.client.send("iframe.click", { ...opts, tabId });
28875
+ }
28876
+ type(opts, tabId = "default") {
28877
+ return this.client.send("iframe.type", { ...opts, tabId });
28878
+ }
28879
+ text(opts, tabId = "default") {
28880
+ return this.client.send("iframe.text", { ...opts, tabId });
28881
+ }
28882
+ html(opts, tabId = "default") {
28883
+ return this.client.send("iframe.html", { ...opts, tabId });
28884
+ }
28885
+ waitSel(opts, tabId = "default") {
28886
+ return this.client.send("iframe.waitSel", { ...opts, tabId });
28887
+ }
28888
+ }
28889
+ function createIframeAPI(client) {
28890
+ return new IframeClient(client);
28891
+ }
28892
+
28893
+ // piggy/expose/index.ts
28894
+ async function exposeFunction(client, fnName, handler, tabId) {
28895
+ await client.exposeFunction(fnName, handler, tabId);
28896
+ logger_default.success(`[${tabId}] exposed function: ${fnName}`);
28897
+ }
28898
+ async function unexposeFunction(client, fnName, tabId) {
28899
+ await client.unexposeFunction(fnName, tabId);
28900
+ logger_default.info(`[${tabId}] unexposed function: ${fnName}`);
28901
+ }
28902
+ async function clearExposedFunctions(client, tabId) {
28903
+ await client.clearExposedFunctions(tabId);
28904
+ logger_default.info(`[${tabId}] cleared all exposed functions`);
28905
+ }
28906
+ async function exposeAndInject(client, fnName, handler, injectionJs, tabId) {
28907
+ await client.exposeFunction(fnName, handler, tabId);
28908
+ const js = typeof injectionJs === "function" ? injectionJs(fnName) : injectionJs;
28909
+ await client.evaluate(js, tabId);
28910
+ logger_default.success(`[${tabId}] exposed and injected: ${fnName}`);
28911
+ }
28912
+
28913
+ // piggy/captcha/index.ts
28914
+ class CaptchaClient {
28915
+ client;
28916
+ constructor(client) {
28917
+ this.client = client;
28918
+ }
28919
+ status(tabId = "default") {
28920
+ return this.client.send("captcha.status", { tabId });
28921
+ }
28922
+ resolve(tabId = "default") {
28923
+ return this.client.send("captcha.resolve", { tabId });
28924
+ }
28925
+ pause(tabId = "default") {
28926
+ return this.client.send("captcha.pause", { tabId });
28927
+ }
28928
+ check(tabId = "default") {
28929
+ return this.client.send("captcha.check", { tabId });
28930
+ }
28931
+ setAutoRetry(enabled) {
28932
+ return this.client.send("captcha.autoRetry", { enabled });
28933
+ }
28934
+ blockStatus(tabId = "default") {
28935
+ return this.client.send("block.status", { tabId });
28936
+ }
28937
+ blockRetry(tabId = "default") {
28938
+ return this.client.send("block.retry", { tabId });
28939
+ }
28940
+ onCaptcha(tabId, handler) {
28941
+ return this.client.onEvent("captcha", tabId, handler);
28942
+ }
28943
+ onCaptchaResolved(tabId, handler) {
28944
+ return this.client.onEvent("captcha:resolved", tabId, handler);
28945
+ }
28946
+ onBlocked(tabId, handler) {
28947
+ return this.client.onEvent("blocked", tabId, handler);
28948
+ }
28949
+ onBlockRetry(tabId, handler) {
28950
+ return this.client.onEvent("block:retry", tabId, handler);
28951
+ }
28952
+ waitForResolution(tabId = "default", timeoutMs = 300000) {
28953
+ return new Promise((resolve2, reject) => {
28954
+ const timer = setTimeout(() => {
28955
+ unsub();
28956
+ reject(new Error(`captcha.waitForResolution: timed out after ${timeoutMs}ms`));
28957
+ }, timeoutMs);
28958
+ const unsub = this.onCaptchaResolved(tabId, () => {
28959
+ clearTimeout(timer);
28960
+ unsub();
28961
+ resolve2();
28962
+ });
28963
+ logger_default.warn(`[captcha] waiting for manual resolution on tab ${tabId}…`);
28964
+ });
28965
+ }
28966
+ }
28967
+ function createCaptchaAPI(client) {
28968
+ return new CaptchaClient(client);
28969
+ }
28970
+
28971
+ // piggy/register/index.ts
28972
+ var globalClient = null;
28973
+ var humanMode = false;
28974
+ function setClient(c) {
28975
+ globalClient = c;
28976
+ }
28977
+ function setHumanMode(v) {
28978
+ humanMode = v;
28979
+ }
28980
+ async function retry(label, fn, retries = 2, backoff = 150) {
28981
+ let last;
28982
+ for (let i = 0;i <= retries; i++) {
28983
+ try {
28984
+ return await fn();
28985
+ } catch (e) {
28986
+ last = e;
28987
+ if (i < retries) {
28988
+ logger_default.warn(`[${label}] retry ${i + 1}/${retries}: ${e.message}`);
28989
+ await new Promise((r) => setTimeout(r, backoff * (i + 1)));
28990
+ }
28991
+ }
28992
+ }
28993
+ throw last;
28994
+ }
28995
+ function createSiteObject(name, registeredUrl, client, tabId, pool) {
28996
+ let _currentUrl = registeredUrl;
28997
+ let _modifyRuleCounter = 0;
28998
+ function withTab(fn) {
28999
+ return pool ? pool.withTab(fn) : fn(tabId);
29000
+ }
29001
+ const _eventListeners = new Map;
29002
+ const _unsubNavigate = client.onEvent("navigate", tabId, (url2) => {
29003
+ _currentUrl = url2;
29004
+ const handlers = _eventListeners.get("navigate");
29005
+ if (handlers) {
29006
+ for (const h of handlers) {
29007
+ try {
29008
+ h(url2);
29009
+ } catch (e) {
29010
+ logger_default.error(`[${name}] navigate handler error: ${e}`);
29011
+ }
29012
+ }
29013
+ }
29014
+ });
29015
+ const withErrScreen = async (fn, label) => {
29016
+ try {
29017
+ return await fn();
29018
+ } catch (err) {
29019
+ const p = `./error-${name}-${Date.now()}.png`;
29020
+ try {
29021
+ await client.screenshot(p, tabId);
29022
+ logger_default.error(`[${name}] ${label} failed → ${p}`);
29023
+ } catch {
29024
+ logger_default.error(`[${name}] ${label} failed (no screenshot)`);
29025
+ }
29026
+ throw err;
29027
+ }
29028
+ };
29029
+ const site = {
29030
+ _name: name,
29031
+ _tabId: tabId,
29032
+ _pool: pool ?? null,
29033
+ poolStats: () => pool?.stats ?? null,
29034
+ navigate: (url2, opts) => {
29035
+ const target = url2 ?? registeredUrl;
29036
+ return withTab((t2) => retry(name, async () => {
29037
+ logger_default.network(`[${name}] navigating → ${target}`);
29038
+ await client.navigate(target, t2);
29039
+ _currentUrl = target;
29040
+ }, opts?.retries ?? 2));
29041
+ },
29042
+ reload: () => withTab((t2) => client.reload(t2)),
29043
+ goBack: () => withTab((t2) => client.goBack(t2)),
29044
+ goForward: () => withTab((t2) => client.goForward(t2)),
29045
+ waitForNavigation: () => withTab((t2) => client.waitForNavigation(t2)),
29046
+ title: () => withTab(async (t2) => {
29047
+ const title = await client.getTitle(t2);
29048
+ logger_default.info(`[${name}] title: ${title}`);
29049
+ return title;
29050
+ }),
29051
+ url: () => _currentUrl,
29052
+ content: () => withTab((t2) => client.content(t2)),
29053
+ wait: (ms) => {
29054
+ const actual = humanMode ? ms + Math.floor(Math.random() * 600) - 300 : ms;
29055
+ return new Promise((r) => setTimeout(r, Math.max(0, actual)));
29056
+ },
29057
+ waitForSelector: (selector, timeout = 30000) => withTab((t2) => {
29058
+ logger_default.debug(`[${name}] waitForSelector: ${selector}`);
29059
+ return client.waitForSelector(selector, timeout, t2);
29060
+ }),
29061
+ exposeFunction: (fnName, handler) => exposeFunction(client, fnName, handler, tabId).then(() => site),
29062
+ unexposeFunction: (fnName) => unexposeFunction(client, fnName, tabId).then(() => site),
29063
+ clearExposedFunctions: () => clearExposedFunctions(client, tabId).then(() => site),
29064
+ exposeAndInject: (fnName, handler, injectionJs) => exposeAndInject(client, fnName, handler, injectionJs, tabId).then(() => site),
29065
+ waitForVisible: (selector, timeout = 30000) => withTab((t2) => client.waitForSelector(selector, timeout, t2)),
29066
+ waitForResponse: (pattern, timeout = 30000) => withTab((t2) => client.waitForResponse(pattern, timeout, t2)),
29067
+ addInitScript: async (js) => {
29068
+ const code = typeof js === "function" ? `(${js.toString()})();` : js;
29069
+ await withTab((t2) => client.addInitScript(code, t2));
29070
+ logger_default.success(`[${name}] init script added`);
29071
+ return site;
29072
+ },
29073
+ on: (event, handler) => {
29074
+ if (!_eventListeners.has(event))
29075
+ _eventListeners.set(event, new Set);
29076
+ _eventListeners.get(event).add(handler);
29077
+ logger_default.debug(`[${name}] on('${event}') registered`);
29078
+ return () => {
29079
+ _eventListeners.get(event)?.delete(handler);
29080
+ logger_default.debug(`[${name}] on('${event}') unsubscribed`);
29081
+ };
29082
+ },
29083
+ off: (event, handler) => {
29084
+ _eventListeners.get(event)?.delete(handler);
29085
+ },
29086
+ click: (selector, opts) => withErrScreen(() => withTab((t2) => retry(name, async () => {
29087
+ if (humanMode)
29088
+ await new Promise((r) => setTimeout(r, 80 + Math.random() * 140));
29089
+ await client.waitForSelector(selector, opts?.timeout ?? 15000, t2);
29090
+ const ok = await client.click(selector, t2);
29091
+ if (!ok)
29092
+ throw new Error(`click failed: ${selector}`);
29093
+ logger_default.success(`[${name}] clicked: ${selector}`);
29094
+ return ok;
29095
+ }, opts?.retries ?? 2)), `click(${selector})`),
29096
+ doubleClick: (selector) => withErrScreen(() => withTab(async (t2) => {
29097
+ if (humanMode)
29098
+ await new Promise((r) => setTimeout(r, 80 + Math.random() * 120));
29099
+ return client.doubleClick(selector, t2);
29100
+ }), `dblclick(${selector})`),
29101
+ hover: (selector) => withErrScreen(() => withTab(async (t2) => {
29102
+ if (humanMode)
29103
+ await new Promise((r) => setTimeout(r, 50 + Math.random() * 100));
29104
+ return client.hover(selector, t2);
29105
+ }), `hover(${selector})`),
29106
+ type: (selector, text, opts) => withErrScreen(() => withTab(async (t2) => {
29107
+ await client.waitForSelector(selector, 30000, t2);
29108
+ if (humanMode) {
29109
+ const human = new HumanClient(client);
29110
+ await human.type({
28756
29111
  selector,
28757
29112
  text,
28758
29113
  clear: opts?.clear ?? false,
@@ -28816,6 +29171,68 @@ function createSiteObject(name, registeredUrl, client, tabId, pool) {
28816
29171
  css: (query) => withTab((t2) => client.searchCss(query, t2)),
28817
29172
  id: (query) => withTab((t2) => client.searchId(query, t2))
28818
29173
  },
29174
+ captcha: {
29175
+ status: () => withTab((t2) => createCaptchaAPI(client).status(t2)),
29176
+ resolve: () => withTab((t2) => createCaptchaAPI(client).resolve(t2)),
29177
+ pause: () => withTab((t2) => createCaptchaAPI(client).pause(t2)),
29178
+ check: () => withTab((t2) => createCaptchaAPI(client).check(t2)),
29179
+ autoRetry: (opts) => withTab((t2) => createCaptchaAPI(client).setAutoRetry(opts.enabled)),
29180
+ onCaptcha: (handler) => createCaptchaAPI(client).onCaptcha(tabId, handler),
29181
+ onResolved: (handler) => createCaptchaAPI(client).onCaptchaResolved(tabId, handler)
29182
+ },
29183
+ block: {
29184
+ status: () => withTab((t2) => createCaptchaAPI(client).blockStatus(t2)),
29185
+ retry: () => withTab((t2) => createCaptchaAPI(client).blockRetry(t2)),
29186
+ onBlocked: (handler) => createCaptchaAPI(client).onBlocked(tabId, handler),
29187
+ onRetry: (handler) => createCaptchaAPI(client).onBlockRetry(tabId, handler)
29188
+ },
29189
+ find: {
29190
+ css: (selector) => withTab((t2) => {
29191
+ console.log("[DEBUG] find.css tabId:", t2);
29192
+ return createFindAPI(client).css(selector, t2);
29193
+ }),
29194
+ all: (selector) => withTab((t2) => createFindAPI(client).all(selector, t2)),
29195
+ first: (selector) => withTab((t2) => createFindAPI(client).first(selector, t2)),
29196
+ byText: (text) => withTab((t2) => createFindAPI(client).byText(text, t2)),
29197
+ byAttr: (attr, value) => withTab((t2) => createFindAPI(client).byAttr(attr, value, t2)),
29198
+ byTag: (tag) => withTab((t2) => createFindAPI(client).byTag(tag, t2)),
29199
+ byPlaceholder: (text) => withTab((t2) => createFindAPI(client).byPlaceholder(text, t2)),
29200
+ byRole: (role, name2) => withTab((t2) => createFindAPI(client).byRole(role, name2, t2)),
29201
+ children: (selector) => withTab((t2) => createFindAPI(client).children(selector, t2)),
29202
+ parent: (selector) => withTab((t2) => createFindAPI(client).parent(selector, t2)),
29203
+ closest: (selector, ancestor) => withTab((t2) => createFindAPI(client).closest(selector, ancestor, t2)),
29204
+ count: (selector) => withTab((t2) => createFindAPI(client).count(selector, t2)),
29205
+ exists: (selector) => withTab((t2) => createFindAPI(client).exists(selector, t2)),
29206
+ visible: (selector) => withTab((t2) => createFindAPI(client).visible(selector, t2)),
29207
+ enabled: (selector) => withTab((t2) => createFindAPI(client).enabled(selector, t2)),
29208
+ checked: (selector) => withTab((t2) => createFindAPI(client).checked(selector, t2))
29209
+ },
29210
+ provide: {
29211
+ text: (opts) => withTab((t2) => createProvideAPI(client).text(opts, t2)),
29212
+ textAll: (opts) => withTab((t2) => createProvideAPI(client).textAll(opts, t2)),
29213
+ attr: (opts) => withTab((t2) => createProvideAPI(client).attr(opts, t2)),
29214
+ attrAll: (opts) => withTab((t2) => createProvideAPI(client).attrAll(opts, t2)),
29215
+ html: (opts) => withTab((t2) => createProvideAPI(client).html(opts, t2)),
29216
+ table: (opts) => withTab((t2) => createProvideAPI(client).table(opts, t2)),
29217
+ list: (opts) => withTab((t2) => createProvideAPI(client).list(opts, t2)),
29218
+ links: (opts) => withTab((t2) => createProvideAPI(client).links(opts, t2)),
29219
+ images: (opts) => withTab((t2) => createProvideAPI(client).images(opts, t2)),
29220
+ form: (opts) => withTab((t2) => createProvideAPI(client).form(opts, t2)),
29221
+ page: () => withTab((t2) => createProvideAPI(client).page(t2)),
29222
+ div: (opts) => withTab((t2) => createProvideAPI(client).div(opts, t2)),
29223
+ meta: () => withTab((t2) => createProvideAPI(client).meta(t2)),
29224
+ select: (opts) => withTab((t2) => createProvideAPI(client).select(opts, t2)),
29225
+ json: (opts) => withTab((t2) => createProvideAPI(client).json(opts, t2))
29226
+ },
29227
+ iframe: {
29228
+ list: () => withTab((t2) => createIframeAPI(client).list(t2)),
29229
+ evaluate: (opts) => withTab((t2) => createIframeAPI(client).evaluate(opts, t2)),
29230
+ click: (opts) => withTab((t2) => createIframeAPI(client).click(opts, t2)),
29231
+ type: (opts) => withTab((t2) => createIframeAPI(client).type(opts, t2)),
29232
+ text: (opts) => withTab((t2) => createIframeAPI(client).text(opts, t2)),
29233
+ html: (opts) => withTab((t2) => createIframeAPI(client).html(opts, t2)),
29234
+ waitSel: (opts) => withTab((t2) => createIframeAPI(client).waitSel(opts, t2))
29235
+ },
28819
29236
  screenshot: async (filePath) => {
28820
29237
  const r = await withTab((t2) => client.screenshot(filePath, t2));
28821
29238
  logger_default.success(`[${name}] screenshot → ${filePath ?? "base64"}`);
@@ -28826,6 +29243,12 @@ function createSiteObject(name, registeredUrl, client, tabId, pool) {
28826
29243
  logger_default.success(`[${name}] pdf → ${filePath ?? "base64"}`);
28827
29244
  return r;
28828
29245
  },
29246
+ human: {
29247
+ set: (opts) => withTab((t2) => createHumanAPI(client).set(opts, t2)),
29248
+ get: () => withTab((t2) => createHumanAPI(client).get(t2)),
29249
+ type: (opts) => withTab((t2) => createHumanAPI(client).type(opts, t2)),
29250
+ click: (opts) => withTab((t2) => createHumanAPI(client).click(opts, t2))
29251
+ },
28829
29252
  blockImages: () => withTab(async (t2) => {
28830
29253
  await client.blockImages(t2);
28831
29254
  logger_default.info(`[${name}] images blocked`);
@@ -28839,12 +29262,13 @@ function createSiteObject(name, registeredUrl, client, tabId, pool) {
28839
29262
  await withTab((t2) => client.setCookie(cookieName, value, domain, path, t2));
28840
29263
  logger_default.info(`[${name}] cookie set: ${cookieName} @ ${domain}`);
28841
29264
  },
28842
- get: (cookieName) => withTab((t2) => client.getCookie(cookieName, t2)),
28843
- delete: async (cookieName) => {
28844
- await withTab((t2) => client.deleteCookie(cookieName, t2));
29265
+ get: (cookieName, domain = "") => withTab((t2) => client.getCookie(cookieName, domain, t2)),
29266
+ delete: async (cookieName, domain) => {
29267
+ const d = domain ?? new URL(registeredUrl).hostname;
29268
+ await withTab((t2) => client.deleteCookie(cookieName, d, t2));
28845
29269
  logger_default.info(`[${name}] cookie deleted: ${cookieName}`);
28846
29270
  },
28847
- list: () => withTab((t2) => client.listCookies(t2))
29271
+ list: (domain = "") => withTab((t2) => client.listCookies(domain, t2))
28848
29272
  },
28849
29273
  intercept: {
28850
29274
  block: async (pattern) => {
@@ -28931,6 +29355,16 @@ function createSiteObject(name, registeredUrl, client, tabId, pool) {
28931
29355
  logger_default.info(`[${name}] intercept rules cleared`);
28932
29356
  }
28933
29357
  },
29358
+ dialog: {
29359
+ accept: (tabId2 = "default", text) => new DialogClient(client).accept(tabId2, text),
29360
+ dismiss: (tabId2 = "default") => new DialogClient(client).dismiss(tabId2),
29361
+ status: (tabId2 = "default") => new DialogClient(client).status(tabId2),
29362
+ setAutoAction: (tabId2 = "default", action) => new DialogClient(client).setAutoAction(tabId2, action),
29363
+ upload: (selector, filePath, tabId2 = "default") => new DialogClient(client).upload(selector, filePath, tabId2),
29364
+ onDialog: (tabId2, handler) => new DialogClient(client).onDialog(tabId2, handler),
29365
+ waitAndAccept: (tabId2 = "default", text, timeoutMs = 30000) => new DialogClient(client).waitAndAccept(tabId2, text, timeoutMs),
29366
+ waitAndDismiss: (tabId2 = "default", timeoutMs = 30000) => new DialogClient(client).waitAndDismiss(tabId2, timeoutMs)
29367
+ },
28934
29368
  capture: {
28935
29369
  start: () => withTab(async (t2) => {
28936
29370
  await client.captureStart(t2);
@@ -28950,37 +29384,14 @@ function createSiteObject(name, registeredUrl, client, tabId, pool) {
28950
29384
  })
28951
29385
  },
28952
29386
  session: {
28953
- export: async () => {
28954
- const data = await withTab((t2) => client.sessionExport(t2));
28955
- logger_default.success(`[${name}] session exported`);
28956
- return data;
28957
- },
28958
- import: async (data) => {
28959
- await withTab((t2) => client.sessionImport(data, t2));
28960
- logger_default.success(`[${name}] session imported`);
28961
- }
28962
- },
28963
- exposeFunction: async (fnName, handler) => {
28964
- await client.exposeFunction(fnName, handler, tabId);
28965
- logger_default.success(`[${name}] exposed function: ${fnName}`);
28966
- return site;
28967
- },
28968
- unexposeFunction: async (fnName) => {
28969
- await client.unexposeFunction(fnName, tabId);
28970
- logger_default.info(`[${name}] unexposed function: ${fnName}`);
28971
- return site;
28972
- },
28973
- clearExposedFunctions: async () => {
28974
- await client.clearExposedFunctions(tabId);
28975
- logger_default.info(`[${name}] cleared all exposed functions`);
28976
- return site;
28977
- },
28978
- exposeAndInject: async (fnName, handler, injectionJs) => {
28979
- await client.exposeFunction(fnName, handler, tabId);
28980
- const js = typeof injectionJs === "function" ? injectionJs(fnName) : injectionJs;
28981
- await withTab((t2) => client.evaluate(js, t2));
28982
- logger_default.success(`[${name}] exposed and injected: ${fnName}`);
28983
- return site;
29387
+ export: () => withTab((t2) => createSessionAPI(client).export(t2)),
29388
+ import: (data) => withTab((t2) => createSessionAPI(client).import(data, t2)),
29389
+ reload: () => withTab((t2) => createSessionAPI(client).reload(t2)),
29390
+ paths: () => createSessionAPI(client).paths(),
29391
+ cookies: { path: () => createSessionAPI(client).cookiesPath() },
29392
+ profile: { path: () => createSessionAPI(client).profilePath() },
29393
+ ws: { save: (opts) => createSessionAPI(client).setWsSave(opts.enabled) },
29394
+ pings: { save: (opts) => createSessionAPI(client).setPingsSave(opts.enabled) }
28984
29395
  },
28985
29396
  store: async (data, schemaName) => {
28986
29397
  const target = schemaName ?? name;
@@ -29108,139 +29519,24 @@ class CaptureClient {
29108
29519
  stop(tabId = "default") {
29109
29520
  return this.client.send("capture.stop", { tabId });
29110
29521
  }
29111
- requests(tabId = "default") {
29112
- return this.client.send("capture.requests", { tabId });
29113
- }
29114
- ws(tabId = "default") {
29115
- return this.client.send("capture.ws", { tabId });
29116
- }
29117
- cookies(tabId = "default") {
29118
- return this.client.send("capture.cookies", { tabId });
29119
- }
29120
- storage(tabId = "default") {
29121
- return this.client.send("capture.storage", { tabId });
29122
- }
29123
- clear(tabId = "default") {
29124
- return this.client.send("capture.clear", { tabId });
29125
- }
29126
- }
29127
- function createCaptureAPI(client) {
29128
- return new CaptureClient(client);
29129
- }
29130
-
29131
- // piggy/captcha/index.ts
29132
- class CaptchaClient {
29133
- client;
29134
- constructor(client) {
29135
- this.client = client;
29136
- }
29137
- status(tabId = "default") {
29138
- return this.client.send("captcha.status", { tabId });
29139
- }
29140
- resolve(tabId = "default") {
29141
- return this.client.send("captcha.resolve", { tabId });
29142
- }
29143
- pause(tabId = "default") {
29144
- return this.client.send("captcha.pause", { tabId });
29145
- }
29146
- check(tabId = "default") {
29147
- return this.client.send("captcha.check", { tabId });
29148
- }
29149
- setAutoRetry(enabled) {
29150
- return this.client.send("captcha.autoRetry", { enabled });
29151
- }
29152
- blockStatus(tabId = "default") {
29153
- return this.client.send("block.status", { tabId });
29154
- }
29155
- blockRetry(tabId = "default") {
29156
- return this.client.send("block.retry", { tabId });
29157
- }
29158
- onCaptcha(tabId, handler) {
29159
- return this.client.onEvent("captcha", tabId, handler);
29160
- }
29161
- onCaptchaResolved(tabId, handler) {
29162
- return this.client.onEvent("captcha:resolved", tabId, handler);
29163
- }
29164
- onBlocked(tabId, handler) {
29165
- return this.client.onEvent("blocked", tabId, handler);
29166
- }
29167
- onBlockRetry(tabId, handler) {
29168
- return this.client.onEvent("block:retry", tabId, handler);
29169
- }
29170
- waitForResolution(tabId = "default", timeoutMs = 300000) {
29171
- return new Promise((resolve2, reject) => {
29172
- const timer = setTimeout(() => {
29173
- unsub();
29174
- reject(new Error(`captcha.waitForResolution: timed out after ${timeoutMs}ms`));
29175
- }, timeoutMs);
29176
- const unsub = this.onCaptchaResolved(tabId, () => {
29177
- clearTimeout(timer);
29178
- unsub();
29179
- resolve2();
29180
- });
29181
- logger_default.warn(`[captcha] waiting for manual resolution on tab ${tabId}…`);
29182
- });
29183
- }
29184
- }
29185
- function createCaptchaAPI(client) {
29186
- return new CaptchaClient(client);
29187
- }
29188
-
29189
- // piggy/dialog/index.ts
29190
- class DialogClient {
29191
- client;
29192
- constructor(client) {
29193
- this.client = client;
29194
- }
29195
- accept(tabId = "default", text) {
29196
- return this.client.send("dialog.accept", { tabId, ...text !== undefined ? { text } : {} });
29197
- }
29198
- dismiss(tabId = "default") {
29199
- return this.client.send("dialog.dismiss", { tabId });
29200
- }
29201
- status(tabId = "default") {
29202
- return this.client.send("dialog.status", { tabId });
29203
- }
29204
- setAutoAction(tabId = "default", action) {
29205
- return this.client.send("dialog.onDialog", { tabId, action });
29522
+ requests(tabId = "default") {
29523
+ return this.client.send("capture.requests", { tabId });
29206
29524
  }
29207
- upload(selector, filePath, tabId = "default") {
29208
- return this.client.send("upload", { selector, path: filePath, tabId });
29525
+ ws(tabId = "default") {
29526
+ return this.client.send("capture.ws", { tabId });
29209
29527
  }
29210
- onDialog(tabId, handler) {
29211
- return this.client.onEvent("dialog", tabId, handler);
29528
+ cookies(tabId = "default") {
29529
+ return this.client.send("capture.cookies", { tabId });
29212
29530
  }
29213
- waitAndAccept(tabId = "default", text, timeoutMs = 30000) {
29214
- return new Promise((resolve2, reject) => {
29215
- const timer = setTimeout(() => {
29216
- unsub();
29217
- reject(new Error(`dialog.waitAndAccept: timed out after ${timeoutMs}ms`));
29218
- }, timeoutMs);
29219
- const unsub = this.onDialog(tabId, async (data) => {
29220
- clearTimeout(timer);
29221
- unsub();
29222
- await this.accept(tabId, text);
29223
- resolve2(data);
29224
- });
29225
- });
29531
+ storage(tabId = "default") {
29532
+ return this.client.send("capture.storage", { tabId });
29226
29533
  }
29227
- waitAndDismiss(tabId = "default", timeoutMs = 30000) {
29228
- return new Promise((resolve2, reject) => {
29229
- const timer = setTimeout(() => {
29230
- unsub();
29231
- reject(new Error(`dialog.waitAndDismiss: timed out after ${timeoutMs}ms`));
29232
- }, timeoutMs);
29233
- const unsub = this.onDialog(tabId, async (data) => {
29234
- clearTimeout(timer);
29235
- unsub();
29236
- await this.dismiss(tabId);
29237
- resolve2(data);
29238
- });
29239
- });
29534
+ clear(tabId = "default") {
29535
+ return this.client.send("capture.clear", { tabId });
29240
29536
  }
29241
29537
  }
29242
- function createDialogAPI(client) {
29243
- return new DialogClient(client);
29538
+ function createCaptureAPI(client) {
29539
+ return new CaptureClient(client);
29244
29540
  }
29245
29541
 
29246
29542
  // piggy/export/index.ts
@@ -29318,97 +29614,6 @@ function createExportAPI(client) {
29318
29614
  return new ExportClient(client);
29319
29615
  }
29320
29616
 
29321
- // piggy/find/index.ts
29322
- class FindClient {
29323
- client;
29324
- constructor(client) {
29325
- this.client = client;
29326
- }
29327
- css(selector, tabId = "default") {
29328
- return this.client.send("find.css", { selector, tabId });
29329
- }
29330
- all(selector, tabId = "default") {
29331
- return this.client.send("find.all", { selector, tabId });
29332
- }
29333
- first(selector, tabId = "default") {
29334
- return this.client.send("find.first", { selector, tabId });
29335
- }
29336
- byText(text, tabId = "default") {
29337
- return this.client.send("find.byText", { text, tabId });
29338
- }
29339
- byAttr(attr, value, tabId = "default") {
29340
- return this.client.send("find.byAttr", { attr, value, tabId });
29341
- }
29342
- byTag(tag, tabId = "default") {
29343
- return this.client.send("find.byTag", { tag, tabId });
29344
- }
29345
- byPlaceholder(text, tabId = "default") {
29346
- return this.client.send("find.byPlaceholder", { text, tabId });
29347
- }
29348
- byRole(role, name, tabId = "default") {
29349
- return this.client.send("find.byRole", { role, name, tabId });
29350
- }
29351
- children(selector, tabId = "default") {
29352
- return this.client.send("find.children", { selector, tabId });
29353
- }
29354
- parent(selector, tabId = "default") {
29355
- return this.client.send("find.parent", { selector, tabId });
29356
- }
29357
- closest(selector, ancestor, tabId = "default") {
29358
- return this.client.send("find.closest", { selector, ancestor, tabId });
29359
- }
29360
- count(selector, tabId = "default") {
29361
- return this.client.send("find.count", { selector, tabId });
29362
- }
29363
- exists(selector, tabId = "default") {
29364
- return this.client.send("find.exists", { selector, tabId });
29365
- }
29366
- visible(selector, tabId = "default") {
29367
- return this.client.send("find.visible", { selector, tabId });
29368
- }
29369
- enabled(selector, tabId = "default") {
29370
- return this.client.send("find.enabled", { selector, tabId });
29371
- }
29372
- checked(selector, tabId = "default") {
29373
- return this.client.send("find.checked", { selector, tabId });
29374
- }
29375
- }
29376
- function createFindAPI(client) {
29377
- return new FindClient(client);
29378
- }
29379
-
29380
- // piggy/iframe/index.ts
29381
- class IframeClient {
29382
- client;
29383
- constructor(client) {
29384
- this.client = client;
29385
- }
29386
- list(tabId = "default") {
29387
- return this.client.send("iframe.list", { tabId });
29388
- }
29389
- evaluate(opts, tabId = "default") {
29390
- return this.client.send("iframe.evaluate", { ...opts, tabId });
29391
- }
29392
- click(opts, tabId = "default") {
29393
- return this.client.send("iframe.click", { ...opts, tabId });
29394
- }
29395
- type(opts, tabId = "default") {
29396
- return this.client.send("iframe.type", { ...opts, tabId });
29397
- }
29398
- text(opts, tabId = "default") {
29399
- return this.client.send("iframe.text", { ...opts, tabId });
29400
- }
29401
- html(opts, tabId = "default") {
29402
- return this.client.send("iframe.html", { ...opts, tabId });
29403
- }
29404
- waitSel(opts, tabId = "default") {
29405
- return this.client.send("iframe.waitSel", { ...opts, tabId });
29406
- }
29407
- }
29408
- function createIframeAPI(client) {
29409
- return new IframeClient(client);
29410
- }
29411
-
29412
29617
  // piggy/interactions/index.ts
29413
29618
  class InteractionsClient {
29414
29619
  client;
@@ -29535,62 +29740,6 @@ function createNavigationAPI(client) {
29535
29740
  return new NavigationClient(client);
29536
29741
  }
29537
29742
 
29538
- // piggy/provide/index.ts
29539
- class ProvideClient {
29540
- client;
29541
- constructor(client) {
29542
- this.client = client;
29543
- }
29544
- text(opts, tabId = "default") {
29545
- return this.client.send("provide.text", { ...opts, tabId });
29546
- }
29547
- textAll(opts, tabId = "default") {
29548
- return this.client.send("provide.textAll", { ...opts, tabId });
29549
- }
29550
- attr(opts, tabId = "default") {
29551
- return this.client.send("provide.attr", { ...opts, tabId });
29552
- }
29553
- attrAll(opts, tabId = "default") {
29554
- return this.client.send("provide.attrAll", { ...opts, tabId });
29555
- }
29556
- html(opts, tabId = "default") {
29557
- return this.client.send("provide.html", { ...opts, tabId });
29558
- }
29559
- table(opts, tabId = "default") {
29560
- return this.client.send("provide.table", { ...opts, tabId });
29561
- }
29562
- list(opts, tabId = "default") {
29563
- return this.client.send("provide.list", { ...opts, tabId });
29564
- }
29565
- links(opts, tabId = "default") {
29566
- return this.client.send("provide.links", { ...opts, tabId });
29567
- }
29568
- images(opts, tabId = "default") {
29569
- return this.client.send("provide.images", { ...opts, tabId });
29570
- }
29571
- form(opts, tabId = "default") {
29572
- return this.client.send("provide.form", { ...opts, tabId });
29573
- }
29574
- page(tabId = "default") {
29575
- return this.client.send("provide.page", { tabId });
29576
- }
29577
- div(opts, tabId = "default") {
29578
- return this.client.send("provide.div", { ...opts, tabId });
29579
- }
29580
- meta(tabId = "default") {
29581
- return this.client.send("provide.meta", { tabId });
29582
- }
29583
- select(opts, tabId = "default") {
29584
- return this.client.send("provide.select", { ...opts, tabId });
29585
- }
29586
- json(opts, tabId = "default") {
29587
- return this.client.send("provide.json", { ...opts, tabId });
29588
- }
29589
- }
29590
- function createProvideAPI(client) {
29591
- return new ProvideClient(client);
29592
- }
29593
-
29594
29743
  // piggy/proxy/index.ts
29595
29744
  class ProxyClient {
29596
29745
  client;
@@ -29650,57 +29799,6 @@ function createProxyAPI(client) {
29650
29799
  return new ProxyClient(client);
29651
29800
  }
29652
29801
 
29653
- // piggy/session/index.ts
29654
- class SessionClient {
29655
- client;
29656
- constructor(client) {
29657
- this.client = client;
29658
- }
29659
- reload(tabId = "default") {
29660
- return this.client.send("session.reload", { tabId });
29661
- }
29662
- paths() {
29663
- return this.client.send("session.paths", {});
29664
- }
29665
- cookiesPath() {
29666
- return this.client.send("session.cookies.path", {});
29667
- }
29668
- profilePath() {
29669
- return this.client.send("session.profile.path", {});
29670
- }
29671
- wsPath() {
29672
- return this.client.send("session.ws.path", {});
29673
- }
29674
- pingsPath() {
29675
- return this.client.send("session.pings.path", {});
29676
- }
29677
- setWsSave(enabled) {
29678
- return this.client.send("session.ws.save", { enabled });
29679
- }
29680
- setPingsSave(enabled) {
29681
- return this.client.send("session.pings.save", { enabled });
29682
- }
29683
- async export(tabId = "default") {
29684
- const raw = await this.client.send("session.export", { tabId });
29685
- return typeof raw === "string" ? JSON.parse(raw) : raw;
29686
- }
29687
- import(data, tabId = "default") {
29688
- return this.client.send("session.import", {
29689
- data: JSON.stringify(data),
29690
- tabId
29691
- });
29692
- }
29693
- setCookie(opts, tabId = "default") {
29694
- return this.client.send("cookie.set", { ...opts, tabId });
29695
- }
29696
- deleteCookie(opts, tabId = "default") {
29697
- return this.client.send("cookie.delete", { ...opts, tabId });
29698
- }
29699
- }
29700
- function createSessionAPI(client) {
29701
- return new SessionClient(client);
29702
- }
29703
-
29704
29802
  // piggy/tabs/index.ts
29705
29803
  class TabsClient {
29706
29804
  client;
@@ -29710,7 +29808,8 @@ class TabsClient {
29710
29808
  new() {
29711
29809
  return this.client.send("tab.new", {});
29712
29810
  }
29713
- close(tabId) {
29811
+ close(opts) {
29812
+ const tabId = typeof opts === "string" ? opts : opts.tabId;
29714
29813
  return this.client.send("tab.close", { tabId });
29715
29814
  }
29716
29815
  list() {
@@ -29867,6 +29966,7 @@ var _router = null;
29867
29966
  var _tabMode = "tab";
29868
29967
  var _extraProcs = [];
29869
29968
  var _sites = {};
29969
+ var _singleSiteName = null;
29870
29970
  function guardClient() {
29871
29971
  if (!_client)
29872
29972
  throw new Error("No client. Call piggy.launch() or piggy.connect() first.");
@@ -29903,9 +30003,15 @@ var piggy = {
29903
30003
  throw new Error(`No URL for site "${name}"`);
29904
30004
  const binaryMode = opts?.binary ?? "headless";
29905
30005
  const poolSize = opts?.pool ?? 0;
30006
+ const isSingle = opts?.single === true;
30007
+ if (isSingle && _singleSiteName && _singleSiteName !== name) {
30008
+ throw new Error(`piggy: site "${_singleSiteName}" is already registered as single. ` + `Only one site may use { single: true } at a time.`);
30009
+ }
29906
30010
  if (_tabMode === "tab") {
29907
30011
  const client = guardClient();
29908
30012
  if (poolSize > 1) {
30013
+ if (isSingle)
30014
+ throw new Error("piggy: { single: true } is incompatible with pool > 1");
29909
30015
  const pool = new TabPool(client, poolSize, url2, name);
29910
30016
  await pool.init();
29911
30017
  const siteObj = createSiteObject(name, url2, client, "default", pool);
@@ -29913,13 +30019,20 @@ var piggy = {
29913
30019
  piggy[name] = siteObj;
29914
30020
  logger_default.success(`[${name}] registered with pool of ${poolSize} tabs`);
29915
30021
  } else {
29916
- const tabId = await client.newTab();
30022
+ const tabId = isSingle ? "default" : await client.newTab();
29917
30023
  const siteObj = createSiteObject(name, url2, client, tabId);
29918
30024
  _sites[name] = siteObj;
29919
30025
  piggy[name] = siteObj;
29920
- logger_default.success(`[${name}] registered as tab ${tabId}`);
30026
+ if (isSingle) {
30027
+ _singleSiteName = name;
30028
+ logger_default.success(`[${name}] registered as single-tab site (default tab)`);
30029
+ } else {
30030
+ logger_default.success(`[${name}] registered as tab ${tabId}`);
30031
+ }
29921
30032
  }
29922
30033
  } else {
30034
+ if (isSingle)
30035
+ throw new Error("piggy: { single: true } is only supported in tab mode");
29923
30036
  const socketName = `piggy_${name}`;
29924
30037
  await spawnBrowserOnSocket(socketName, binaryMode);
29925
30038
  await new Promise((r) => setTimeout(r, 500));
@@ -29933,9 +30046,34 @@ var piggy = {
29933
30046
  }
29934
30047
  return piggy;
29935
30048
  },
30049
+ extend: async (...installers) => {
30050
+ if (!_singleSiteName) {
30051
+ throw new Error(`piggy.extend() requires a site registered with { single: true }.
30052
+ ` + 'Example: await piggy.register("mysite", url, { single: true })');
30053
+ }
30054
+ if (installers.length === 0) {
30055
+ logger_default.warn("[piggy] extend() called with no plugins — nothing to do");
30056
+ return piggy;
30057
+ }
30058
+ const site = _sites[_singleSiteName];
30059
+ if (!site) {
30060
+ throw new Error(`piggy.extend(): site "${_singleSiteName}" not found — register it first`);
30061
+ }
30062
+ for (const installer of installers) {
30063
+ if (typeof installer !== "function") {
30064
+ throw new Error("piggy.extend(): each argument must be a plugin installer function");
30065
+ }
30066
+ await installer(site);
30067
+ }
30068
+ logger_default.success(`[piggy] ${installers.length} plugin(s) installed on "${_singleSiteName}"`);
30069
+ return piggy;
30070
+ },
29936
30071
  get tabs() {
29937
30072
  return _router?.tabs ?? createTabsAPI(guardClient());
29938
30073
  },
30074
+ get tab() {
30075
+ return _router?.tabs ?? createTabsAPI(guardClient());
30076
+ },
29939
30077
  get navigation() {
29940
30078
  return _router?.navigation ?? createNavigationAPI(guardClient());
29941
30079
  },
@@ -30045,6 +30183,7 @@ var piggy = {
30045
30183
  }),
30046
30184
  close: async (opts) => {
30047
30185
  stopServer();
30186
+ _singleSiteName = null;
30048
30187
  if (opts?.force) {
30049
30188
  for (const { client: c } of _extraProcs)
30050
30189
  c.disconnect();