nothing-browser 0.0.18 → 0.0.20
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 +20 -20
- package/README.md +253 -253
- package/dist/piggy/register/index.d.ts.map +1 -1
- package/dist/piggy.js +14 -3
- package/dist/register/index.js +14 -3
- package/nothing_browser_pig_pink.svg +58 -58
- package/package.json +4 -3
- package/piggy/cache/memory.d.ts +6 -6
- package/piggy/cache/memory.ts +37 -37
- package/piggy/client/index.d.ts +78 -78
- package/piggy/client/index.ts +567 -567
- package/piggy/human/index.d.ts +6 -6
- package/piggy/human/index.ts +52 -52
- package/piggy/intercept/scripts.d.ts +12 -12
- package/piggy/intercept/scripts.ts +152 -152
- package/piggy/launch/detect.d.ts +2 -2
- package/piggy/launch/detect.ts +42 -42
- package/piggy/launch/spawn.d.ts +5 -5
- package/piggy/launch/spawn.ts +164 -164
- package/piggy/logger/index.d.ts +2 -2
- package/piggy/logger/index.ts +58 -58
- package/piggy/open/index.d.ts +3 -3
- package/piggy/open/index.ts +4 -4
- package/piggy/pool/index.d.ts +11 -11
- package/piggy/pool/index.ts +74 -74
- package/piggy/register/index.d.ts +6 -6
- package/piggy/register/index.ts +517 -506
- package/piggy/server/index.d.ts +57 -57
- package/piggy/server/index.ts +189 -189
- package/piggy/store/index.d.ts +25 -25
- package/piggy/store/index.ts +229 -229
- package/piggy.ts +216 -216
package/piggy/human/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare function randomDelay(min: number, max: number): Promise<void>;
|
|
2
|
-
/**
|
|
3
|
-
* Simulates human typing by introducing ~2 random typos and correcting them.
|
|
4
|
-
* Returns an array of { char, isBackspace } actions to replay.
|
|
5
|
-
*/
|
|
6
|
-
export declare function humanTypeSequence(text: string): string[];
|
|
1
|
+
export declare function randomDelay(min: number, max: number): Promise<void>;
|
|
2
|
+
/**
|
|
3
|
+
* Simulates human typing by introducing ~2 random typos and correcting them.
|
|
4
|
+
* Returns an array of { char, isBackspace } actions to replay.
|
|
5
|
+
*/
|
|
6
|
+
export declare function humanTypeSequence(text: string): string[];
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/piggy/human/index.ts
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
// piggy/human/index.ts
|
|
2
|
-
|
|
3
|
-
export function randomDelay(min: number, max: number): Promise<void> {
|
|
4
|
-
return new Promise(r => setTimeout(r, Math.floor(Math.random() * (max - min + 1)) + min));
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Simulates human typing by introducing ~2 random typos and correcting them.
|
|
9
|
-
* Returns an array of { char, isBackspace } actions to replay.
|
|
10
|
-
*/
|
|
11
|
-
export function humanTypeSequence(text: string): string[] {
|
|
12
|
-
const adjacent: Record<string, string[]> = {
|
|
13
|
-
a: ["q","w","s","z"], b: ["v","g","h","n"], c: ["x","d","f","v"],
|
|
14
|
-
d: ["s","e","r","f","c","x"], e: ["w","r","d","s"],
|
|
15
|
-
f: ["d","r","t","g","v","c"], g: ["f","t","y","h","b","v"],
|
|
16
|
-
h: ["g","y","u","j","n","b"], i: ["u","o","k","j"],
|
|
17
|
-
j: ["h","u","i","k","m","n"], k: ["j","i","o","l","m"],
|
|
18
|
-
l: ["k","o","p"], m: ["n","j","k"], n: ["b","h","j","m"],
|
|
19
|
-
o: ["i","p","l","k"], p: ["o","l"], q: ["w","a"],
|
|
20
|
-
r: ["e","t","f","d"], s: ["a","w","e","d","x","z"],
|
|
21
|
-
t: ["r","y","g","f"], u: ["y","i","h","j"],
|
|
22
|
-
v: ["c","f","g","b"], w: ["q","e","a","s"],
|
|
23
|
-
x: ["z","s","d","c"], y: ["t","u","g","h"],
|
|
24
|
-
z: ["a","s","x"],
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const actions: string[] = [];
|
|
28
|
-
const typoIndices = new Set<number>();
|
|
29
|
-
|
|
30
|
-
// Pick ~2 random positions to typo (skip short strings)
|
|
31
|
-
if (text.length > 4) {
|
|
32
|
-
let tries = 0;
|
|
33
|
-
while (typoIndices.size < 2 && tries < 20) {
|
|
34
|
-
typoIndices.add(Math.floor(Math.random() * text.length));
|
|
35
|
-
tries++;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
for (let i = 0; i < text.length; i++) {
|
|
40
|
-
if (typoIndices.has(i)) {
|
|
41
|
-
const ch = text[i]!.toLowerCase();
|
|
42
|
-
const neighbors = adjacent[ch];
|
|
43
|
-
const typo = neighbors
|
|
44
|
-
? neighbors[Math.floor(Math.random() * neighbors.length)] ?? ch
|
|
45
|
-
: ch;
|
|
46
|
-
actions.push(typo); // wrong char
|
|
47
|
-
actions.push("BACKSPACE"); // correct it
|
|
48
|
-
}
|
|
49
|
-
actions.push(text[i]!);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return actions;
|
|
1
|
+
// piggy/human/index.ts
|
|
2
|
+
|
|
3
|
+
export function randomDelay(min: number, max: number): Promise<void> {
|
|
4
|
+
return new Promise(r => setTimeout(r, Math.floor(Math.random() * (max - min + 1)) + min));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Simulates human typing by introducing ~2 random typos and correcting them.
|
|
9
|
+
* Returns an array of { char, isBackspace } actions to replay.
|
|
10
|
+
*/
|
|
11
|
+
export function humanTypeSequence(text: string): string[] {
|
|
12
|
+
const adjacent: Record<string, string[]> = {
|
|
13
|
+
a: ["q","w","s","z"], b: ["v","g","h","n"], c: ["x","d","f","v"],
|
|
14
|
+
d: ["s","e","r","f","c","x"], e: ["w","r","d","s"],
|
|
15
|
+
f: ["d","r","t","g","v","c"], g: ["f","t","y","h","b","v"],
|
|
16
|
+
h: ["g","y","u","j","n","b"], i: ["u","o","k","j"],
|
|
17
|
+
j: ["h","u","i","k","m","n"], k: ["j","i","o","l","m"],
|
|
18
|
+
l: ["k","o","p"], m: ["n","j","k"], n: ["b","h","j","m"],
|
|
19
|
+
o: ["i","p","l","k"], p: ["o","l"], q: ["w","a"],
|
|
20
|
+
r: ["e","t","f","d"], s: ["a","w","e","d","x","z"],
|
|
21
|
+
t: ["r","y","g","f"], u: ["y","i","h","j"],
|
|
22
|
+
v: ["c","f","g","b"], w: ["q","e","a","s"],
|
|
23
|
+
x: ["z","s","d","c"], y: ["t","u","g","h"],
|
|
24
|
+
z: ["a","s","x"],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const actions: string[] = [];
|
|
28
|
+
const typoIndices = new Set<number>();
|
|
29
|
+
|
|
30
|
+
// Pick ~2 random positions to typo (skip short strings)
|
|
31
|
+
if (text.length > 4) {
|
|
32
|
+
let tries = 0;
|
|
33
|
+
while (typoIndices.size < 2 && tries < 20) {
|
|
34
|
+
typoIndices.add(Math.floor(Math.random() * text.length));
|
|
35
|
+
tries++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < text.length; i++) {
|
|
40
|
+
if (typoIndices.has(i)) {
|
|
41
|
+
const ch = text[i]!.toLowerCase();
|
|
42
|
+
const neighbors = adjacent[ch];
|
|
43
|
+
const typo = neighbors
|
|
44
|
+
? neighbors[Math.floor(Math.random() * neighbors.length)] ?? ch
|
|
45
|
+
: ch;
|
|
46
|
+
actions.push(typo); // wrong char
|
|
47
|
+
actions.push("BACKSPACE"); // correct it
|
|
48
|
+
}
|
|
49
|
+
actions.push(text[i]!);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return actions;
|
|
53
53
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a script that short-circuits matching fetch/XHR requests
|
|
3
|
-
* and returns a static fake response — the request never hits the network.
|
|
4
|
-
*/
|
|
5
|
-
export declare function buildRespondScript(pattern: string, status: number, contentType: string, body: string): string;
|
|
6
|
-
/**
|
|
7
|
-
* Generates a script that lets the request hit the network, then calls
|
|
8
|
-
* an exposed function with { body, status, headers }.
|
|
9
|
-
* The exposed function returns { body?, status?, headers? } modifications
|
|
10
|
-
* or an empty object {} to pass through unchanged.
|
|
11
|
-
*/
|
|
12
|
-
export declare function buildModifyResponseScript(pattern: string, exposedFnName: string): string;
|
|
1
|
+
/**
|
|
2
|
+
* Generates a script that short-circuits matching fetch/XHR requests
|
|
3
|
+
* and returns a static fake response — the request never hits the network.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildRespondScript(pattern: string, status: number, contentType: string, body: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Generates a script that lets the request hit the network, then calls
|
|
8
|
+
* an exposed function with { body, status, headers }.
|
|
9
|
+
* The exposed function returns { body?, status?, headers? } modifications
|
|
10
|
+
* or an empty object {} to pass through unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildModifyResponseScript(pattern: string, exposedFnName: string): string;
|
|
13
13
|
//# sourceMappingURL=scripts.d.ts.map
|
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
// piggy/intercept/scripts.ts
|
|
2
|
-
// JS injection helpers for intercept.respond and intercept.modifyResponse.
|
|
3
|
-
// Both work purely in the browser's JS layer — no C++ changes needed.
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Generates a script that short-circuits matching fetch/XHR requests
|
|
7
|
-
* and returns a static fake response — the request never hits the network.
|
|
8
|
-
*/
|
|
9
|
-
export function buildRespondScript(
|
|
10
|
-
pattern: string,
|
|
11
|
-
status: number,
|
|
12
|
-
contentType: string,
|
|
13
|
-
body: string
|
|
14
|
-
): string {
|
|
15
|
-
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
16
|
-
const safeBody = body.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
17
|
-
const safeContentType = contentType.replace(/'/g, "\\'");
|
|
18
|
-
|
|
19
|
-
return `
|
|
20
|
-
(function() {
|
|
21
|
-
'use strict';
|
|
22
|
-
if (!window.__PIGGY_RESPOND_RULES__) window.__PIGGY_RESPOND_RULES__ = [];
|
|
23
|
-
window.__PIGGY_RESPOND_RULES__.push({
|
|
24
|
-
pattern: '${safePattern}',
|
|
25
|
-
status: ${status},
|
|
26
|
-
contentType: '${safeContentType}',
|
|
27
|
-
body: \`${safeBody}\`
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
function _piggyMatchUrl(url, pattern) {
|
|
31
|
-
try { return url.includes(pattern) || new RegExp(pattern).test(url); }
|
|
32
|
-
catch { return url.includes(pattern); }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Only install wrappers once per page
|
|
36
|
-
if (window.__PIGGY_RESPOND_INSTALLED__) return;
|
|
37
|
-
window.__PIGGY_RESPOND_INSTALLED__ = true;
|
|
38
|
-
|
|
39
|
-
// ── fetch wrapper ──────────────────────────────────────────────────────────
|
|
40
|
-
const _origFetch = window.fetch;
|
|
41
|
-
window.fetch = function(input, init) {
|
|
42
|
-
const url = typeof input === 'string' ? input : (input?.url ?? String(input));
|
|
43
|
-
const rules = window.__PIGGY_RESPOND_RULES__ || [];
|
|
44
|
-
for (const rule of rules) {
|
|
45
|
-
if (_piggyMatchUrl(url, rule.pattern)) {
|
|
46
|
-
return Promise.resolve(new Response(rule.body, {
|
|
47
|
-
status: rule.status,
|
|
48
|
-
headers: { 'Content-Type': rule.contentType }
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return _origFetch.apply(this, arguments);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// ── XHR wrapper ────────────────────────────────────────────────────────────
|
|
56
|
-
const _origOpen = XMLHttpRequest.prototype.open;
|
|
57
|
-
const _origSend = XMLHttpRequest.prototype.send;
|
|
58
|
-
|
|
59
|
-
XMLHttpRequest.prototype.open = function(method, url) {
|
|
60
|
-
this.__piggy_url__ = String(url);
|
|
61
|
-
return _origOpen.apply(this, arguments);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
XMLHttpRequest.prototype.send = function() {
|
|
65
|
-
const url = this.__piggy_url__ || '';
|
|
66
|
-
const rules = window.__PIGGY_RESPOND_RULES__ || [];
|
|
67
|
-
for (const rule of rules) {
|
|
68
|
-
if (_piggyMatchUrl(url, rule.pattern)) {
|
|
69
|
-
const self = this;
|
|
70
|
-
Object.defineProperty(self, 'readyState', { get: () => 4, configurable: true });
|
|
71
|
-
Object.defineProperty(self, 'status', { get: () => rule.status, configurable: true });
|
|
72
|
-
Object.defineProperty(self, 'responseText', { get: () => rule.body, configurable: true });
|
|
73
|
-
Object.defineProperty(self, 'response', { get: () => rule.body, configurable: true });
|
|
74
|
-
setTimeout(() => {
|
|
75
|
-
if (typeof self.onreadystatechange === 'function') self.onreadystatechange();
|
|
76
|
-
self.dispatchEvent(new Event('readystatechange'));
|
|
77
|
-
self.dispatchEvent(new Event('load'));
|
|
78
|
-
self.dispatchEvent(new Event('loadend'));
|
|
79
|
-
}, 0);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return _origSend.apply(this, arguments);
|
|
84
|
-
};
|
|
85
|
-
})();
|
|
86
|
-
`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Generates a script that lets the request hit the network, then calls
|
|
91
|
-
* an exposed function with { body, status, headers }.
|
|
92
|
-
* The exposed function returns { body?, status?, headers? } modifications
|
|
93
|
-
* or an empty object {} to pass through unchanged.
|
|
94
|
-
*/
|
|
95
|
-
export function buildModifyResponseScript(pattern: string, exposedFnName: string): string {
|
|
96
|
-
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
97
|
-
const safeFnName = exposedFnName.replace(/'/g, "\\'");
|
|
98
|
-
|
|
99
|
-
return `
|
|
100
|
-
(function() {
|
|
101
|
-
'use strict';
|
|
102
|
-
if (!window.__PIGGY_MODIFY_RULES__) window.__PIGGY_MODIFY_RULES__ = [];
|
|
103
|
-
window.__PIGGY_MODIFY_RULES__.push({ pattern: '${safePattern}', fn: '${safeFnName}' });
|
|
104
|
-
|
|
105
|
-
function _piggyMatchUrl(url, pattern) {
|
|
106
|
-
try { return url.includes(pattern) || new RegExp(pattern).test(url); }
|
|
107
|
-
catch { return url.includes(pattern); }
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Only install wrappers once per page
|
|
111
|
-
if (window.__PIGGY_MODIFY_INSTALLED__) return;
|
|
112
|
-
window.__PIGGY_MODIFY_INSTALLED__ = true;
|
|
113
|
-
|
|
114
|
-
const _origFetch = window.fetch;
|
|
115
|
-
window.fetch = async function(input, init) {
|
|
116
|
-
const url = typeof input === 'string' ? input : (input?.url ?? String(input));
|
|
117
|
-
const rules = window.__PIGGY_MODIFY_RULES__ || [];
|
|
118
|
-
|
|
119
|
-
let matchedFn = null;
|
|
120
|
-
for (const rule of rules) {
|
|
121
|
-
if (_piggyMatchUrl(url, rule.pattern)) { matchedFn = rule.fn; break; }
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// No match — pass through untouched
|
|
125
|
-
const resp = await _origFetch.apply(this, arguments);
|
|
126
|
-
if (!matchedFn) return resp;
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const bodyText = await resp.clone().text();
|
|
130
|
-
const headers = {};
|
|
131
|
-
resp.headers.forEach((v, k) => { headers[k] = v; });
|
|
132
|
-
|
|
133
|
-
const handlerFn = window[matchedFn];
|
|
134
|
-
if (typeof handlerFn !== 'function') return resp;
|
|
135
|
-
|
|
136
|
-
// Call Node.js handler via exposeFunction bridge
|
|
137
|
-
const mod = await handlerFn({ body: bodyText, status: resp.status, headers });
|
|
138
|
-
if (!mod || typeof mod !== 'object' || Object.keys(mod).length === 0) return resp;
|
|
139
|
-
|
|
140
|
-
return new Response(
|
|
141
|
-
mod.body !== undefined ? mod.body : bodyText,
|
|
142
|
-
{
|
|
143
|
-
status: mod.status !== undefined ? mod.status : resp.status,
|
|
144
|
-
headers: mod.headers !== undefined ? mod.headers : headers,
|
|
145
|
-
}
|
|
146
|
-
);
|
|
147
|
-
} catch {
|
|
148
|
-
return resp; // On any error, pass through original response
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
})();
|
|
152
|
-
`;
|
|
1
|
+
// piggy/intercept/scripts.ts
|
|
2
|
+
// JS injection helpers for intercept.respond and intercept.modifyResponse.
|
|
3
|
+
// Both work purely in the browser's JS layer — no C++ changes needed.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates a script that short-circuits matching fetch/XHR requests
|
|
7
|
+
* and returns a static fake response — the request never hits the network.
|
|
8
|
+
*/
|
|
9
|
+
export function buildRespondScript(
|
|
10
|
+
pattern: string,
|
|
11
|
+
status: number,
|
|
12
|
+
contentType: string,
|
|
13
|
+
body: string
|
|
14
|
+
): string {
|
|
15
|
+
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
16
|
+
const safeBody = body.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
17
|
+
const safeContentType = contentType.replace(/'/g, "\\'");
|
|
18
|
+
|
|
19
|
+
return `
|
|
20
|
+
(function() {
|
|
21
|
+
'use strict';
|
|
22
|
+
if (!window.__PIGGY_RESPOND_RULES__) window.__PIGGY_RESPOND_RULES__ = [];
|
|
23
|
+
window.__PIGGY_RESPOND_RULES__.push({
|
|
24
|
+
pattern: '${safePattern}',
|
|
25
|
+
status: ${status},
|
|
26
|
+
contentType: '${safeContentType}',
|
|
27
|
+
body: \`${safeBody}\`
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function _piggyMatchUrl(url, pattern) {
|
|
31
|
+
try { return url.includes(pattern) || new RegExp(pattern).test(url); }
|
|
32
|
+
catch { return url.includes(pattern); }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Only install wrappers once per page
|
|
36
|
+
if (window.__PIGGY_RESPOND_INSTALLED__) return;
|
|
37
|
+
window.__PIGGY_RESPOND_INSTALLED__ = true;
|
|
38
|
+
|
|
39
|
+
// ── fetch wrapper ──────────────────────────────────────────────────────────
|
|
40
|
+
const _origFetch = window.fetch;
|
|
41
|
+
window.fetch = function(input, init) {
|
|
42
|
+
const url = typeof input === 'string' ? input : (input?.url ?? String(input));
|
|
43
|
+
const rules = window.__PIGGY_RESPOND_RULES__ || [];
|
|
44
|
+
for (const rule of rules) {
|
|
45
|
+
if (_piggyMatchUrl(url, rule.pattern)) {
|
|
46
|
+
return Promise.resolve(new Response(rule.body, {
|
|
47
|
+
status: rule.status,
|
|
48
|
+
headers: { 'Content-Type': rule.contentType }
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return _origFetch.apply(this, arguments);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ── XHR wrapper ────────────────────────────────────────────────────────────
|
|
56
|
+
const _origOpen = XMLHttpRequest.prototype.open;
|
|
57
|
+
const _origSend = XMLHttpRequest.prototype.send;
|
|
58
|
+
|
|
59
|
+
XMLHttpRequest.prototype.open = function(method, url) {
|
|
60
|
+
this.__piggy_url__ = String(url);
|
|
61
|
+
return _origOpen.apply(this, arguments);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
XMLHttpRequest.prototype.send = function() {
|
|
65
|
+
const url = this.__piggy_url__ || '';
|
|
66
|
+
const rules = window.__PIGGY_RESPOND_RULES__ || [];
|
|
67
|
+
for (const rule of rules) {
|
|
68
|
+
if (_piggyMatchUrl(url, rule.pattern)) {
|
|
69
|
+
const self = this;
|
|
70
|
+
Object.defineProperty(self, 'readyState', { get: () => 4, configurable: true });
|
|
71
|
+
Object.defineProperty(self, 'status', { get: () => rule.status, configurable: true });
|
|
72
|
+
Object.defineProperty(self, 'responseText', { get: () => rule.body, configurable: true });
|
|
73
|
+
Object.defineProperty(self, 'response', { get: () => rule.body, configurable: true });
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
if (typeof self.onreadystatechange === 'function') self.onreadystatechange();
|
|
76
|
+
self.dispatchEvent(new Event('readystatechange'));
|
|
77
|
+
self.dispatchEvent(new Event('load'));
|
|
78
|
+
self.dispatchEvent(new Event('loadend'));
|
|
79
|
+
}, 0);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return _origSend.apply(this, arguments);
|
|
84
|
+
};
|
|
85
|
+
})();
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generates a script that lets the request hit the network, then calls
|
|
91
|
+
* an exposed function with { body, status, headers }.
|
|
92
|
+
* The exposed function returns { body?, status?, headers? } modifications
|
|
93
|
+
* or an empty object {} to pass through unchanged.
|
|
94
|
+
*/
|
|
95
|
+
export function buildModifyResponseScript(pattern: string, exposedFnName: string): string {
|
|
96
|
+
const safePattern = pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
97
|
+
const safeFnName = exposedFnName.replace(/'/g, "\\'");
|
|
98
|
+
|
|
99
|
+
return `
|
|
100
|
+
(function() {
|
|
101
|
+
'use strict';
|
|
102
|
+
if (!window.__PIGGY_MODIFY_RULES__) window.__PIGGY_MODIFY_RULES__ = [];
|
|
103
|
+
window.__PIGGY_MODIFY_RULES__.push({ pattern: '${safePattern}', fn: '${safeFnName}' });
|
|
104
|
+
|
|
105
|
+
function _piggyMatchUrl(url, pattern) {
|
|
106
|
+
try { return url.includes(pattern) || new RegExp(pattern).test(url); }
|
|
107
|
+
catch { return url.includes(pattern); }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Only install wrappers once per page
|
|
111
|
+
if (window.__PIGGY_MODIFY_INSTALLED__) return;
|
|
112
|
+
window.__PIGGY_MODIFY_INSTALLED__ = true;
|
|
113
|
+
|
|
114
|
+
const _origFetch = window.fetch;
|
|
115
|
+
window.fetch = async function(input, init) {
|
|
116
|
+
const url = typeof input === 'string' ? input : (input?.url ?? String(input));
|
|
117
|
+
const rules = window.__PIGGY_MODIFY_RULES__ || [];
|
|
118
|
+
|
|
119
|
+
let matchedFn = null;
|
|
120
|
+
for (const rule of rules) {
|
|
121
|
+
if (_piggyMatchUrl(url, rule.pattern)) { matchedFn = rule.fn; break; }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// No match — pass through untouched
|
|
125
|
+
const resp = await _origFetch.apply(this, arguments);
|
|
126
|
+
if (!matchedFn) return resp;
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const bodyText = await resp.clone().text();
|
|
130
|
+
const headers = {};
|
|
131
|
+
resp.headers.forEach((v, k) => { headers[k] = v; });
|
|
132
|
+
|
|
133
|
+
const handlerFn = window[matchedFn];
|
|
134
|
+
if (typeof handlerFn !== 'function') return resp;
|
|
135
|
+
|
|
136
|
+
// Call Node.js handler via exposeFunction bridge
|
|
137
|
+
const mod = await handlerFn({ body: bodyText, status: resp.status, headers });
|
|
138
|
+
if (!mod || typeof mod !== 'object' || Object.keys(mod).length === 0) return resp;
|
|
139
|
+
|
|
140
|
+
return new Response(
|
|
141
|
+
mod.body !== undefined ? mod.body : bodyText,
|
|
142
|
+
{
|
|
143
|
+
status: mod.status !== undefined ? mod.status : resp.status,
|
|
144
|
+
headers: mod.headers !== undefined ? mod.headers : headers,
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
} catch {
|
|
148
|
+
return resp; // On any error, pass through original response
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
})();
|
|
152
|
+
`;
|
|
153
153
|
}
|
package/piggy/launch/detect.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type BinaryMode = 'headless' | 'headful';
|
|
2
|
-
export declare function detectBinary(mode?: BinaryMode): string | null;
|
|
1
|
+
export type BinaryMode = 'headless' | 'headful';
|
|
2
|
+
export declare function detectBinary(mode?: BinaryMode): string | null;
|
|
3
3
|
//# sourceMappingURL=detect.d.ts.map
|
package/piggy/launch/detect.ts
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { existsSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import logger from '../logger';
|
|
4
|
-
|
|
5
|
-
export type BinaryMode = 'headless' | 'headful';
|
|
6
|
-
|
|
7
|
-
const BINARY_NAMES: Record<BinaryMode, string> = {
|
|
8
|
-
headless: 'nothing-browser-headless',
|
|
9
|
-
headful: 'nothing-browser-headful',
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function detectBinary(mode: BinaryMode = 'headless'): string | null {
|
|
13
|
-
const cwd = process.cwd();
|
|
14
|
-
const name = BINARY_NAMES[mode];
|
|
15
|
-
|
|
16
|
-
// Windows
|
|
17
|
-
if (process.platform === 'win32') {
|
|
18
|
-
const p = join(cwd, `${name}.exe`);
|
|
19
|
-
if (existsSync(p)) {
|
|
20
|
-
logger.success(`Binary found (${mode}): ${p}`);
|
|
21
|
-
return p;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Linux / macOS
|
|
26
|
-
const p = join(cwd, name);
|
|
27
|
-
if (existsSync(p)) {
|
|
28
|
-
logger.success(`Binary found (${mode}): ${p}`);
|
|
29
|
-
return p;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
logger.error(`❌ Binary not found in project root: ${name}`);
|
|
33
|
-
logger.error('');
|
|
34
|
-
logger.error('Download from:');
|
|
35
|
-
logger.error(' https://github.com/BunElysiaReact/nothing-browser/releases/');
|
|
36
|
-
logger.error('');
|
|
37
|
-
logger.error(`Place in: ${cwd}/${name}${process.platform === 'win32' ? '.exe' : ''}`);
|
|
38
|
-
if (process.platform !== 'win32') {
|
|
39
|
-
logger.error(`Then run: chmod +x ${name}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return null;
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import logger from '../logger';
|
|
4
|
+
|
|
5
|
+
export type BinaryMode = 'headless' | 'headful';
|
|
6
|
+
|
|
7
|
+
const BINARY_NAMES: Record<BinaryMode, string> = {
|
|
8
|
+
headless: 'nothing-browser-headless',
|
|
9
|
+
headful: 'nothing-browser-headful',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function detectBinary(mode: BinaryMode = 'headless'): string | null {
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
const name = BINARY_NAMES[mode];
|
|
15
|
+
|
|
16
|
+
// Windows
|
|
17
|
+
if (process.platform === 'win32') {
|
|
18
|
+
const p = join(cwd, `${name}.exe`);
|
|
19
|
+
if (existsSync(p)) {
|
|
20
|
+
logger.success(`Binary found (${mode}): ${p}`);
|
|
21
|
+
return p;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Linux / macOS
|
|
26
|
+
const p = join(cwd, name);
|
|
27
|
+
if (existsSync(p)) {
|
|
28
|
+
logger.success(`Binary found (${mode}): ${p}`);
|
|
29
|
+
return p;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
logger.error(`❌ Binary not found in project root: ${name}`);
|
|
33
|
+
logger.error('');
|
|
34
|
+
logger.error('Download from:');
|
|
35
|
+
logger.error(' https://github.com/BunElysiaReact/nothing-browser/releases/');
|
|
36
|
+
logger.error('');
|
|
37
|
+
logger.error(`Place in: ${cwd}/${name}${process.platform === 'win32' ? '.exe' : ''}`);
|
|
38
|
+
if (process.platform !== 'win32') {
|
|
39
|
+
logger.error(`Then run: chmod +x ${name}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
43
|
}
|
package/piggy/launch/spawn.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type BinaryMode } from "./detect";
|
|
2
|
-
export declare function killAllBrowsers(): void;
|
|
3
|
-
export declare function spawnBrowser(mode?: BinaryMode): Promise<string>;
|
|
4
|
-
export declare function spawnBrowserOnSocket(socketName: string, mode?: BinaryMode): Promise<void>;
|
|
5
|
-
export declare function killBrowser(): void;
|
|
1
|
+
import { type BinaryMode } from "./detect";
|
|
2
|
+
export declare function killAllBrowsers(): void;
|
|
3
|
+
export declare function spawnBrowser(mode?: BinaryMode): Promise<string>;
|
|
4
|
+
export declare function spawnBrowserOnSocket(socketName: string, mode?: BinaryMode): Promise<void>;
|
|
5
|
+
export declare function killBrowser(): void;
|
|
6
6
|
//# sourceMappingURL=spawn.d.ts.map
|