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