CORS-via-GM

CORS via Greasemonkey/Tampermonkey

  1. // ==UserScript==
  2. // @name CORS-via-GM
  3. // @description CORS via Greasemonkey/Tampermonkey
  4. // @version 2.0
  5. // @include /^(file:///.*/index.html|https?://(127.0.0.1|192.168.\d+.\d+|localhost)(:\d+)?/(([^/]*/)?bundled/)?(index.html)?)(\?.*|$)/
  6. // @include /\b(pages\.dev|onrender\.com)\b/
  7. // @include /\b(?:github.io|gitlab.io|glitch.me|js.org|eu.org|web.app|netlify.(?:com|app)|now.sh|vercel.(?:com|app)|herokuapp.com|neocities.org)/
  8. // @include /https?://\w+?fork\.org/
  9. // @connect *
  10. // @grant GM_xmlhttpRequest
  11. // @namespace https://greasyfork.org/users/882700
  12. // @license WTFPL
  13. // ==/UserScript==
  14.  
  15.  
  16. const CORSViaGM = document.body.appendChild(Object.assign(document.createElement('div'), { id: 'CORSViaGM' }))
  17.  
  18. addEventListener('fetchViaGM', e => GM_fetch(e.detail.forwardingFetch))
  19.  
  20. CORSViaGM.init = function (window = unsafeWindow) {
  21. if (!window) throw 'The `window` parameter must be passed in!'
  22. window.fetch = window.fetchViaGM = fetchViaGM.bind(window)
  23.  
  24. // Support for service worker
  25. window.forwardingFetch = new BroadcastChannel('forwardingFetch')
  26. window.forwardingFetch.onmessage = async e => {
  27. const req = e.data
  28. const { url } = req
  29. const res = await fetchViaGM(url, req)
  30. const response = await res.blob()
  31. window.forwardingFetch.postMessage({ type: 'fetchResponse', url, response })
  32. }
  33.  
  34. window._CORSViaGM && window._CORSViaGM.inited.done()
  35.  
  36. const info = '🙉 CORS-via-GM initiated!'
  37. console.info(info)
  38. return info
  39. }
  40.  
  41.  
  42. function GM_fetch(p) {
  43. GM_xmlhttpRequest({
  44. ...p.init,
  45. url: p.url,
  46. method: p.init.method || 'GET',
  47. data: p.body,
  48. responseType: 'blob', // !!important
  49. onload: responseDetails => p.res(new Response(
  50. responseDetails.response,
  51. {
  52. status: responseDetails.status,
  53. statusText: responseDetails.statusText,
  54. headers: convertHeaders(responseDetails.responseHeaders)
  55. }
  56. ))
  57. })
  58. }
  59.  
  60. function fetchViaGM(url, init) {
  61. let res
  62. const p = new Promise(r => res = r)
  63. p.res = res
  64. p.url = url
  65. p.init = init || {}
  66. dispatchEvent(new CustomEvent('fetchViaGM', { detail: { forwardingFetch: p } }))
  67. return p
  68. }
  69.  
  70.  
  71. function convertHeaders(string) {
  72. return JSON.parse(`{${string.trim().split(/(?:\r?\n(?!(?:.(?!: ))+\r?\n))+/).map(_ => _.replaceAll('"', '\\"').replace(/[\r\n]/g, '; ').replace(/^(.+?):\s*(.+)/s, '"$1": "$2"')).join(',\n')}}`)
  73. }