@whitesev/utils 1.0.1 → 1.0.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/README.md +5 -3
- package/dist/index.amd.js +6037 -0
- package/dist/index.amd.js.map +1 -0
- package/dist/index.cjs.js +518 -505
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +518 -505
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +6038 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.system.js +6040 -0
- package/dist/index.system.js.map +1 -0
- package/dist/index.umd.js +518 -505
- package/dist/index.umd.js.map +1 -1
- package/dist/src/ajaxHooker.d.ts +6 -6
- package/package.json +2 -1
- package/rollup.config.js +21 -1
- package/src/Utils.ts +3 -3
- package/src/ajaxHooker.js +518 -505
package/src/ajaxHooker.js
CHANGED
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
// ==UserScript==
|
|
2
1
|
// @name ajaxHooker
|
|
3
2
|
// @author cxxjackie
|
|
4
3
|
// @version 1.4.1
|
|
5
4
|
// @supportURL https://bbs.tampermonkey.net.cn/thread-3284-1-1.html
|
|
6
|
-
// ==/UserScript==
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
function catchError(fn, ...args) {
|
|
48
|
-
try {
|
|
49
|
-
const result = fn(...args);
|
|
50
|
-
if (isThenable(result)) return result.then(null, errorFn);
|
|
51
|
-
return result;
|
|
52
|
-
} catch (err) {
|
|
53
|
-
console.error(err);
|
|
6
|
+
const AjaxHooker = function () {
|
|
7
|
+
return (function () {
|
|
8
|
+
"use strict";
|
|
9
|
+
const version = "1.4.1";
|
|
10
|
+
const hookInst = {
|
|
11
|
+
hookFns: [],
|
|
12
|
+
filters: [],
|
|
13
|
+
};
|
|
14
|
+
const win = window.unsafeWindow || document.defaultView || window;
|
|
15
|
+
let winAh = win.__ajaxHooker;
|
|
16
|
+
const resProto = win.Response.prototype;
|
|
17
|
+
const xhrResponses = ["response", "responseText", "responseXML"];
|
|
18
|
+
const fetchResponses = ["arrayBuffer", "blob", "formData", "json", "text"];
|
|
19
|
+
const fetchInitProps = [
|
|
20
|
+
"method",
|
|
21
|
+
"headers",
|
|
22
|
+
"body",
|
|
23
|
+
"mode",
|
|
24
|
+
"credentials",
|
|
25
|
+
"cache",
|
|
26
|
+
"redirect",
|
|
27
|
+
"referrer",
|
|
28
|
+
"referrerPolicy",
|
|
29
|
+
"integrity",
|
|
30
|
+
"keepalive",
|
|
31
|
+
"signal",
|
|
32
|
+
"priority",
|
|
33
|
+
];
|
|
34
|
+
const xhrAsyncEvents = ["readystatechange", "load", "loadend"];
|
|
35
|
+
const getType = {}.toString.call.bind({}.toString);
|
|
36
|
+
const getDescriptor = Object.getOwnPropertyDescriptor.bind(Object);
|
|
37
|
+
const emptyFn = () => {};
|
|
38
|
+
const errorFn = (e) => console.error(e);
|
|
39
|
+
function isThenable(obj) {
|
|
40
|
+
return (
|
|
41
|
+
obj &&
|
|
42
|
+
["object", "function"].includes(typeof obj) &&
|
|
43
|
+
typeof obj.then === "function"
|
|
44
|
+
);
|
|
54
45
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
function readonly(obj, prop, value = obj[prop]) {
|
|
65
|
-
defineProp(obj, prop, () => value, emptyFn);
|
|
66
|
-
}
|
|
67
|
-
function writable(obj, prop, value = obj[prop]) {
|
|
68
|
-
Object.defineProperty(obj, prop, {
|
|
69
|
-
configurable: true,
|
|
70
|
-
enumerable: true,
|
|
71
|
-
writable: true,
|
|
72
|
-
value: value,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
function parseHeaders(obj) {
|
|
76
|
-
const headers = {};
|
|
77
|
-
switch (getType(obj)) {
|
|
78
|
-
case "[object String]":
|
|
79
|
-
for (const line of obj.trim().split(/[\r\n]+/)) {
|
|
80
|
-
const [header, value] = line.split(/\s*:\s*/);
|
|
81
|
-
if (!header) break;
|
|
82
|
-
const lheader = header.toLowerCase();
|
|
83
|
-
headers[lheader] =
|
|
84
|
-
lheader in headers ? `${headers[lheader]}, ${value}` : value;
|
|
85
|
-
}
|
|
86
|
-
break;
|
|
87
|
-
case "[object Headers]":
|
|
88
|
-
for (const [key, val] of obj) {
|
|
89
|
-
headers[key] = val;
|
|
90
|
-
}
|
|
91
|
-
break;
|
|
92
|
-
case "[object Object]":
|
|
93
|
-
return { ...obj };
|
|
46
|
+
function catchError(fn, ...args) {
|
|
47
|
+
try {
|
|
48
|
+
const result = fn(...args);
|
|
49
|
+
if (isThenable(result)) return result.then(null, errorFn);
|
|
50
|
+
return result;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(err);
|
|
53
|
+
}
|
|
94
54
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
fn && fn();
|
|
103
|
-
return new SyncThenable();
|
|
55
|
+
function defineProp(obj, prop, getter, setter) {
|
|
56
|
+
Object.defineProperty(obj, prop, {
|
|
57
|
+
configurable: true,
|
|
58
|
+
enumerable: true,
|
|
59
|
+
get: getter,
|
|
60
|
+
set: setter,
|
|
61
|
+
});
|
|
104
62
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
constructor(request) {
|
|
108
|
-
this.request = request;
|
|
109
|
-
this.requestClone = { ...this.request };
|
|
63
|
+
function readonly(obj, prop, value = obj[prop]) {
|
|
64
|
+
defineProp(obj, prop, () => value, emptyFn);
|
|
110
65
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
66
|
+
function writable(obj, prop, value = obj[prop]) {
|
|
67
|
+
Object.defineProperty(obj, prop, {
|
|
68
|
+
configurable: true,
|
|
69
|
+
enumerable: true,
|
|
70
|
+
writable: true,
|
|
71
|
+
value: value,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function parseHeaders(obj) {
|
|
75
|
+
const headers = {};
|
|
76
|
+
switch (getType(obj)) {
|
|
77
|
+
case "[object String]":
|
|
78
|
+
for (const line of obj.trim().split(/[\r\n]+/)) {
|
|
79
|
+
const [header, value] = line.split(/\s*:\s*/);
|
|
80
|
+
if (!header) break;
|
|
81
|
+
const lheader = header.toLowerCase();
|
|
82
|
+
headers[lheader] =
|
|
83
|
+
lheader in headers ? `${headers[lheader]}, ${value}` : value;
|
|
125
84
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
85
|
+
break;
|
|
86
|
+
case "[object Headers]":
|
|
87
|
+
for (const [key, val] of obj) {
|
|
88
|
+
headers[key] = val;
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
case "[object Object]":
|
|
92
|
+
return { ...obj };
|
|
93
|
+
}
|
|
94
|
+
return headers;
|
|
129
95
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (getType(fn) === "[object Function]")
|
|
137
|
-
catchError(fn, this.request);
|
|
138
|
-
});
|
|
139
|
-
requestKeys.forEach((key) => {
|
|
140
|
-
if (isThenable(this.request[key]))
|
|
141
|
-
this.request[key] = this.requestClone[key];
|
|
142
|
-
});
|
|
143
|
-
});
|
|
96
|
+
function stopImmediatePropagation() {
|
|
97
|
+
this.ajaxHooker_isStopped = true;
|
|
98
|
+
}
|
|
99
|
+
class SyncThenable {
|
|
100
|
+
then(fn) {
|
|
101
|
+
fn && fn();
|
|
144
102
|
return new SyncThenable();
|
|
145
103
|
}
|
|
146
|
-
const promises = [];
|
|
147
|
-
win.__ajaxHooker.hookInsts.forEach(({ hookFns, filters }) => {
|
|
148
|
-
if (this.shouldFilter(filters)) return;
|
|
149
|
-
promises.push(
|
|
150
|
-
Promise.all(hookFns.map((fn) => catchError(fn, this.request))).then(
|
|
151
|
-
() =>
|
|
152
|
-
Promise.all(
|
|
153
|
-
requestKeys.map((key) =>
|
|
154
|
-
Promise.resolve(this.request[key]).then(
|
|
155
|
-
(val) => (this.request[key] = val),
|
|
156
|
-
() => (this.request[key] = this.requestClone[key])
|
|
157
|
-
)
|
|
158
|
-
)
|
|
159
|
-
)
|
|
160
|
-
)
|
|
161
|
-
);
|
|
162
|
-
});
|
|
163
|
-
return Promise.all(promises);
|
|
164
104
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
this.request
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
) {
|
|
176
|
-
|
|
105
|
+
class AHRequest {
|
|
106
|
+
constructor(request) {
|
|
107
|
+
this.request = request;
|
|
108
|
+
this.requestClone = { ...this.request };
|
|
109
|
+
}
|
|
110
|
+
shouldFilter(filters) {
|
|
111
|
+
const { type, url, method, async } = this.request;
|
|
112
|
+
return (
|
|
113
|
+
filters.length &&
|
|
114
|
+
!filters.find((obj) => {
|
|
115
|
+
switch (true) {
|
|
116
|
+
case obj.type && obj.type !== type:
|
|
117
|
+
case getType(obj.url) === "[object String]" &&
|
|
118
|
+
!url.includes(obj.url):
|
|
119
|
+
case getType(obj.url) === "[object RegExp]" && !obj.url.test(url):
|
|
120
|
+
case obj.method &&
|
|
121
|
+
obj.method.toUpperCase() !== method.toUpperCase():
|
|
122
|
+
case "async" in obj && obj.async !== async:
|
|
123
|
+
return false;
|
|
177
124
|
}
|
|
125
|
+
return true;
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
waitForRequestKeys() {
|
|
130
|
+
const requestKeys = ["url", "method", "abort", "headers", "data"];
|
|
131
|
+
if (!this.request.async) {
|
|
132
|
+
win.__ajaxHooker.hookInsts.forEach(({ hookFns, filters }) => {
|
|
133
|
+
if (this.shouldFilter(filters)) return;
|
|
134
|
+
hookFns.forEach((fn) => {
|
|
135
|
+
if (getType(fn) === "[object Function]")
|
|
136
|
+
catchError(fn, this.request);
|
|
137
|
+
});
|
|
138
|
+
requestKeys.forEach((key) => {
|
|
139
|
+
if (isThenable(this.request[key]))
|
|
140
|
+
this.request[key] = this.requestClone[key];
|
|
141
|
+
});
|
|
178
142
|
});
|
|
143
|
+
return new SyncThenable();
|
|
179
144
|
}
|
|
180
|
-
|
|
145
|
+
const promises = [];
|
|
146
|
+
win.__ajaxHooker.hookInsts.forEach(({ hookFns, filters }) => {
|
|
147
|
+
if (this.shouldFilter(filters)) return;
|
|
148
|
+
promises.push(
|
|
149
|
+
Promise.all(hookFns.map((fn) => catchError(fn, this.request))).then(
|
|
150
|
+
() =>
|
|
151
|
+
Promise.all(
|
|
152
|
+
requestKeys.map((key) =>
|
|
153
|
+
Promise.resolve(this.request[key]).then(
|
|
154
|
+
(val) => (this.request[key] = val),
|
|
155
|
+
() => (this.request[key] = this.requestClone[key])
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
return Promise.all(promises);
|
|
181
163
|
}
|
|
182
|
-
|
|
183
|
-
|
|
164
|
+
waitForResponseKeys(response) {
|
|
165
|
+
const responseKeys =
|
|
166
|
+
this.request.type === "xhr" ? xhrResponses : fetchResponses;
|
|
167
|
+
if (!this.request.async) {
|
|
168
|
+
if (getType(this.request.response) === "[object Function]") {
|
|
169
|
+
catchError(this.request.response, response);
|
|
170
|
+
responseKeys.forEach((key) => {
|
|
171
|
+
if (
|
|
172
|
+
"get" in getDescriptor(response, key) ||
|
|
173
|
+
isThenable(response[key])
|
|
174
|
+
) {
|
|
175
|
+
delete response[key];
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return new SyncThenable();
|
|
180
|
+
}
|
|
181
|
+
return Promise.resolve(
|
|
182
|
+
catchError(this.request.response, response)
|
|
183
|
+
).then(() =>
|
|
184
184
|
Promise.all(
|
|
185
185
|
responseKeys.map((key) => {
|
|
186
186
|
const descriptor = getDescriptor(response, key);
|
|
@@ -194,367 +194,380 @@ var ajaxHooker = (function () {
|
|
|
194
194
|
}
|
|
195
195
|
})
|
|
196
196
|
)
|
|
197
|
-
|
|
197
|
+
);
|
|
198
|
+
}
|
|
198
199
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
const proxyHandler = {
|
|
201
|
+
get(target, prop) {
|
|
202
|
+
const descriptor = getDescriptor(target, prop);
|
|
203
|
+
if (
|
|
204
|
+
descriptor &&
|
|
205
|
+
!descriptor.configurable &&
|
|
206
|
+
!descriptor.writable &&
|
|
207
|
+
!descriptor.get
|
|
208
|
+
)
|
|
209
|
+
return target[prop];
|
|
210
|
+
const ah = target.__ajaxHooker;
|
|
211
|
+
if (ah && ah.proxyProps) {
|
|
212
|
+
if (prop in ah.proxyProps) {
|
|
213
|
+
const pDescriptor = ah.proxyProps[prop];
|
|
214
|
+
if ("get" in pDescriptor) return pDescriptor.get();
|
|
215
|
+
if (typeof pDescriptor.value === "function")
|
|
216
|
+
return pDescriptor.value.bind(ah);
|
|
217
|
+
return pDescriptor.value;
|
|
218
|
+
}
|
|
219
|
+
if (typeof target[prop] === "function")
|
|
220
|
+
return target[prop].bind(target);
|
|
221
|
+
}
|
|
209
222
|
return target[prop];
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
223
|
+
},
|
|
224
|
+
set(target, prop, value) {
|
|
225
|
+
const descriptor = getDescriptor(target, prop);
|
|
226
|
+
if (
|
|
227
|
+
descriptor &&
|
|
228
|
+
!descriptor.configurable &&
|
|
229
|
+
!descriptor.writable &&
|
|
230
|
+
!descriptor.set
|
|
231
|
+
)
|
|
232
|
+
return true;
|
|
233
|
+
const ah = target.__ajaxHooker;
|
|
234
|
+
if (ah && ah.proxyProps && prop in ah.proxyProps) {
|
|
213
235
|
const pDescriptor = ah.proxyProps[prop];
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
236
|
+
pDescriptor.set
|
|
237
|
+
? pDescriptor.set(value)
|
|
238
|
+
: (pDescriptor.value = value);
|
|
239
|
+
} else {
|
|
240
|
+
target[prop] = value;
|
|
218
241
|
}
|
|
219
|
-
if (typeof target[prop] === "function")
|
|
220
|
-
return target[prop].bind(target);
|
|
221
|
-
}
|
|
222
|
-
return target[prop];
|
|
223
|
-
},
|
|
224
|
-
set(target, prop, value) {
|
|
225
|
-
const descriptor = getDescriptor(target, prop);
|
|
226
|
-
if (
|
|
227
|
-
descriptor &&
|
|
228
|
-
!descriptor.configurable &&
|
|
229
|
-
!descriptor.writable &&
|
|
230
|
-
!descriptor.set
|
|
231
|
-
)
|
|
232
242
|
return true;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
class XhrHooker {
|
|
246
|
+
constructor(xhr) {
|
|
247
|
+
const ah = this;
|
|
248
|
+
Object.assign(ah, {
|
|
249
|
+
originalXhr: xhr,
|
|
250
|
+
proxyXhr: new Proxy(xhr, proxyHandler),
|
|
251
|
+
resThenable: new SyncThenable(),
|
|
252
|
+
proxyProps: {},
|
|
253
|
+
proxyEvents: {},
|
|
254
|
+
});
|
|
255
|
+
xhr.addEventListener("readystatechange", (e) => {
|
|
256
|
+
if (
|
|
257
|
+
ah.proxyXhr.readyState === 4 &&
|
|
258
|
+
ah.request &&
|
|
259
|
+
typeof ah.request.response === "function"
|
|
260
|
+
) {
|
|
261
|
+
const response = {
|
|
262
|
+
finalUrl: ah.proxyXhr.responseURL,
|
|
263
|
+
status: ah.proxyXhr.status,
|
|
264
|
+
responseHeaders: parseHeaders(
|
|
265
|
+
ah.proxyXhr.getAllResponseHeaders()
|
|
266
|
+
),
|
|
267
|
+
};
|
|
268
|
+
const tempValues = {};
|
|
269
|
+
for (const key of xhrResponses) {
|
|
270
|
+
try {
|
|
271
|
+
tempValues[key] = ah.originalXhr[key];
|
|
272
|
+
} catch (err) {}
|
|
273
|
+
defineProp(
|
|
274
|
+
response,
|
|
275
|
+
key,
|
|
276
|
+
() => {
|
|
277
|
+
return (response[key] = tempValues[key]);
|
|
278
|
+
},
|
|
279
|
+
(val) => {
|
|
280
|
+
delete response[key];
|
|
281
|
+
response[key] = val;
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
ah.resThenable = new AHRequest(ah.request)
|
|
286
|
+
.waitForResponseKeys(response)
|
|
287
|
+
.then(() => {
|
|
288
|
+
for (const key of xhrResponses) {
|
|
289
|
+
ah.proxyProps[key] = {
|
|
290
|
+
get: () => {
|
|
291
|
+
if (!(key in response)) response[key] = tempValues[key];
|
|
292
|
+
return response[key];
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
});
|
|
280
297
|
}
|
|
281
|
-
ah.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
298
|
+
ah.dispatchEvent(e);
|
|
299
|
+
});
|
|
300
|
+
xhr.addEventListener("load", (e) => ah.dispatchEvent(e));
|
|
301
|
+
xhr.addEventListener("loadend", (e) => ah.dispatchEvent(e));
|
|
302
|
+
for (const evt of xhrAsyncEvents) {
|
|
303
|
+
const onEvt = "on" + evt;
|
|
304
|
+
ah.proxyProps[onEvt] = {
|
|
305
|
+
get: () => ah.proxyEvents[onEvt] || null,
|
|
306
|
+
set: (val) => ah.addEvent(onEvt, val),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
for (const method of [
|
|
310
|
+
"setRequestHeader",
|
|
311
|
+
"addEventListener",
|
|
312
|
+
"removeEventListener",
|
|
313
|
+
"open",
|
|
314
|
+
"send",
|
|
315
|
+
]) {
|
|
316
|
+
ah.proxyProps[method] = { value: ah[method] };
|
|
293
317
|
}
|
|
294
|
-
ah.dispatchEvent(e);
|
|
295
|
-
});
|
|
296
|
-
xhr.addEventListener("load", (e) => ah.dispatchEvent(e));
|
|
297
|
-
xhr.addEventListener("loadend", (e) => ah.dispatchEvent(e));
|
|
298
|
-
for (const evt of xhrAsyncEvents) {
|
|
299
|
-
const onEvt = "on" + evt;
|
|
300
|
-
ah.proxyProps[onEvt] = {
|
|
301
|
-
get: () => ah.proxyEvents[onEvt] || null,
|
|
302
|
-
set: (val) => ah.addEvent(onEvt, val),
|
|
303
|
-
};
|
|
304
318
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
"
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
319
|
+
toJSON() {} // Converting circular structure to JSON
|
|
320
|
+
addEvent(type, event) {
|
|
321
|
+
if (type.startsWith("on")) {
|
|
322
|
+
this.proxyEvents[type] = typeof event === "function" ? event : null;
|
|
323
|
+
} else {
|
|
324
|
+
if (typeof event === "object" && event !== null)
|
|
325
|
+
event = event.handleEvent;
|
|
326
|
+
if (typeof event !== "function") return;
|
|
327
|
+
this.proxyEvents[type] = this.proxyEvents[type] || new Set();
|
|
328
|
+
this.proxyEvents[type].add(event);
|
|
329
|
+
}
|
|
313
330
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (typeof event !== "function") return;
|
|
323
|
-
this.proxyEvents[type] = this.proxyEvents[type] || new Set();
|
|
324
|
-
this.proxyEvents[type].add(event);
|
|
331
|
+
removeEvent(type, event) {
|
|
332
|
+
if (type.startsWith("on")) {
|
|
333
|
+
this.proxyEvents[type] = null;
|
|
334
|
+
} else {
|
|
335
|
+
if (typeof event === "object" && event !== null)
|
|
336
|
+
event = event.handleEvent;
|
|
337
|
+
this.proxyEvents[type] && this.proxyEvents[type].delete(event);
|
|
338
|
+
}
|
|
325
339
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
this.proxyEvents[type]
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
340
|
+
dispatchEvent(e) {
|
|
341
|
+
e.stopImmediatePropagation = stopImmediatePropagation;
|
|
342
|
+
defineProp(e, "target", () => this.proxyXhr);
|
|
343
|
+
this.proxyEvents[e.type] &&
|
|
344
|
+
this.proxyEvents[e.type].forEach((fn) => {
|
|
345
|
+
this.resThenable.then(
|
|
346
|
+
() => !e.ajaxHooker_isStopped && fn.call(this.proxyXhr, e)
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
if (e.ajaxHooker_isStopped) return;
|
|
350
|
+
const onEvent = this.proxyEvents["on" + e.type];
|
|
351
|
+
onEvent && this.resThenable.then(onEvent.bind(this.proxyXhr, e));
|
|
334
352
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
353
|
+
setRequestHeader(header, value) {
|
|
354
|
+
this.originalXhr.setRequestHeader(header, value);
|
|
355
|
+
if (this.originalXhr.readyState !== 1) return;
|
|
356
|
+
const headers = this.request.headers;
|
|
357
|
+
headers[header] =
|
|
358
|
+
header in headers ? `${headers[header]}, ${value}` : value;
|
|
359
|
+
}
|
|
360
|
+
addEventListener(...args) {
|
|
361
|
+
if (xhrAsyncEvents.includes(args[0])) {
|
|
362
|
+
this.addEvent(args[0], args[1]);
|
|
363
|
+
} else {
|
|
364
|
+
this.originalXhr.addEventListener(...args);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
removeEventListener(...args) {
|
|
368
|
+
if (xhrAsyncEvents.includes(args[0])) {
|
|
369
|
+
this.removeEvent(args[0], args[1]);
|
|
370
|
+
} else {
|
|
371
|
+
this.originalXhr.removeEventListener(...args);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
open(method, url, async = true, ...args) {
|
|
375
|
+
this.request = {
|
|
376
|
+
type: "xhr",
|
|
377
|
+
url: url.toString(),
|
|
378
|
+
method: method.toUpperCase(),
|
|
379
|
+
abort: false,
|
|
380
|
+
headers: {},
|
|
381
|
+
data: null,
|
|
382
|
+
response: null,
|
|
383
|
+
async: !!async,
|
|
384
|
+
};
|
|
385
|
+
this.openArgs = args;
|
|
386
|
+
this.resThenable = new SyncThenable();
|
|
387
|
+
[
|
|
388
|
+
"responseURL",
|
|
389
|
+
"readyState",
|
|
390
|
+
"status",
|
|
391
|
+
"statusText",
|
|
392
|
+
...xhrResponses,
|
|
393
|
+
].forEach((key) => {
|
|
394
|
+
delete this.proxyProps[key];
|
|
344
395
|
});
|
|
345
|
-
|
|
346
|
-
const onEvent = this.proxyEvents["on" + e.type];
|
|
347
|
-
onEvent && this.resThenable.then(onEvent.bind(this.proxyXhr, e));
|
|
348
|
-
}
|
|
349
|
-
setRequestHeader(header, value) {
|
|
350
|
-
this.originalXhr.setRequestHeader(header, value);
|
|
351
|
-
if (this.originalXhr.readyState !== 1) return;
|
|
352
|
-
const headers = this.request.headers;
|
|
353
|
-
headers[header] =
|
|
354
|
-
header in headers ? `${headers[header]}, ${value}` : value;
|
|
355
|
-
}
|
|
356
|
-
addEventListener(...args) {
|
|
357
|
-
if (xhrAsyncEvents.includes(args[0])) {
|
|
358
|
-
this.addEvent(args[0], args[1]);
|
|
359
|
-
} else {
|
|
360
|
-
this.originalXhr.addEventListener(...args);
|
|
396
|
+
return this.originalXhr.open(method, url, async, ...args);
|
|
361
397
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
398
|
+
send(data) {
|
|
399
|
+
const ah = this;
|
|
400
|
+
const xhr = ah.originalXhr;
|
|
401
|
+
const request = ah.request;
|
|
402
|
+
if (!request) return xhr.send(data);
|
|
403
|
+
request.data = data;
|
|
404
|
+
new AHRequest(request).waitForRequestKeys().then(() => {
|
|
405
|
+
if (request.abort) {
|
|
406
|
+
if (typeof request.response === "function") {
|
|
407
|
+
Object.assign(ah.proxyProps, {
|
|
408
|
+
responseURL: { value: request.url },
|
|
409
|
+
readyState: { value: 4 },
|
|
410
|
+
status: { value: 200 },
|
|
411
|
+
statusText: { value: "OK" },
|
|
412
|
+
});
|
|
413
|
+
xhrAsyncEvents.forEach((evt) =>
|
|
414
|
+
xhr.dispatchEvent(new Event(evt))
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
xhr.open(
|
|
419
|
+
request.method,
|
|
420
|
+
request.url,
|
|
421
|
+
request.async,
|
|
422
|
+
...ah.openArgs
|
|
423
|
+
);
|
|
424
|
+
for (const header in request.headers) {
|
|
425
|
+
xhr.setRequestHeader(header, request.headers[header]);
|
|
426
|
+
}
|
|
427
|
+
xhr.send(request.data);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
368
430
|
}
|
|
369
431
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
432
|
+
function fakeXHR() {
|
|
433
|
+
const xhr = new winAh.realXHR();
|
|
434
|
+
if ("__ajaxHooker" in xhr)
|
|
435
|
+
console.warn("检测到不同版本的ajaxHooker,可能发生冲突!");
|
|
436
|
+
xhr.__ajaxHooker = new XhrHooker(xhr);
|
|
437
|
+
return xhr.__ajaxHooker.proxyXhr;
|
|
438
|
+
}
|
|
439
|
+
fakeXHR.prototype = win.XMLHttpRequest.prototype;
|
|
440
|
+
Object.keys(win.XMLHttpRequest).forEach(
|
|
441
|
+
(key) => (fakeXHR[key] = win.XMLHttpRequest[key])
|
|
442
|
+
);
|
|
443
|
+
function fakeFetch(url, options = {}) {
|
|
444
|
+
if (!url) return winAh.realFetch.call(win, url, options);
|
|
445
|
+
const init = {};
|
|
446
|
+
if (getType(url) === "[object Request]") {
|
|
447
|
+
for (const prop of fetchInitProps) init[prop] = url[prop];
|
|
448
|
+
url = url.url;
|
|
449
|
+
}
|
|
450
|
+
url = url.toString();
|
|
451
|
+
Object.assign(init, options);
|
|
452
|
+
init.method = init.method || "GET";
|
|
453
|
+
init.headers = init.headers || {};
|
|
454
|
+
const request = {
|
|
455
|
+
type: "fetch",
|
|
456
|
+
url: url,
|
|
457
|
+
method: init.method.toUpperCase(),
|
|
375
458
|
abort: false,
|
|
376
|
-
headers:
|
|
377
|
-
data:
|
|
459
|
+
headers: parseHeaders(init.headers),
|
|
460
|
+
data: init.body,
|
|
378
461
|
response: null,
|
|
379
|
-
async:
|
|
462
|
+
async: true,
|
|
380
463
|
};
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
464
|
+
const req = new AHRequest(request);
|
|
465
|
+
return new Promise((resolve, reject) => {
|
|
466
|
+
req
|
|
467
|
+
.waitForRequestKeys()
|
|
468
|
+
.then(() => {
|
|
469
|
+
if (request.abort) {
|
|
470
|
+
if (typeof request.response === "function") {
|
|
471
|
+
const response = {
|
|
472
|
+
finalUrl: request.url,
|
|
473
|
+
status: 200,
|
|
474
|
+
responseHeaders: {},
|
|
475
|
+
};
|
|
476
|
+
req.waitForResponseKeys(response).then(() => {
|
|
477
|
+
const key = fetchResponses.find((k) => k in response);
|
|
478
|
+
let val = response[key];
|
|
479
|
+
if (key === "json" && typeof val === "object") {
|
|
480
|
+
val = catchError(JSON.stringify.bind(JSON), val);
|
|
481
|
+
}
|
|
482
|
+
const res = new Response(val, {
|
|
483
|
+
status: 200,
|
|
484
|
+
statusText: "OK",
|
|
485
|
+
});
|
|
486
|
+
defineProp(res, "type", () => "basic");
|
|
487
|
+
defineProp(res, "url", () => request.url);
|
|
488
|
+
resolve(res);
|
|
489
|
+
});
|
|
490
|
+
} else {
|
|
491
|
+
reject(new DOMException("aborted", "AbortError"));
|
|
492
|
+
}
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
init.method = request.method;
|
|
496
|
+
init.headers = request.headers;
|
|
497
|
+
init.body = request.data;
|
|
498
|
+
winAh.realFetch.call(win, request.url, init).then((res) => {
|
|
499
|
+
if (typeof request.response === "function") {
|
|
500
|
+
const response = {
|
|
501
|
+
finalUrl: res.url,
|
|
502
|
+
status: res.status,
|
|
503
|
+
responseHeaders: parseHeaders(res.headers),
|
|
504
|
+
};
|
|
505
|
+
fetchResponses.forEach(
|
|
506
|
+
(key) =>
|
|
507
|
+
(res[key] = function () {
|
|
508
|
+
if (key in response)
|
|
509
|
+
return Promise.resolve(response[key]);
|
|
510
|
+
return resProto[key].call(this).then((val) => {
|
|
511
|
+
response[key] = val;
|
|
512
|
+
return req
|
|
513
|
+
.waitForResponseKeys(response)
|
|
514
|
+
.then(() => (key in response ? response[key] : val));
|
|
515
|
+
});
|
|
516
|
+
})
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
resolve(res);
|
|
520
|
+
}, reject);
|
|
521
|
+
})
|
|
522
|
+
.catch((err) => {
|
|
523
|
+
console.error(err);
|
|
524
|
+
resolve(winAh.realFetch.call(win, url, init));
|
|
525
|
+
});
|
|
391
526
|
});
|
|
392
|
-
return this.originalXhr.open(method, url, async, ...args);
|
|
393
527
|
}
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
request.data = data;
|
|
400
|
-
new AHRequest(request).waitForRequestKeys().then(() => {
|
|
401
|
-
if (request.abort) {
|
|
402
|
-
if (typeof request.response === "function") {
|
|
403
|
-
Object.assign(ah.proxyProps, {
|
|
404
|
-
responseURL: { value: request.url },
|
|
405
|
-
readyState: { value: 4 },
|
|
406
|
-
status: { value: 200 },
|
|
407
|
-
statusText: { value: "OK" },
|
|
408
|
-
});
|
|
409
|
-
xhrAsyncEvents.forEach((evt) => xhr.dispatchEvent(new Event(evt)));
|
|
410
|
-
}
|
|
411
|
-
} else {
|
|
412
|
-
xhr.open(request.method, request.url, request.async, ...ah.openArgs);
|
|
413
|
-
for (const header in request.headers) {
|
|
414
|
-
xhr.setRequestHeader(header, request.headers[header]);
|
|
415
|
-
}
|
|
416
|
-
xhr.send(request.data);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
528
|
+
function fakeFetchClone() {
|
|
529
|
+
const descriptors = Object.getOwnPropertyDescriptors(this);
|
|
530
|
+
const res = winAh.realFetchClone.call(this);
|
|
531
|
+
Object.defineProperties(res, descriptors);
|
|
532
|
+
return res;
|
|
419
533
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
534
|
+
winAh = win.__ajaxHooker = winAh || {
|
|
535
|
+
version,
|
|
536
|
+
fakeXHR,
|
|
537
|
+
fakeFetch,
|
|
538
|
+
fakeFetchClone,
|
|
539
|
+
realXHR: win.XMLHttpRequest,
|
|
540
|
+
realFetch: win.fetch,
|
|
541
|
+
realFetchClone: resProto.clone,
|
|
542
|
+
hookInsts: new Set(),
|
|
543
|
+
};
|
|
544
|
+
if (winAh.version !== version)
|
|
424
545
|
console.warn("检测到不同版本的ajaxHooker,可能发生冲突!");
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
headers: parseHeaders(init.headers),
|
|
449
|
-
data: init.body,
|
|
450
|
-
response: null,
|
|
451
|
-
async: true,
|
|
546
|
+
win.XMLHttpRequest = winAh.fakeXHR;
|
|
547
|
+
win.fetch = winAh.fakeFetch;
|
|
548
|
+
resProto.clone = winAh.fakeFetchClone;
|
|
549
|
+
winAh.hookInsts.add(hookInst);
|
|
550
|
+
return {
|
|
551
|
+
hook: (fn) => hookInst.hookFns.push(fn),
|
|
552
|
+
filter: (arr) => {
|
|
553
|
+
if (Array.isArray(arr)) hookInst.filters = arr;
|
|
554
|
+
},
|
|
555
|
+
protect: () => {
|
|
556
|
+
readonly(win, "XMLHttpRequest", winAh.fakeXHR);
|
|
557
|
+
readonly(win, "fetch", winAh.fakeFetch);
|
|
558
|
+
readonly(resProto, "clone", winAh.fakeFetchClone);
|
|
559
|
+
},
|
|
560
|
+
unhook: () => {
|
|
561
|
+
winAh.hookInsts.delete(hookInst);
|
|
562
|
+
if (!winAh.hookInsts.size) {
|
|
563
|
+
writable(win, "XMLHttpRequest", winAh.realXHR);
|
|
564
|
+
writable(win, "fetch", winAh.realFetch);
|
|
565
|
+
writable(resProto, "clone", winAh.realFetchClone);
|
|
566
|
+
delete win.__ajaxHooker;
|
|
567
|
+
}
|
|
568
|
+
},
|
|
452
569
|
};
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
req
|
|
456
|
-
.waitForRequestKeys()
|
|
457
|
-
.then(() => {
|
|
458
|
-
if (request.abort) {
|
|
459
|
-
if (typeof request.response === "function") {
|
|
460
|
-
const response = {
|
|
461
|
-
finalUrl: request.url,
|
|
462
|
-
status: 200,
|
|
463
|
-
responseHeaders: {},
|
|
464
|
-
};
|
|
465
|
-
req.waitForResponseKeys(response).then(() => {
|
|
466
|
-
const key = fetchResponses.find((k) => k in response);
|
|
467
|
-
let val = response[key];
|
|
468
|
-
if (key === "json" && typeof val === "object") {
|
|
469
|
-
val = catchError(JSON.stringify.bind(JSON), val);
|
|
470
|
-
}
|
|
471
|
-
const res = new Response(val, {
|
|
472
|
-
status: 200,
|
|
473
|
-
statusText: "OK",
|
|
474
|
-
});
|
|
475
|
-
defineProp(res, "type", () => "basic");
|
|
476
|
-
defineProp(res, "url", () => request.url);
|
|
477
|
-
resolve(res);
|
|
478
|
-
});
|
|
479
|
-
} else {
|
|
480
|
-
reject(new DOMException("aborted", "AbortError"));
|
|
481
|
-
}
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
init.method = request.method;
|
|
485
|
-
init.headers = request.headers;
|
|
486
|
-
init.body = request.data;
|
|
487
|
-
winAh.realFetch.call(win, request.url, init).then((res) => {
|
|
488
|
-
if (typeof request.response === "function") {
|
|
489
|
-
const response = {
|
|
490
|
-
finalUrl: res.url,
|
|
491
|
-
status: res.status,
|
|
492
|
-
responseHeaders: parseHeaders(res.headers),
|
|
493
|
-
};
|
|
494
|
-
fetchResponses.forEach(
|
|
495
|
-
(key) =>
|
|
496
|
-
(res[key] = function () {
|
|
497
|
-
if (key in response) return Promise.resolve(response[key]);
|
|
498
|
-
return resProto[key].call(this).then((val) => {
|
|
499
|
-
response[key] = val;
|
|
500
|
-
return req
|
|
501
|
-
.waitForResponseKeys(response)
|
|
502
|
-
.then(() => (key in response ? response[key] : val));
|
|
503
|
-
});
|
|
504
|
-
})
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
resolve(res);
|
|
508
|
-
}, reject);
|
|
509
|
-
})
|
|
510
|
-
.catch((err) => {
|
|
511
|
-
console.error(err);
|
|
512
|
-
resolve(winAh.realFetch.call(win, url, init));
|
|
513
|
-
});
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
function fakeFetchClone() {
|
|
517
|
-
const descriptors = Object.getOwnPropertyDescriptors(this);
|
|
518
|
-
const res = winAh.realFetchClone.call(this);
|
|
519
|
-
Object.defineProperties(res, descriptors);
|
|
520
|
-
return res;
|
|
521
|
-
}
|
|
522
|
-
winAh = win.__ajaxHooker = winAh || {
|
|
523
|
-
version,
|
|
524
|
-
fakeXHR,
|
|
525
|
-
fakeFetch,
|
|
526
|
-
fakeFetchClone,
|
|
527
|
-
realXHR: win.XMLHttpRequest,
|
|
528
|
-
realFetch: win.fetch,
|
|
529
|
-
realFetchClone: resProto.clone,
|
|
530
|
-
hookInsts: new Set(),
|
|
531
|
-
};
|
|
532
|
-
if (winAh.version !== version)
|
|
533
|
-
console.warn("检测到不同版本的ajaxHooker,可能发生冲突!");
|
|
534
|
-
win.XMLHttpRequest = winAh.fakeXHR;
|
|
535
|
-
win.fetch = winAh.fakeFetch;
|
|
536
|
-
resProto.clone = winAh.fakeFetchClone;
|
|
537
|
-
winAh.hookInsts.add(hookInst);
|
|
538
|
-
return {
|
|
539
|
-
hook: (fn) => hookInst.hookFns.push(fn),
|
|
540
|
-
filter: (arr) => {
|
|
541
|
-
if (Array.isArray(arr)) hookInst.filters = arr;
|
|
542
|
-
},
|
|
543
|
-
protect: () => {
|
|
544
|
-
readonly(win, "XMLHttpRequest", winAh.fakeXHR);
|
|
545
|
-
readonly(win, "fetch", winAh.fakeFetch);
|
|
546
|
-
readonly(resProto, "clone", winAh.fakeFetchClone);
|
|
547
|
-
},
|
|
548
|
-
unhook: () => {
|
|
549
|
-
winAh.hookInsts.delete(hookInst);
|
|
550
|
-
if (!winAh.hookInsts.size) {
|
|
551
|
-
writable(win, "XMLHttpRequest", winAh.realXHR);
|
|
552
|
-
writable(win, "fetch", winAh.realFetch);
|
|
553
|
-
writable(resProto, "clone", winAh.realFetchClone);
|
|
554
|
-
delete win.__ajaxHooker;
|
|
555
|
-
}
|
|
556
|
-
},
|
|
557
|
-
};
|
|
558
|
-
})();
|
|
570
|
+
})();
|
|
571
|
+
};
|
|
559
572
|
|
|
560
|
-
export {
|
|
573
|
+
export { AjaxHooker };
|