@whitesev/utils 2.9.12 → 2.9.13

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