nothing-browser 0.0.9 → 0.0.11
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/client/index.js +39 -19
- package/dist/piggy.js +289 -23
- package/dist/register/index.js +242 -0
- package/package.json +1 -1
- package/piggy/client/index.ts +117 -247
- package/piggy/intercept/scripts.ts +153 -0
- package/piggy/register/index.ts +182 -36
- package/piggy.ts +11 -5
package/dist/client/index.js
CHANGED
|
@@ -746,6 +746,7 @@ class PiggyClient {
|
|
|
746
746
|
buf = "";
|
|
747
747
|
eventBuffer = "";
|
|
748
748
|
eventHandlers = new Map;
|
|
749
|
+
globalEventHandlers = new Map;
|
|
749
750
|
constructor(socketPath = SOCKET_PATH) {
|
|
750
751
|
this.socketPath = socketPath;
|
|
751
752
|
this.eventHandlers.set("default", new Map);
|
|
@@ -806,21 +807,12 @@ class PiggyClient {
|
|
|
806
807
|
if (handler) {
|
|
807
808
|
Promise.resolve(handler(JSON.parse(data || "null"))).then((response) => {
|
|
808
809
|
if (response && typeof response === "object" && "success" in response) {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
}).catch((e) => logger_default.error(`Failed to send exposed result: ${e}`));
|
|
816
|
-
} else {
|
|
817
|
-
this.send("exposed.result", {
|
|
818
|
-
tabId: effectiveTabId,
|
|
819
|
-
callId,
|
|
820
|
-
result: response.error || "Unknown error",
|
|
821
|
-
isError: true
|
|
822
|
-
}).catch((e) => logger_default.error(`Failed to send exposed error: ${e}`));
|
|
823
|
-
}
|
|
810
|
+
this.send("exposed.result", {
|
|
811
|
+
tabId: effectiveTabId,
|
|
812
|
+
callId,
|
|
813
|
+
result: response.success ? JSON.stringify(response.result) : response.error || "Unknown error",
|
|
814
|
+
isError: !response.success
|
|
815
|
+
}).catch((e) => logger_default.error(`Failed to send exposed result: ${e}`));
|
|
824
816
|
} else {
|
|
825
817
|
this.send("exposed.result", {
|
|
826
818
|
tabId: effectiveTabId,
|
|
@@ -840,8 +832,38 @@ class PiggyClient {
|
|
|
840
832
|
} else {
|
|
841
833
|
logger_default.warn(`No handler for exposed function: ${name} in tab ${effectiveTabId}`);
|
|
842
834
|
}
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
if (event.event === "navigate") {
|
|
838
|
+
const handlers = this.globalEventHandlers.get(`navigate:${event.tabId}`);
|
|
839
|
+
if (handlers) {
|
|
840
|
+
for (const h of handlers) {
|
|
841
|
+
try {
|
|
842
|
+
h(event.url);
|
|
843
|
+
} catch (e) {
|
|
844
|
+
logger_default.error(`navigate handler error: ${e}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
const wildcard = this.globalEventHandlers.get("navigate:*");
|
|
849
|
+
if (wildcard) {
|
|
850
|
+
for (const h of wildcard) {
|
|
851
|
+
try {
|
|
852
|
+
h({ url: event.url, tabId: event.tabId });
|
|
853
|
+
} catch {}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return;
|
|
843
857
|
}
|
|
844
858
|
}
|
|
859
|
+
onEvent(eventName, tabId, handler) {
|
|
860
|
+
const key = `${eventName}:${tabId}`;
|
|
861
|
+
if (!this.globalEventHandlers.has(key)) {
|
|
862
|
+
this.globalEventHandlers.set(key, new Set);
|
|
863
|
+
}
|
|
864
|
+
this.globalEventHandlers.get(key).add(handler);
|
|
865
|
+
return () => this.globalEventHandlers.get(key)?.delete(handler);
|
|
866
|
+
}
|
|
845
867
|
disconnect() {
|
|
846
868
|
this.socket?.destroy();
|
|
847
869
|
this.socket = null;
|
|
@@ -1023,15 +1045,13 @@ class PiggyClient {
|
|
|
1023
1045
|
await this.send("session.import", { data, tabId });
|
|
1024
1046
|
}
|
|
1025
1047
|
async exposeFunction(name, handler, tabId = "default") {
|
|
1026
|
-
if (!this.eventHandlers.has(tabId))
|
|
1048
|
+
if (!this.eventHandlers.has(tabId))
|
|
1027
1049
|
this.eventHandlers.set(tabId, new Map);
|
|
1028
|
-
}
|
|
1029
1050
|
this.eventHandlers.get(tabId).set(name, async (data) => {
|
|
1030
1051
|
try {
|
|
1031
1052
|
const result = await handler(data);
|
|
1032
|
-
if (result && typeof result === "object" && (("success" in result) || ("error" in result)))
|
|
1053
|
+
if (result && typeof result === "object" && (("success" in result) || ("error" in result)))
|
|
1033
1054
|
return result;
|
|
1034
|
-
}
|
|
1035
1055
|
return { success: true, result };
|
|
1036
1056
|
} catch (err) {
|
|
1037
1057
|
return { success: false, error: err.message || String(err) };
|
package/dist/piggy.js
CHANGED
|
@@ -6274,6 +6274,7 @@ class PiggyClient {
|
|
|
6274
6274
|
buf = "";
|
|
6275
6275
|
eventBuffer = "";
|
|
6276
6276
|
eventHandlers = new Map;
|
|
6277
|
+
globalEventHandlers = new Map;
|
|
6277
6278
|
constructor(socketPath = SOCKET_PATH) {
|
|
6278
6279
|
this.socketPath = socketPath;
|
|
6279
6280
|
this.eventHandlers.set("default", new Map);
|
|
@@ -6334,21 +6335,12 @@ class PiggyClient {
|
|
|
6334
6335
|
if (handler) {
|
|
6335
6336
|
Promise.resolve(handler(JSON.parse(data || "null"))).then((response) => {
|
|
6336
6337
|
if (response && typeof response === "object" && "success" in response) {
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
}).catch((e) => logger_default.error(`Failed to send exposed result: ${e}`));
|
|
6344
|
-
} else {
|
|
6345
|
-
this.send("exposed.result", {
|
|
6346
|
-
tabId: effectiveTabId,
|
|
6347
|
-
callId,
|
|
6348
|
-
result: response.error || "Unknown error",
|
|
6349
|
-
isError: true
|
|
6350
|
-
}).catch((e) => logger_default.error(`Failed to send exposed error: ${e}`));
|
|
6351
|
-
}
|
|
6338
|
+
this.send("exposed.result", {
|
|
6339
|
+
tabId: effectiveTabId,
|
|
6340
|
+
callId,
|
|
6341
|
+
result: response.success ? JSON.stringify(response.result) : response.error || "Unknown error",
|
|
6342
|
+
isError: !response.success
|
|
6343
|
+
}).catch((e) => logger_default.error(`Failed to send exposed result: ${e}`));
|
|
6352
6344
|
} else {
|
|
6353
6345
|
this.send("exposed.result", {
|
|
6354
6346
|
tabId: effectiveTabId,
|
|
@@ -6368,8 +6360,38 @@ class PiggyClient {
|
|
|
6368
6360
|
} else {
|
|
6369
6361
|
logger_default.warn(`No handler for exposed function: ${name} in tab ${effectiveTabId}`);
|
|
6370
6362
|
}
|
|
6363
|
+
return;
|
|
6364
|
+
}
|
|
6365
|
+
if (event.event === "navigate") {
|
|
6366
|
+
const handlers = this.globalEventHandlers.get(`navigate:${event.tabId}`);
|
|
6367
|
+
if (handlers) {
|
|
6368
|
+
for (const h of handlers) {
|
|
6369
|
+
try {
|
|
6370
|
+
h(event.url);
|
|
6371
|
+
} catch (e) {
|
|
6372
|
+
logger_default.error(`navigate handler error: ${e}`);
|
|
6373
|
+
}
|
|
6374
|
+
}
|
|
6375
|
+
}
|
|
6376
|
+
const wildcard = this.globalEventHandlers.get("navigate:*");
|
|
6377
|
+
if (wildcard) {
|
|
6378
|
+
for (const h of wildcard) {
|
|
6379
|
+
try {
|
|
6380
|
+
h({ url: event.url, tabId: event.tabId });
|
|
6381
|
+
} catch {}
|
|
6382
|
+
}
|
|
6383
|
+
}
|
|
6384
|
+
return;
|
|
6371
6385
|
}
|
|
6372
6386
|
}
|
|
6387
|
+
onEvent(eventName, tabId, handler) {
|
|
6388
|
+
const key = `${eventName}:${tabId}`;
|
|
6389
|
+
if (!this.globalEventHandlers.has(key)) {
|
|
6390
|
+
this.globalEventHandlers.set(key, new Set);
|
|
6391
|
+
}
|
|
6392
|
+
this.globalEventHandlers.get(key).add(handler);
|
|
6393
|
+
return () => this.globalEventHandlers.get(key)?.delete(handler);
|
|
6394
|
+
}
|
|
6373
6395
|
disconnect() {
|
|
6374
6396
|
this.socket?.destroy();
|
|
6375
6397
|
this.socket = null;
|
|
@@ -6551,15 +6573,13 @@ class PiggyClient {
|
|
|
6551
6573
|
await this.send("session.import", { data, tabId });
|
|
6552
6574
|
}
|
|
6553
6575
|
async exposeFunction(name, handler, tabId = "default") {
|
|
6554
|
-
if (!this.eventHandlers.has(tabId))
|
|
6576
|
+
if (!this.eventHandlers.has(tabId))
|
|
6555
6577
|
this.eventHandlers.set(tabId, new Map);
|
|
6556
|
-
}
|
|
6557
6578
|
this.eventHandlers.get(tabId).set(name, async (data) => {
|
|
6558
6579
|
try {
|
|
6559
6580
|
const result = await handler(data);
|
|
6560
|
-
if (result && typeof result === "object" && (("success" in result) || ("error" in result)))
|
|
6581
|
+
if (result && typeof result === "object" && (("success" in result) || ("error" in result)))
|
|
6561
6582
|
return result;
|
|
6562
|
-
}
|
|
6563
6583
|
return { success: true, result };
|
|
6564
6584
|
} catch (err) {
|
|
6565
6585
|
return { success: false, error: err.message || String(err) };
|
|
@@ -21191,6 +21211,139 @@ function humanTypeSequence(text) {
|
|
|
21191
21211
|
return actions;
|
|
21192
21212
|
}
|
|
21193
21213
|
|
|
21214
|
+
// piggy/intercept/scripts.ts
|
|
21215
|
+
function buildRespondScript(pattern, status2, contentType, body) {
|
|
21216
|
+
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
21217
|
+
const safeBody = body.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
21218
|
+
const safeContentType = contentType.replace(/'/g, "\\'");
|
|
21219
|
+
return `
|
|
21220
|
+
(function() {
|
|
21221
|
+
'use strict';
|
|
21222
|
+
if (!window.__PIGGY_RESPOND_RULES__) window.__PIGGY_RESPOND_RULES__ = [];
|
|
21223
|
+
window.__PIGGY_RESPOND_RULES__.push({
|
|
21224
|
+
pattern: '${safePattern}',
|
|
21225
|
+
status: ${status2},
|
|
21226
|
+
contentType: '${safeContentType}',
|
|
21227
|
+
body: \`${safeBody}\`
|
|
21228
|
+
});
|
|
21229
|
+
|
|
21230
|
+
function _piggyMatchUrl(url, pattern) {
|
|
21231
|
+
try { return url.includes(pattern) || new RegExp(pattern).test(url); }
|
|
21232
|
+
catch { return url.includes(pattern); }
|
|
21233
|
+
}
|
|
21234
|
+
|
|
21235
|
+
// Only install wrappers once per page
|
|
21236
|
+
if (window.__PIGGY_RESPOND_INSTALLED__) return;
|
|
21237
|
+
window.__PIGGY_RESPOND_INSTALLED__ = true;
|
|
21238
|
+
|
|
21239
|
+
// ── fetch wrapper ──────────────────────────────────────────────────────────
|
|
21240
|
+
const _origFetch = window.fetch;
|
|
21241
|
+
window.fetch = function(input, init) {
|
|
21242
|
+
const url = typeof input === 'string' ? input : (input?.url ?? String(input));
|
|
21243
|
+
const rules = window.__PIGGY_RESPOND_RULES__ || [];
|
|
21244
|
+
for (const rule of rules) {
|
|
21245
|
+
if (_piggyMatchUrl(url, rule.pattern)) {
|
|
21246
|
+
return Promise.resolve(new Response(rule.body, {
|
|
21247
|
+
status: rule.status,
|
|
21248
|
+
headers: { 'Content-Type': rule.contentType }
|
|
21249
|
+
}));
|
|
21250
|
+
}
|
|
21251
|
+
}
|
|
21252
|
+
return _origFetch.apply(this, arguments);
|
|
21253
|
+
};
|
|
21254
|
+
|
|
21255
|
+
// ── XHR wrapper ────────────────────────────────────────────────────────────
|
|
21256
|
+
const _origOpen = XMLHttpRequest.prototype.open;
|
|
21257
|
+
const _origSend = XMLHttpRequest.prototype.send;
|
|
21258
|
+
|
|
21259
|
+
XMLHttpRequest.prototype.open = function(method, url) {
|
|
21260
|
+
this.__piggy_url__ = String(url);
|
|
21261
|
+
return _origOpen.apply(this, arguments);
|
|
21262
|
+
};
|
|
21263
|
+
|
|
21264
|
+
XMLHttpRequest.prototype.send = function() {
|
|
21265
|
+
const url = this.__piggy_url__ || '';
|
|
21266
|
+
const rules = window.__PIGGY_RESPOND_RULES__ || [];
|
|
21267
|
+
for (const rule of rules) {
|
|
21268
|
+
if (_piggyMatchUrl(url, rule.pattern)) {
|
|
21269
|
+
const self = this;
|
|
21270
|
+
Object.defineProperty(self, 'readyState', { get: () => 4, configurable: true });
|
|
21271
|
+
Object.defineProperty(self, 'status', { get: () => rule.status, configurable: true });
|
|
21272
|
+
Object.defineProperty(self, 'responseText', { get: () => rule.body, configurable: true });
|
|
21273
|
+
Object.defineProperty(self, 'response', { get: () => rule.body, configurable: true });
|
|
21274
|
+
setTimeout(() => {
|
|
21275
|
+
if (typeof self.onreadystatechange === 'function') self.onreadystatechange();
|
|
21276
|
+
self.dispatchEvent(new Event('readystatechange'));
|
|
21277
|
+
self.dispatchEvent(new Event('load'));
|
|
21278
|
+
self.dispatchEvent(new Event('loadend'));
|
|
21279
|
+
}, 0);
|
|
21280
|
+
return;
|
|
21281
|
+
}
|
|
21282
|
+
}
|
|
21283
|
+
return _origSend.apply(this, arguments);
|
|
21284
|
+
};
|
|
21285
|
+
})();
|
|
21286
|
+
`;
|
|
21287
|
+
}
|
|
21288
|
+
function buildModifyResponseScript(pattern, exposedFnName) {
|
|
21289
|
+
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
21290
|
+
const safeFnName = exposedFnName.replace(/'/g, "\\'");
|
|
21291
|
+
return `
|
|
21292
|
+
(function() {
|
|
21293
|
+
'use strict';
|
|
21294
|
+
if (!window.__PIGGY_MODIFY_RULES__) window.__PIGGY_MODIFY_RULES__ = [];
|
|
21295
|
+
window.__PIGGY_MODIFY_RULES__.push({ pattern: '${safePattern}', fn: '${safeFnName}' });
|
|
21296
|
+
|
|
21297
|
+
function _piggyMatchUrl(url, pattern) {
|
|
21298
|
+
try { return url.includes(pattern) || new RegExp(pattern).test(url); }
|
|
21299
|
+
catch { return url.includes(pattern); }
|
|
21300
|
+
}
|
|
21301
|
+
|
|
21302
|
+
// Only install wrappers once per page
|
|
21303
|
+
if (window.__PIGGY_MODIFY_INSTALLED__) return;
|
|
21304
|
+
window.__PIGGY_MODIFY_INSTALLED__ = true;
|
|
21305
|
+
|
|
21306
|
+
const _origFetch = window.fetch;
|
|
21307
|
+
window.fetch = async function(input, init) {
|
|
21308
|
+
const url = typeof input === 'string' ? input : (input?.url ?? String(input));
|
|
21309
|
+
const rules = window.__PIGGY_MODIFY_RULES__ || [];
|
|
21310
|
+
|
|
21311
|
+
let matchedFn = null;
|
|
21312
|
+
for (const rule of rules) {
|
|
21313
|
+
if (_piggyMatchUrl(url, rule.pattern)) { matchedFn = rule.fn; break; }
|
|
21314
|
+
}
|
|
21315
|
+
|
|
21316
|
+
// No match — pass through untouched
|
|
21317
|
+
const resp = await _origFetch.apply(this, arguments);
|
|
21318
|
+
if (!matchedFn) return resp;
|
|
21319
|
+
|
|
21320
|
+
try {
|
|
21321
|
+
const bodyText = await resp.clone().text();
|
|
21322
|
+
const headers = {};
|
|
21323
|
+
resp.headers.forEach((v, k) => { headers[k] = v; });
|
|
21324
|
+
|
|
21325
|
+
const handlerFn = window[matchedFn];
|
|
21326
|
+
if (typeof handlerFn !== 'function') return resp;
|
|
21327
|
+
|
|
21328
|
+
// Call Node.js handler via exposeFunction bridge
|
|
21329
|
+
const mod = await handlerFn({ body: bodyText, status: resp.status, headers });
|
|
21330
|
+
if (!mod || typeof mod !== 'object' || Object.keys(mod).length === 0) return resp;
|
|
21331
|
+
|
|
21332
|
+
return new Response(
|
|
21333
|
+
mod.body !== undefined ? mod.body : bodyText,
|
|
21334
|
+
{
|
|
21335
|
+
status: mod.status !== undefined ? mod.status : resp.status,
|
|
21336
|
+
headers: mod.headers !== undefined ? mod.headers : headers,
|
|
21337
|
+
}
|
|
21338
|
+
);
|
|
21339
|
+
} catch {
|
|
21340
|
+
return resp; // On any error, pass through original response
|
|
21341
|
+
}
|
|
21342
|
+
};
|
|
21343
|
+
})();
|
|
21344
|
+
`;
|
|
21345
|
+
}
|
|
21346
|
+
|
|
21194
21347
|
// piggy/register/index.ts
|
|
21195
21348
|
var globalClient = null;
|
|
21196
21349
|
var humanMode = false;
|
|
@@ -21217,6 +21370,20 @@ async function retry(label, fn, retries = 2, backoff = 150) {
|
|
|
21217
21370
|
}
|
|
21218
21371
|
function createSiteObject(name, registeredUrl, client, tabId) {
|
|
21219
21372
|
let _currentUrl = registeredUrl;
|
|
21373
|
+
const _eventListeners = new Map;
|
|
21374
|
+
const _unsubNavigate = client.onEvent("navigate", tabId, (url) => {
|
|
21375
|
+
_currentUrl = url;
|
|
21376
|
+
const handlers = _eventListeners.get("navigate");
|
|
21377
|
+
if (handlers) {
|
|
21378
|
+
for (const h of handlers) {
|
|
21379
|
+
try {
|
|
21380
|
+
h(url);
|
|
21381
|
+
} catch (e) {
|
|
21382
|
+
logger_default.error(`[${name}] navigate handler error: ${e}`);
|
|
21383
|
+
}
|
|
21384
|
+
}
|
|
21385
|
+
}
|
|
21386
|
+
});
|
|
21220
21387
|
const withErrScreen = async (fn, label) => {
|
|
21221
21388
|
try {
|
|
21222
21389
|
return await fn();
|
|
@@ -21231,6 +21398,7 @@ function createSiteObject(name, registeredUrl, client, tabId) {
|
|
|
21231
21398
|
throw err;
|
|
21232
21399
|
}
|
|
21233
21400
|
};
|
|
21401
|
+
let _modifyRuleCounter = 0;
|
|
21234
21402
|
const site = {
|
|
21235
21403
|
_name: name,
|
|
21236
21404
|
_tabId: tabId,
|
|
@@ -21269,6 +21437,19 @@ function createSiteObject(name, registeredUrl, client, tabId) {
|
|
|
21269
21437
|
logger_default.success(`[${name}] init script added`);
|
|
21270
21438
|
return site;
|
|
21271
21439
|
},
|
|
21440
|
+
on: (event, handler) => {
|
|
21441
|
+
if (!_eventListeners.has(event))
|
|
21442
|
+
_eventListeners.set(event, new Set);
|
|
21443
|
+
_eventListeners.get(event).add(handler);
|
|
21444
|
+
logger_default.debug(`[${name}] on('${event}') registered`);
|
|
21445
|
+
return () => {
|
|
21446
|
+
_eventListeners.get(event)?.delete(handler);
|
|
21447
|
+
logger_default.debug(`[${name}] on('${event}') unsubscribed`);
|
|
21448
|
+
};
|
|
21449
|
+
},
|
|
21450
|
+
off: (event, handler) => {
|
|
21451
|
+
_eventListeners.get(event)?.delete(handler);
|
|
21452
|
+
},
|
|
21272
21453
|
click: (selector, opts) => withErrScreen(() => retry(name, async () => {
|
|
21273
21454
|
if (humanMode)
|
|
21274
21455
|
await randomDelay(80, 220);
|
|
@@ -21402,6 +21583,86 @@ function createSiteObject(name, registeredUrl, client, tabId) {
|
|
|
21402
21583
|
await client.addInterceptRule("modifyHeaders", pattern, { headers }, tabId);
|
|
21403
21584
|
logger_default.info(`[${name}] intercept modifyHeaders: ${pattern}`);
|
|
21404
21585
|
},
|
|
21586
|
+
respond: async (pattern, handlerOrResponse) => {
|
|
21587
|
+
const isStatic = typeof handlerOrResponse === "object";
|
|
21588
|
+
const response = isStatic ? handlerOrResponse : { status: 200, contentType: "application/json", body: "" };
|
|
21589
|
+
if (!isStatic) {
|
|
21590
|
+
const fnName = `__piggy_respond_${name}_${++_modifyRuleCounter}__`;
|
|
21591
|
+
await client.exposeFunction(fnName, async (req) => {
|
|
21592
|
+
try {
|
|
21593
|
+
const result = handlerOrResponse(req);
|
|
21594
|
+
return {
|
|
21595
|
+
success: true,
|
|
21596
|
+
result: {
|
|
21597
|
+
status: result.status ?? 200,
|
|
21598
|
+
contentType: result.contentType ?? "application/json",
|
|
21599
|
+
body: result.body ?? ""
|
|
21600
|
+
}
|
|
21601
|
+
};
|
|
21602
|
+
} catch (e) {
|
|
21603
|
+
return { success: false, error: e.message };
|
|
21604
|
+
}
|
|
21605
|
+
}, tabId);
|
|
21606
|
+
const dynamicScript = `
|
|
21607
|
+
(function() {
|
|
21608
|
+
'use strict';
|
|
21609
|
+
if (!window.__PIGGY_DYNAMIC_RESPOND__) window.__PIGGY_DYNAMIC_RESPOND__ = [];
|
|
21610
|
+
window.__PIGGY_DYNAMIC_RESPOND__.push({ pattern: ${JSON.stringify(pattern)}, fn: ${JSON.stringify(fnName)} });
|
|
21611
|
+
|
|
21612
|
+
function matchUrl(url, pattern) {
|
|
21613
|
+
try { return url.includes(pattern) || new RegExp(pattern).test(url); }
|
|
21614
|
+
catch { return url.includes(pattern); }
|
|
21615
|
+
}
|
|
21616
|
+
|
|
21617
|
+
if (window.__PIGGY_DYN_INSTALLED__) return;
|
|
21618
|
+
window.__PIGGY_DYN_INSTALLED__ = true;
|
|
21619
|
+
|
|
21620
|
+
const _origFetch = window.fetch;
|
|
21621
|
+
window.fetch = async function(input, init) {
|
|
21622
|
+
const url = typeof input === 'string' ? input : (input?.url ?? String(input));
|
|
21623
|
+
const method = (init?.method ?? 'GET').toUpperCase();
|
|
21624
|
+
const rules = window.__PIGGY_DYNAMIC_RESPOND__ || [];
|
|
21625
|
+
for (const rule of rules) {
|
|
21626
|
+
if (matchUrl(url, rule.pattern) && typeof window[rule.fn] === 'function') {
|
|
21627
|
+
try {
|
|
21628
|
+
const r = await window[rule.fn]({ url, method });
|
|
21629
|
+
return new Response(r.body ?? '', {
|
|
21630
|
+
status: r.status ?? 200,
|
|
21631
|
+
headers: { 'Content-Type': r.contentType ?? 'application/json' }
|
|
21632
|
+
});
|
|
21633
|
+
} catch { break; }
|
|
21634
|
+
}
|
|
21635
|
+
}
|
|
21636
|
+
return _origFetch.apply(this, arguments);
|
|
21637
|
+
};
|
|
21638
|
+
})();`;
|
|
21639
|
+
await client.addInitScript(dynamicScript, tabId);
|
|
21640
|
+
await client.evaluate(dynamicScript, tabId);
|
|
21641
|
+
logger_default.success(`[${name}] intercept.respond (dynamic): ${pattern}`);
|
|
21642
|
+
return site;
|
|
21643
|
+
}
|
|
21644
|
+
const script = buildRespondScript(pattern, response.status ?? 200, response.contentType ?? "application/json", response.body);
|
|
21645
|
+
await client.addInitScript(script, tabId);
|
|
21646
|
+
await client.evaluate(script, tabId);
|
|
21647
|
+
logger_default.success(`[${name}] intercept.respond (static): ${pattern} → ${response.status ?? 200}`);
|
|
21648
|
+
return site;
|
|
21649
|
+
},
|
|
21650
|
+
modifyResponse: async (pattern, handler) => {
|
|
21651
|
+
const fnName = `__piggy_modres_${name}_${++_modifyRuleCounter}__`;
|
|
21652
|
+
await client.exposeFunction(fnName, async (response) => {
|
|
21653
|
+
try {
|
|
21654
|
+
const mod = await handler(response);
|
|
21655
|
+
return { success: true, result: mod ?? {} };
|
|
21656
|
+
} catch (e) {
|
|
21657
|
+
return { success: false, error: e.message };
|
|
21658
|
+
}
|
|
21659
|
+
}, tabId);
|
|
21660
|
+
const script = buildModifyResponseScript(pattern, fnName);
|
|
21661
|
+
await client.addInitScript(script, tabId);
|
|
21662
|
+
await client.evaluate(script, tabId);
|
|
21663
|
+
logger_default.success(`[${name}] intercept.modifyResponse: ${pattern}`);
|
|
21664
|
+
return site;
|
|
21665
|
+
},
|
|
21405
21666
|
clear: async () => {
|
|
21406
21667
|
await client.clearInterceptRules(tabId);
|
|
21407
21668
|
logger_default.info(`[${name}] intercept rules cleared`);
|
|
@@ -21480,6 +21741,7 @@ function createSiteObject(name, registeredUrl, client, tabId) {
|
|
|
21480
21741
|
return site;
|
|
21481
21742
|
},
|
|
21482
21743
|
close: async () => {
|
|
21744
|
+
_unsubNavigate();
|
|
21483
21745
|
keepAliveSites.delete(name);
|
|
21484
21746
|
if (tabId !== "default") {
|
|
21485
21747
|
await client.closeTab(tabId);
|
|
@@ -21513,9 +21775,12 @@ var piggy = {
|
|
|
21513
21775
|
const binaryMode = opts?.binary ?? "headless";
|
|
21514
21776
|
let tabId = "default";
|
|
21515
21777
|
if (_tabMode === "tab") {
|
|
21778
|
+
if (!_client)
|
|
21779
|
+
throw new Error("No client. Call piggy.launch() first.");
|
|
21516
21780
|
tabId = await _client.newTab();
|
|
21517
|
-
|
|
21518
|
-
|
|
21781
|
+
const siteObj = createSiteObject(name, url, _client, tabId);
|
|
21782
|
+
_sites[name] = siteObj;
|
|
21783
|
+
piggy[name] = siteObj;
|
|
21519
21784
|
logger_default.success(`[${name}] registered as tab ${tabId}`);
|
|
21520
21785
|
} else {
|
|
21521
21786
|
const socketName = `piggy_${name}`;
|
|
@@ -21524,8 +21789,9 @@ var piggy = {
|
|
|
21524
21789
|
const c = new PiggyClient(socketName);
|
|
21525
21790
|
await c.connect();
|
|
21526
21791
|
_extraProcs.push({ socket: socketName, client: c });
|
|
21527
|
-
|
|
21528
|
-
|
|
21792
|
+
const siteObj = createSiteObject(name, url, c, "default");
|
|
21793
|
+
_sites[name] = siteObj;
|
|
21794
|
+
piggy[name] = siteObj;
|
|
21529
21795
|
logger_default.success(`[${name}] registered as process on "${socketName}"`);
|
|
21530
21796
|
}
|
|
21531
21797
|
return piggy;
|