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