├── fortnite ├── assets ├── icon.png └── meteor.png ├── lib ├── index.d.ts └── index.cjs ├── .npmignore ├── .gitignore ├── src ├── bundle │ ├── util │ │ ├── createOrigin.ts │ │ ├── logger.ts │ │ ├── formatUrl.ts │ │ ├── createContext.ts │ │ ├── plugins.ts │ │ └── cookies.ts │ ├── rewrite │ │ ├── srcset.ts │ │ ├── url.ts │ │ ├── css.ts │ │ ├── js.ts │ │ ├── headers.ts │ │ └── html.ts │ └── index.ts ├── client │ ├── apis │ │ ├── workers.ts │ │ ├── css.ts │ │ ├── history.ts │ │ ├── ws.ts │ │ ├── eventlistener.ts │ │ ├── navigator.ts │ │ ├── storage.ts │ │ ├── requests.ts │ │ └── location.ts │ ├── rewrite.ts │ ├── index.ts │ ├── patch.ts │ └── dom.ts ├── config.ts ├── codecs │ ├── index.ts │ └── locvar.ts ├── types.d.ts ├── errorpagecss.ts └── worker.ts ├── pages.sh ├── tsconfig.json ├── .github └── workflows │ └── quality.yaml ├── demo ├── sw.js ├── iframe.html ├── 404.html └── index.html ├── TODO.md ├── biome.json ├── package.json ├── server.ts ├── README.md ├── LICENSE └── pnpm-lock.yaml /fortnite: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeteorProxy/meteor-old/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/meteor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MeteorProxy/meteor-old/HEAD/assets/meteor.png -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from '../src/types' 2 | declare const meteorPath: string 3 | 4 | export { meteorPath } 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | docs 3 | .github 4 | build.ts 5 | server.ts 6 | pnpm-lock.yaml 7 | package-lock.json 8 | demo -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | lock 4 | cache 5 | demo/meteor 6 | demo/bare-mux.js 7 | metafile.json 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /lib/index.cjs: -------------------------------------------------------------------------------- 1 | const { resolve } = require('node:path') 2 | 3 | const meteorPath = resolve(__dirname, '..', 'dist') 4 | 5 | exports.meteorPath = meteorPath 6 | -------------------------------------------------------------------------------- /src/bundle/util/createOrigin.ts: -------------------------------------------------------------------------------- 1 | export function createOrigin() { 2 | return new URL( 3 | self.$meteor.config.codec.decode( 4 | location.href.slice((location.origin + self.$meteor.config.prefix).length) 5 | ) 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pnpm i 3 | pnpm run build 4 | echo "Built meteor, copying stuff now..." 5 | mkdir app 6 | cp -r demo/* app/ 7 | cp -r dist/* app/meteor 8 | cp -r node_modules/@mercuryworkshop/bare-mux/* app/baremux/ 9 | echo "Copied all needed files for the demo." -------------------------------------------------------------------------------- /src/bundle/util/logger.ts: -------------------------------------------------------------------------------- 1 | export function log(message: string, color = '#945cff') { 2 | const style = ` 3 | background: ${color}; 4 | border-radius: 0.5em; 5 | color: white; 6 | font-weight: medium; 7 | padding: 2px 0.5em; 8 | ` 9 | 10 | console.log(`%c${'Meteor'}%c ${message}`, style, '') 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | }, 7 | "skipLibCheck": true, 8 | "lib": ["ES2022", "dom", "webworker"], 9 | "target": "ES2022", 10 | "module": "ES2022", 11 | "moduleResolution": "Bundler", 12 | "noEmit": true 13 | }, 14 | "include": ["src"], 15 | "exclude": ["node_modules"] 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/quality.yaml: -------------------------------------------------------------------------------- 1 | name: Code quality 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | quality: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Setup Biome 14 | uses: biomejs/setup-biome@v2 15 | with: 16 | version: latest 17 | - name: Run Biome 18 | run: biome ci . 19 | -------------------------------------------------------------------------------- /src/bundle/rewrite/srcset.ts: -------------------------------------------------------------------------------- 1 | export function rewriteSrcset(srcset: string, origin: URL) { 2 | return srcset 3 | .split(', ') 4 | .map((set) => { 5 | return set 6 | .split(' ') 7 | .map((url, index) => { 8 | return index === 0 9 | ? self.$meteor.rewrite.url.encode(url, origin) 10 | : url 11 | }) 12 | .join(' ') 13 | }) 14 | .join(', ') 15 | } 16 | -------------------------------------------------------------------------------- /src/bundle/rewrite/url.ts: -------------------------------------------------------------------------------- 1 | export function encodeURL(url: string, origin: URL): string { 2 | return ( 3 | location.origin + 4 | self.$meteor.config.prefix + 5 | self.$meteor.config.codec.encode(new URL(url, origin).href) 6 | ) 7 | } 8 | 9 | export function decodeURL(string: string) { 10 | return self.$meteor.config.codec.decode( 11 | string.slice((location.origin + self.$meteor.config.prefix).length) 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/client/apis/workers.ts: -------------------------------------------------------------------------------- 1 | import { patchConstructor, patchFunction } from '../patch' 2 | import { rewriteStringOrUrl } from '../rewrite' 3 | window.Worker = patchConstructor(Worker, (args) => [ 4 | rewriteStringOrUrl(args[0], self.$meteor.util.createOrigin()), 5 | args[1] 6 | ]) 7 | 8 | window.Worklet.prototype.addModule = patchFunction( 9 | Worklet.prototype.addModule, 10 | (args) => [ 11 | rewriteStringOrUrl(args[0], self.$meteor.util.createOrigin()), 12 | args[1] 13 | ] 14 | ) 15 | -------------------------------------------------------------------------------- /src/client/rewrite.ts: -------------------------------------------------------------------------------- 1 | export function rewriteStringOrUrl(input: string | URL, origin?: URL) { 2 | if (!origin) { 3 | origin = self.$meteor.util.createOrigin() 4 | } 5 | 6 | if (input instanceof URL) { 7 | return new URL( 8 | self.$meteor.rewrite.url.encode( 9 | input.toString(), 10 | self.$meteor.util.createOrigin() 11 | ) 12 | ) 13 | } 14 | 15 | return self.$meteor.rewrite.url.encode( 16 | input, 17 | self.$meteor.util.createOrigin() 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /demo/sw.js: -------------------------------------------------------------------------------- 1 | importScripts('/meteor/meteor.codecs.js') 2 | importScripts('/meteor/meteor.config.js') 3 | importScripts('/meteor/meteor.bundle.js') 4 | importScripts('/meteor/meteor.worker.js') 5 | 6 | const meteor = new MeteorServiceWorker() 7 | function handleRequest(event) { 8 | if (meteor.shouldRoute(event)) { 9 | return meteor.handleFetch(event) 10 | } 11 | 12 | return fetch(event.request) 13 | } 14 | self.addEventListener('fetch', (event) => { 15 | event.respondWith(handleRequest(event)) 16 | }) 17 | -------------------------------------------------------------------------------- /src/bundle/util/formatUrl.ts: -------------------------------------------------------------------------------- 1 | export function formatUrl( 2 | input: string, 3 | template = 'https://google.com/search?q=%s' 4 | ): string { 5 | function isValidUrl(url: string) { 6 | return URL.canParse(url) 7 | } 8 | 9 | const formatted = isValidUrl(input) 10 | ? input 11 | : isValidUrl(`http://${input}`) && `http://${input}`.includes('.') 12 | ? `http://${input}` 13 | : template.replace('%s', encodeURIComponent(input)) 14 | 15 | return ( 16 | self.$meteor.config.prefix + self.$meteor.config.codec.encode(formatted) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/client/apis/css.ts: -------------------------------------------------------------------------------- 1 | import { patchFunction } from '../patch' 2 | const urlProps = [ 3 | 'background', 4 | 'background-image', 5 | 'mask', 6 | 'mask-image', 7 | 'list-style', 8 | 'list-style-image', 9 | 'border-image', 10 | 'border-image-source', 11 | 'cursor' 12 | ] 13 | CSSStyleDeclaration.prototype.setProperty = patchFunction( 14 | CSSStyleDeclaration.prototype.setProperty, 15 | ([prop, value, ...rest]) => { 16 | if (urlProps.includes(prop)) { 17 | value = self.$meteor.rewrite.css(value, new URL(self.$location.origin)) 18 | } 19 | return [prop, value, ...rest] 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /src/client/apis/history.ts: -------------------------------------------------------------------------------- 1 | import { patchFunction } from '../patch' 2 | import { rewriteStringOrUrl } from '../rewrite' 3 | // biome-ignore lint: 4 | const rwFun: any = ([state, , url]) => { 5 | if (url) { 6 | url = rewriteStringOrUrl(url) 7 | } 8 | return [state, '', url] 9 | } 10 | window.history.replaceState = patchFunction(window.history.replaceState, rwFun) 11 | window.history.pushState = patchFunction(window.history.pushState, rwFun) 12 | 13 | window.History.prototype.replaceState = patchFunction( 14 | window.History.prototype.replaceState, 15 | rwFun 16 | ) 17 | window.History.prototype.pushState = patchFunction( 18 | window.History.prototype.pushState, 19 | rwFun 20 | ) 21 | -------------------------------------------------------------------------------- /src/client/apis/ws.ts: -------------------------------------------------------------------------------- 1 | import BareClient from '@mercuryworkshop/bare-mux' 2 | const client = new BareClient() 3 | const RealWebSocket = globalThis.WebSocket 4 | 5 | globalThis.WebSocket = new Proxy(globalThis.WebSocket, { 6 | construct(_target, args) { 7 | if (self.$meteor_config.debug === true) { 8 | self.$meteor.util.log( 9 | `Creating websocket to ${args[0]} on origin ${self.$meteor.util.createOrigin().origin}`, 10 | 'teal' 11 | ) 12 | } 13 | return client.createWebSocket( 14 | args[0], 15 | args[1], 16 | RealWebSocket, 17 | { 18 | 'User-Agent': navigator.userAgent, 19 | origin: self.$location.origin 20 | }, 21 | ArrayBuffer.prototype 22 | ) 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /src/bundle/util/createContext.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from '@/types' 2 | export function createContext(html: string, origin: URL): Context { 3 | let modified = html 4 | 5 | function injectAtPosition(content: string, position: number) { 6 | return modified.slice(0, position) + content + modified.slice(position) 7 | } 8 | 9 | return { 10 | injectHTML: async (tag, location = 'head', rewrite = true) => { 11 | const tagString = rewrite 12 | ? await self.$meteor.rewrite.html(tag, origin) 13 | : tag 14 | const headCloseIndex = modified.indexOf(``) 15 | if (headCloseIndex !== -1) { 16 | modified = injectAtPosition(tagString, headCloseIndex) 17 | } else { 18 | modified += tagString 19 | } 20 | }, 21 | getModified: () => modified 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Meteor To-Do 2 | 3 | - [x] Add JS Rewriting 4 | - [ ] Patch more client APIs 5 | - [x] data: url handling 6 | - [x] Module system 7 | - [ ] More efficient rewriting 8 | - [x] Fix Iframes 9 | - [x] Fix weird data problems 10 | - [x] Add support for raw headers [Stub] - Implementation notes: Further testing is still needed for raw headers but it should be working 11 | - [ ] Fix nextJS sites 12 | - [ ] Fix links (leaving the proxy and duplicating the origin) [In Progress] 13 | - [x] Add Media Element rewriting (video/audio elements etc) [Stub] - Implementation notes: Youtube's video player is currently not working. Only elements with `