├── .changeset └── config.json ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── ISSUE-TEMPLATE.md │ └── config.yml ├── PULL_REQUEST_TEMPLATE │ └── pull_request_template.md └── workflows │ ├── binaries.yml │ ├── docker-build.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── astro.config.ts ├── config.example.toml ├── deno.jsonc ├── deno.lock ├── docker-builds.yml ├── docker-compose.yml ├── package.json ├── public ├── logo.svg ├── search.js ├── sw.js └── uv │ └── uv.config.js ├── server ├── cli.ts ├── config │ └── config.ts ├── env.d.ts ├── full │ ├── server.ts │ └── serverFactory.ts ├── message.ts └── standalone │ └── standalone.ts ├── src ├── assets │ ├── apps │ │ ├── coolmath.png │ │ ├── discord.jpg │ │ ├── gfnow.png │ │ ├── google.jpg │ │ ├── reddit.png │ │ ├── retroarch.png │ │ ├── spotify.png │ │ ├── tt.jpg │ │ ├── twitch.jpg │ │ ├── twitter.png │ │ ├── y8.png │ │ └── yt.png │ └── games │ │ ├── 1v1-lol.png │ │ ├── 2048.png │ │ ├── basketball-stars.png │ │ ├── binding-of-isaac.jpeg │ │ ├── bloons.webp │ │ ├── bloonstd.jpg │ │ ├── bloonstd2.png │ │ ├── bloxors.png │ │ ├── cat-ninja.png │ │ ├── chromedino.png │ │ ├── cookieclicker.jpg │ │ ├── crossy-road.png │ │ ├── cybertanks.png │ │ ├── dadish-2.png │ │ ├── dadish.png │ │ ├── doodle-jump.png │ │ ├── ducklife.webp │ │ ├── ducklife2.jpg │ │ ├── ducklife3.jpg │ │ ├── eaglerx.webp │ │ ├── emulatorjs.png │ │ ├── flashtetris.png │ │ ├── frogger.jpeg │ │ ├── fruit-ninja.png │ │ ├── fruitninja.jpg │ │ ├── geometrydash.png │ │ ├── gm2.jpeg │ │ ├── gpacman.jpg │ │ ├── hardestgame.jpg │ │ ├── hexgl.jpg │ │ ├── mario.webp │ │ ├── miniagario.png │ │ ├── moke.png │ │ ├── monkey-mart.png │ │ ├── pacman.jpg │ │ ├── paper.io2.webp │ │ ├── portaltheflashversion.jpg │ │ ├── retrobowl.jpg │ │ ├── riddleschool.webp │ │ ├── riddleschool2.webp │ │ ├── riddleschool3.jpg │ │ ├── riddleschool4.jpg │ │ ├── riddleschool5.webp │ │ ├── run3.png │ │ ├── shellshock.png │ │ ├── slitherio.webp │ │ ├── slope-2.png │ │ ├── slope.jpg │ │ ├── snake.png │ │ ├── ssf.jpeg │ │ ├── stack.png │ │ ├── sticmanhook.jpg │ │ ├── stuntcars2.png │ │ ├── stuntcars3.jpg │ │ ├── subway-surfers.png │ │ ├── superhot.webp │ │ ├── supermario64.jpeg │ │ ├── tanktrouble.webp │ │ ├── tanuki.jpg │ │ ├── templerun2.jpg │ │ ├── the-worlds-hardest-game-2.jpg │ │ ├── tunnel-rush.jpg │ │ ├── vex3.png │ │ ├── vex4.png │ │ ├── vex5.jpeg │ │ ├── vex6.jpeg │ │ ├── vex7.png │ │ ├── watermelon.png │ │ ├── webretro.png │ │ └── yohoho.jpeg ├── components │ ├── FrameManager.astro │ ├── Header.astro │ ├── MobileNav.astro │ ├── NavLinks.astro │ ├── Scripts.astro │ ├── Search.astro │ └── settings │ │ └── Loader.astro ├── data │ └── games.json ├── env.d.ts ├── global.d.ts ├── layouts │ └── Layout.astro ├── pages │ ├── apps.astro │ ├── community.astro │ ├── gs │ │ ├── [game].astro │ │ └── index.astro │ ├── index.astro │ ├── load.astro │ ├── options │ │ ├── about.astro │ │ ├── bt.astro │ │ └── index.astro │ └── support.astro └── ts │ ├── serviceWorker.ts │ └── settings.ts ├── tailwind.config.mjs ├── tsconfig.json └── vendor └── scramjet ├── LICENSE ├── README.md ├── dist ├── scramjet.client.js ├── scramjet.client.js.map ├── scramjet.controller.js ├── scramjet.controller.js.map ├── scramjet.shared.js ├── scramjet.shared.js.map ├── scramjet.sync.js ├── scramjet.sync.js.map ├── scramjet.wasm.js ├── scramjet.worker.js └── scramjet.worker.js.map ├── lib ├── index.cjs └── index.d.ts └── package.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", 3 | "changelog": ["@changesets/changelog-github", { "repo": "titaniumnetwork-dev/incognito" }], 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [], 11 | "privatePackages": { "version": true, "tag": true } 12 | } 13 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode 3 | npm-debug.log 4 | yarn-error.log 5 | .github/ 6 | .env.example 7 | .env 8 | dist/ 9 | .git/ 10 | .astro/ 11 | ~/ 12 | .gitignore 13 | biome.json 14 | docker-compose.yml 15 | Dockerfile 16 | README.md 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ISSUE-TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report Or Feature request 3 | about: Report a Bug or submit a Feature that you would like to see 4 | title: "[BUG/Feature Request]" 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Note: for support questions, please use our [Discord](https://discord.gg/unblock)**. This repository's issues are reserved for feature requests and bug reports. 10 | 11 | - **I'm submitting a ...** 12 | - [ ] bug report 13 | - [ ] feature request 14 | - [ ] support request => Please do not submit support request here, see note at the top of this template. 15 | 16 | - **Do you want to request a _feature_ or report a _bug_?** 17 | 18 | - **What is the current behavior?** 19 | 20 | - **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** 21 | 22 | - **What is the expected behavior?** 23 | 24 | - **What is the motivation / use case for changing the behavior?** 25 | 26 | - **Please tell us about your environment:** 27 | 28 | - Version: 2.0.0-beta.X 29 | - Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] 30 | - Language: [all | TypeScript X.X | ES6/7 | ES5 | Dart] 31 | 32 | - **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Communtiy Support 4 | url: https://discord.gg/unblock 5 | about: Please ask and answer questions here. 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | - **Please check if the PR fulfills these requirements** 2 | 3 | * [ ] Tests for the changes have been added (for bug fixes / features) 4 | * [ ] Docs have been added / updated (for bug fixes / features) 5 | 6 | - **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 7 | 8 | - **What is the current behavior?** (You can also link to an open issue here) 9 | 10 | - **What is the new behavior (if this is a feature change)?** 11 | 12 | - **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?) 13 | 14 | - **Other information**: 15 | -------------------------------------------------------------------------------- /.github/workflows/binaries.yml: -------------------------------------------------------------------------------- 1 | name: Add Binaries to current release. 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | #workflow_run: 8 | # workflows: 9 | # - Release 10 | # types: 11 | # - completed 12 | workflow_dispatch: 13 | 14 | #defaults: 15 | #run: 16 | #shell: bash 17 | 18 | env: 19 | FORCE_COLOR: true 20 | 21 | jobs: 22 | windows-build: 23 | name: Build Windows 24 | if: ${{ github.repository_owner == 'titaniumnetwork-dev' }} 25 | runs-on: windows-latest 26 | permissions: 27 | contents: write 28 | id-token: write 29 | steps: 30 | - uses: actions/checkout@v4 31 | 32 | - name: Setup Deno 33 | uses: denoland/setup-deno@v2 34 | with: 35 | deno-version: 2.1.4 36 | 37 | - name: Install dependencies 38 | run: deno install --allow-scripts 39 | 40 | - name: Build Incog 41 | run: cp config.example.toml config.toml && deno task build 42 | 43 | - name: Compile Windows Binaries 44 | run: deno task exe:windows 45 | 46 | - name: Upload artifacts 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: incognito-windows 50 | path: | 51 | *.exe 52 | maclinux-build: 53 | name: Build MacOS & Linux 54 | if: ${{ github.repository_owner == 'titaniumnetwork-dev' }} 55 | runs-on: ubuntu-latest 56 | permissions: 57 | contents: write 58 | id-token: write 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - name: Setup Deno 63 | uses: denoland/setup-deno@v2 64 | with: 65 | deno-version: 2.1.4 66 | 67 | - name: Install dependencies 68 | run: deno install --allow-scripts 69 | 70 | - name: Build Incog 71 | run: cp config.example.toml config.toml && deno task build 72 | 73 | - name: Compile MacOS 74 | run: deno task exe:macOS 75 | 76 | - name: Compile MacOS Arm64 77 | run: deno task exe:macOSarm 78 | 79 | - name: Compile Linux 80 | run: deno task exe:linux 81 | 82 | - name: Compile Linux Arm64 83 | run: deno task exe:linuxArm 84 | 85 | - name: Upload artifacts 86 | uses: actions/upload-artifact@v4 87 | with: 88 | name: incognito-linux-mac 89 | retention-days: 1 90 | path: | 91 | incognito-* 92 | 93 | 94 | upload-release: 95 | name: Upload the binary files 96 | if: ${{ github.repository_owner == 'titaniumnetwork-dev' }} 97 | runs-on: ubuntu-latest 98 | needs: [windows-build, maclinux-build] 99 | 100 | steps: 101 | - uses: actions/checkout@v4 102 | with: 103 | fetch-depth: 0 104 | fetch-tags: true 105 | - name: Make sure tag is there 106 | id: tag 107 | run: 108 | echo "tag=$(git describe --tags --abbrev=0)" >> $GITHUB_OUTPUT 109 | 110 | - name: Download artifacts for Linux & MacOS 111 | uses: actions/download-artifact@v4 112 | with: 113 | path: . 114 | 115 | - name: Upload Linux & MacOS 116 | uses: svenstaro/upload-release-action@v2 117 | with: 118 | repo_token: ${{ secrets.GITHUB_TOKEN }} 119 | file: incognito-linux-mac/* 120 | tag: ${{ steps.tag.outputs.tag }} 121 | overwrite: true 122 | file_glob: true 123 | 124 | - name: Upload Windows binary 125 | uses: svenstaro/upload-release-action@v2 126 | with: 127 | repo_token: ${{ secrets.GITHUB_TOKEN }} 128 | file: incognito-windows/*.exe 129 | tag: ${{ steps.tag.outputs.tag }} 130 | overwrite: true 131 | file_glob: true 132 | -------------------------------------------------------------------------------- /.github/workflows/docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker image 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | workflow_dispatch: 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | 12 | jobs: 13 | build-and-push: 14 | name: Build and push Docker image to registry 15 | runs-on: ubuntu-latest 16 | if: ${{ github.repository_owner == 'titaniumnetwork-dev' }} 17 | permissions: 18 | contents: write 19 | packages: write 20 | steps: 21 | - name: Checkout repo 22 | uses: actions/checkout@v3 23 | - name: Setup docker buildx 24 | uses: docker/setup-buildx-action@v3 25 | - name: Login To registry ${{ env.REGISTRY }} 26 | uses: docker/login-action@v3 27 | with: 28 | registry: ${{ env.REGISTRY }} 29 | username: ${{ github.actor }} 30 | password: ${{ github.token }} 31 | - name: Extract Docker metadata 32 | id: meta 33 | uses: docker/metadata-action@v3 34 | with: 35 | images: ${{ env.REGISTRY }}/titaniumnetwork-dev/incognito 36 | - name: Build and push 37 | id: build-and-push 38 | uses: docker/build-push-action@v4 39 | with: 40 | context: . 41 | platforms: linux/amd64,linux/arm64 42 | file: ./Dockerfile 43 | name: incognito 44 | push: ${{ github.event_name != 'pull_request' }} 45 | tags: ${{ steps.meta.outputs.tags }} 46 | labels: ${{ steps.meta.outputs.labels }} 47 | cache-from: type=gha 48 | cache-to: type=gha,mode=max 49 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | defaults: 10 | run: 11 | shell: bash 12 | 13 | env: 14 | FORCE_COLOR: true 15 | 16 | jobs: 17 | changelog: 18 | name: Release TAG 19 | if: ${{ github.repository_owner == 'titaniumnetwork-dev' }} 20 | runs-on: ubuntu-latest 21 | permissions: 22 | contents: write 23 | id-token: write 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Setup Deno 28 | uses: denoland/setup-deno@v2 29 | with: 30 | deno-version: 2.1.4 31 | 32 | - name: Install dependencies 33 | run: deno install --allow-scripts 34 | 35 | - name: Create Release Pull Request or Publish 36 | id: changesets 37 | uses: changesets/action@v1 38 | with: 39 | version: deno task changeset:version 40 | publish: deno task changeset:publish 41 | commit: "[ci] release" 42 | title: "[ci] release" 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | 46 | - name: Push tags if created. 47 | run: git push --follow-tags 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | !vendor/scramjet/dist/ 4 | 5 | # generated types 6 | .astro/ 7 | 8 | # dependencies 9 | node_modules/ 10 | 11 | # logs 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # environment variables 18 | .env 19 | .env.production 20 | 21 | #Config stuff 22 | config.toml 23 | 24 | # macOS-specific files 25 | .DS_Store 26 | 27 | # jetbrains setting folder 28 | .idea/ 29 | 30 | # Stupidness 31 | ~/ 32 | 33 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/.gitmodules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.0 2 | - The initial rewrite to Astro. Includes: 3 | - Fully static 4 | - Easier to build 5 | - Feels/is faster to use 6 | - Fixes some bugs 7 | - Among tons of other stuff 8 | 9 | # 1.0.1 10 | - Fixes the games and apps page looking weird 11 | 12 | # 1.0.2 13 | - Fixes Dockerfile issues 14 | 15 | # 1.0.3 16 | - Turns off Rammerhead by default for now 17 | - Docker env fixes again 18 | - SEO 19 | 20 | # 1.0.4 21 | - Bumps RH 22 | - Fixes issues with bare-server-node 23 | 24 | # 1.1.0 25 | - Switches to Deno (mostly) 26 | - General bug fixes 27 | - Removes Rammerhead as an option and replaces it with Scramjet (coming soon) 28 | - Removes bare server and adds libcurl as an option 29 | - Adds a different Deno native server (Hono) - Command: `deno task start:standalone` 30 | - Removes Masqr 31 | - No more SSR, it's purely statically generated 32 | - CI fixes and other general improvements 33 | - Configuration is done via TOML over a bunch of environment vars 34 | - Removes Biome in place of Deno's native formatter 35 | - A better roadmap of what should be done in the future. 36 | 37 | # 1.1.1 38 | - Fixes a bug where if games aren't enabled it redirects to localhost:8080 over just / 39 | - Fixes a bug where if games aren't enabled, it still loads and compresses images over just ignoring them. 40 | 41 | # 1.1.2 42 | - Fixes bugs with apps opening as the full url instead of just the correct link 43 | - Fixes a bug with the iFrame panel where it copies & pastes the full link instead of just the normal link. 44 | - Add Scramjet 45 | 46 | # 1.1.3 47 | - Add the notice that ScramJet is in beta and may break 48 | 49 | # 1.1.4 50 | - Add the ability to disable/enable SEO or selectively render/show it 51 | - Adds the docker-builds.yml file to show the builds I am doing for the docker images. 52 | 53 | # 1.1.5 54 | - Fixes the games (mostly) 55 | 56 | # 1.1.6 57 | - Add the ability to run Incog from a prebuilt file 58 | 59 | # 1.1.7 60 | - Builds both the SEO & non SEO versions of the website for easier deployment 61 | - Adds a `--seo` CLI flag which can enable the SEO build without the config.toml file 62 | - CLI is now easier to use & interact with 63 | - Change and cleanup docs 64 | - Docker command cleanup to reflect new changes 65 | - docker-compose.build.yml no longer exists. 66 | 67 | # v1.1.8 68 | - Add the ability to self update your binary 69 | - Change the way parts of the CLI look 70 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM denoland/deno:debian-2.1.4 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json . 6 | COPY deno.jsonc . 7 | COPY . . 8 | 9 | RUN apt update 10 | RUN apt install -y python3 python3-pip libssl-dev build-essential python3-dev nodejs 11 | RUN cp -n config.example.toml config.toml 12 | RUN deno install --allow-scripts 13 | RUN deno task build 14 | RUN export TERM=xterm-256color 15 | ENV PORT="8000" 16 | VOLUME /app 17 | EXPOSE 8000 18 | ENTRYPOINT ["deno", "task", "start", "--color", "--server"] 19 | CMD ["full"] 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ruby 4 | 5 | repo size 6 | website status 7 | commit a week 8 | original repo 9 | 10 |
11 | 12 |
13 |

Get Started

14 | To get started, press one of the buttons below to deploy Incog 15 |
16 |
17 | 18 | Terminal 19 | 20 | 21 | 22 | Docker 23 | 24 | 25 |
26 | 27 | ## NOTE: 28 | 29 | - This will **NOT** deploy on Github Pages, Netlify, Vercel, Gitlab Pages or any other _static_ host 30 | - This will **NOT** work on Render 31 | 32 | --- 33 | 34 | ## How to get links 35 | 36 | [![Titanium Network Discord](https://invidget.switchblade.xyz/unblock?theme=dark)](https://discord.gg/unblock) 37 | 38 | --- 39 | 40 | ## Features 41 | 42 | - Lots and lots of games 43 | 44 | - Multiple Proxy "Backends": 45 | - [Ultraviolet](https://github.com/titaniumnetwork-dev/ultraviolet) 46 | - [Scramjet](https://github.com/mercuryworkshop/scramjet) 47 | 48 | --- 49 | 50 | ## Contributors 51 | 52 | - [MotorTruck1221](https://motortruck1221.com) - Maintainer 53 | - [Rifting](https://github.com/rifting) - Maintainer 54 | - [caracal-js](https://github.com/caracal-js) - Original Creator 55 | 56 | --- 57 | 58 | ## Tech Stack 59 | 60 | - [Astro](https://astro.build) 61 | - [Fastify](https://fastify.dev) 62 | - [Ultraviolet](https://github.com/titaniumnetwork-dev/ultraviolet) 63 | - [Epoxy](https://github.com/mercuryworkshop/epoxy-tls) 64 | - [Libcurl.js](https://github.com/ading2210/libcurl.js) 65 | - [Hono](https://github.com/honojs) as a Deno native alternative to fastify. Run with command: `deno task start --server standalone` 66 | - [Deno 2.0](https://github.com/denoland/deno) 67 | - HTML, CSS, and JavaScript (DUH) 68 | 69 | --- 70 | 71 | ## Roadmap 72 | 73 | - [x] - [Implement Scramjet](https://github.com/mercuryworkshop/scramjet) 74 | - [ ] - Remove dependency on Fastify & switch completely to Hono 75 | - [ ] - General codebase cleanup & remove all of the functions exisiting on `window` 76 | - [ ] - Games page needs to be reworked due to more games 77 | - [ ] - [i18n](https://github.com/alexandre-fernandez/astro-i18n) 78 | - [ ] - More themes 79 | - [ ] - Detatch from the [Original repo](https://github.com/caracal-js/incognito) 80 | 81 | --- 82 | 83 | ## Branches 84 | 85 | - main - Latest stable branch & production ready 86 | - dev - all major changes are made here before being merged into main 87 | 88 | --- 89 | 90 | ## Deployment 91 | 92 | - Incognito is the easiest proxy to deploy! 93 | - Currently, there are 3 ways to deploy Incognito 94 | - [Pre-built binaries](#Pre-built-binaries) 95 | - [Docker]() 96 | - [Building and running yourself]() 97 | 98 | #### Pre-built Binaries 99 | 100 | - When a new version is pushed, our CI automagically builds & uploads pre-built binaries to the release. 101 | - This enables you to: 102 | - A. Not install any extra runtime or install any new packages 103 | - B. Updates are easy and quick. Just download the new binary! 104 | 105 | ###### Usage: 106 | 107 | - First grab the current binary for your system & OS [here](https://github.com/titaniumnetwork-dev/incognito/releases/latest) 108 | - Then simply run the binary! 109 | - For example using linux: 110 | ```bash 111 | ./incognito-linux --server full 112 | ``` 113 | 114 | > [!NOTE] 115 | > - You MAY have to make the binary executable on certain systems. 116 | > - For example in Linux: 117 | > ```bash 118 | > chmod +x ./incognito-linux 119 | > ``` 120 | > 121 | > - To see all of the CLI options & usage see: [cli](#cli) 122 | 123 | #### Docker 124 | 125 | - Docker is the second easiest way to run Incognito. 126 | - Currently, Docker images are built by [@motortruck1221](https://github.com/motortruck1221) manually per version update. 127 | - They can be located here: [https://hub.docker.com/r/motortruck1221/incognito](https://hub.docker.com/r/motortruck1221/incognito) 128 | - This enables: 129 | - Automatic updates 130 | - Easier deployment then building yourself. 131 | - No dependencies besides docker w/compose 132 | 133 | ###### Usage: 134 | 135 | - The Docker builds currently have 2 tags: 136 | - Latest 137 | - Or a pinned version of the build (eg: 1.1.7) 138 | - Currently, only Docker compose is supported. Docker without compose *can be used* but it's highly discouraged. 139 | 140 | - Prerequisites: 141 | - Docker w/compose 142 | 143 | - First, create a `config.toml` file and copy the [example config from the repo](https://github.com/titaniumnetwork-dev/incognito/blob/main/config.example.toml) 144 | - Then, copy and paste the contents of the [docker-compose.yml file](https://github.com/titaniumnetwork-dev/incognito/blob/main/docker-compose.yml) into your own docker-compose.yml file 145 | - After that, edit the `docker-compose.yml` file to your liking. 146 | - And finally: 147 | ```bash 148 | docker compose up 149 | ``` 150 | - That should fire Incognito up and you should be good! 151 | 152 | > [!NOTE] 153 | > - To see all of the CLI options & usage see: [cli](#cli) 154 | > 155 | > - To see all of the `config.toml` options see: [config](#config) 156 | 157 | #### Building & Running yourself 158 | 159 | - This is the last way to run Incognito 160 | 161 | ###### Usage: 162 | 163 | - Prerequisites: 164 | - Git 165 | - Deno 2.1.4 or newer 166 | - Node.js & NPM 167 | 168 | 1. Clone the repo 169 | ```bash 170 | git clone https://github.com/titaniumnetwork-dev/incognito.git 171 | ``` 172 | 173 | 2. Install all of the dependencies: 174 | ```bash 175 | deno install --allow-scripts # This flag is here due to sharp, bufferutil and some others 176 | ``` 177 | 178 | 3. Create a `config.toml` file 179 | ```bash 180 | cp config.example.toml config.toml 181 | ``` 182 | 183 | 4. Modify the `config.toml` file to your liking. See: [config](#config) 184 | 185 | 5. Build the frontend 186 | ```bash 187 | deno task build 188 | ``` 189 | 190 | 6. Start the server 191 | ```bash 192 | deno task start --server full 193 | ``` 194 | 195 | > [!NOTE] 196 | > - To see all of the CLI options & usage see: [cli](#cli) 197 | 198 | --- 199 | 200 | ## CLI 201 | 202 | - Incognito has a built in CLI for starting and running 203 | - Currently, there are only 4 flags. And 1 extra command 204 | 205 | - `--help` - This triggers the help prompt even when other flags are passed. 206 | - `--server [full|standalone]` - Choose between the full or standalone server. This option is ***required*** otherwise, it will just show the help prompt. 207 | - Full - Uses Fastify has a built in Wisp server (via [wisp server node](https://github.com/mercuryworkshop/wisp-server-node)) *(recommended for self hosters)* 208 | - Standalone - Uses Hono as the server with no built in Wisp server. *(recommended for a huge production instance with an external wisp server)* 209 | - These are the only two options. Anything else passed here WILL throw an error. 210 | - `--config ` - Use a config located somewhere else. 211 | - Useful when using the [Pre-built binaries](#pre-built-binaries) 212 | - The path can either be absolute or relative. 213 | - `--seo` - Currently the default is to only use the build with no seo enabled. This flag enables the seo build. 214 | - Domain must be set in the [config](#config) 215 | - Overrides the enabled option in the [config](#config) 216 | 217 | - `upgrade` - Allows for the ability to self-update the binary. Run this to self upgrade 218 | - `--check` check for a new version. Tells you if one is available. 219 | 220 | --- 221 | 222 | ## Config 223 | 224 | - Incognito currently uses a config file in the toml format. 225 | - An example of this file can be found [here](https://github.com/titaniumnetwork-dev/incognito/blob/main/config.example.toml) 226 | 227 | ##### Build Opts 228 | | Type | Default | Description | Can be overwritten by ENV var | 229 | |------|---------|------------------------------------|-------------------------------| 230 | | games | `true` | Disables or enables the games page | - [ ] - No | 231 | 232 | ##### Server 233 | | Type | Default | Description | Can be overwritten by ENV var | 234 | |------|---------|-----------------------------------------------------------------------------------------------------------------|------------------------------| 235 | | port | `8000` | Change the default port. *Note: the environment var `PORT` takes precedence* | - [x] - Yes | 236 | | wisp | `true` | Disable or enables the in built wisp server. *Note: when using the Hono server there is no built-in wisp server | - [ ] - No | 237 | 238 | ##### SEO 239 | | Type | Default | Description | Can be overwritten by ENV var | 240 | ---------|-------------------------|--------------------------------------------------------------------------|-------------------------------| 241 | | SEO | `false` | Change whether or not to enabled SEO | - [x] - Yes - `SEO` (as well via a CLI flag `--seo` | 242 | | DOMAIN | `http://localhost:8000` | When the `both` option is enable, only show the SEO stuff on this domain | - [x] - Yes - `DOMAIN` | 243 | -------------------------------------------------------------------------------- /astro.config.ts: -------------------------------------------------------------------------------- 1 | import sitemap from '@astrojs/sitemap'; 2 | import tailwind from '@astrojs/tailwind'; 3 | import { baremuxPath } from '@mercuryworkshop/bare-mux/node'; 4 | //@ts-expect-error No types 5 | import { epoxyPath } from '@mercuryworkshop/epoxy-transport'; 6 | import { libcurlPath } from '@mercuryworkshop/libcurl-transport'; 7 | import playformCompress from '@playform/compress'; 8 | import { uvPath } from '@titaniumnetwork-dev/ultraviolet'; 9 | import icon from 'astro-icon'; 10 | import robotsTxt from 'astro-robots-txt'; 11 | import { defineConfig, envField, passthroughImageService } from 'astro/config'; 12 | import { viteStaticCopy } from 'vite-plugin-static-copy'; 13 | //we need the buildOpts from here : D 14 | import { config } from './server/config/config.ts'; 15 | const scramjetPath = `${import.meta.dirname}/vendor/scramjet/dist/` 16 | const parsedDoc = await config(`${Deno.cwd()}/config.toml`); 17 | // https://astro.build/config 18 | export default defineConfig({ 19 | site: Deno.env.get('SITE') || 'https://localhost:8080', 20 | integrations: [ 21 | tailwind(), 22 | robotsTxt(), 23 | sitemap(), 24 | icon(), 25 | playformCompress({ 26 | CSS: false, 27 | HTML: true, 28 | Image: true, 29 | JavaScript: true, 30 | SVG: true, 31 | }), 32 | ], 33 | output: 'static', 34 | image: { 35 | service: passthroughImageService() 36 | }, 37 | env: { 38 | schema: { 39 | GAMES_LINK: envField.boolean({ 40 | context: 'client', 41 | access: 'public', 42 | default: parsedDoc.buildOpts.games, 43 | }), 44 | SEO: envField.boolean({ 45 | context: 'client', 46 | access: 'public', 47 | default: Boolean(Deno.env.get('SEO')) 48 | }) 49 | }, 50 | }, 51 | vite: { 52 | plugins: [ 53 | viteStaticCopy({ 54 | targets: [ 55 | { 56 | src: `${uvPath}/**/*`.replace(/\\/g, '/'), 57 | dest: 'uv', 58 | overwrite: false, 59 | }, 60 | { 61 | src: `${epoxyPath}/**/*`.replace(/\\/g, '/'), 62 | dest: 'epoxy', 63 | overwrite: false, 64 | }, 65 | { 66 | src: `${libcurlPath}/**/*`.replace(/\\/g, '/'), 67 | dest: 'libcurl', 68 | overwrite: false, 69 | }, 70 | { 71 | src: `${baremuxPath}/**/*`.replace(/\\/g, '/'), 72 | dest: 'baremux', 73 | overwrite: false, 74 | }, 75 | { 76 | src: `${scramjetPath}/**/*`.replace(/\\/g, '/'), 77 | dest: 'scram', 78 | overwrite: false 79 | } 80 | ], 81 | }) as any, 82 | ], 83 | server: { 84 | proxy: { 85 | '/wisp/': { 86 | target: 'wss://ruby.rubynetwork.co/wisp/', 87 | changeOrigin: true, 88 | ws: true, 89 | rewrite: (path: any) => path.replace(/^\/wisp\//, ''), 90 | }, 91 | '/gms/': { 92 | target: 'https://rawcdn.githack.com/ruby-network/ruby-assets/main/', 93 | changeOrigin: true, 94 | ws: true, 95 | secure: false, 96 | rewrite: (path: any) => path.replace(/^\/gms\//, ''), 97 | }, 98 | }, 99 | }, 100 | }, 101 | }); 102 | -------------------------------------------------------------------------------- /config.example.toml: -------------------------------------------------------------------------------- 1 | [buildOpts] 2 | games = true 3 | 4 | [server] 5 | wisp = true 6 | port = 8000 7 | 8 | [seo] 9 | enabled = false 10 | both = false 11 | domain = "http://localhost:8000" 12 | -------------------------------------------------------------------------------- /deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://deno.land/x/deno@v2.1.4/cli/schemas/config-file.v1.json", 3 | "nodeModulesDir": "auto", 4 | "tasks": { 5 | "dev:server": "deno run -A --watch server/server.ts", 6 | "dev:client": "astro dev", 7 | "dev": "deno task dev:server & deno task dev:client", 8 | "build": "SEO=true astro build --outDir dist/seo/ && SEO=false astro build --outDir dist/noseo/", 9 | "exe:gen": "deno compile --target $ARC --include public --include dist --include server --include vendor --include deno.jsonc --include config.toml --include package.json --node-modules-dir --output incognito-$ARCN -A server/cli.ts", 10 | "exe:windows": "ARCN=windows ARC=x86_64-pc-windows-msvc deno task exe:gen", 11 | "exe:macOS": "ARCN=MacOS ARC=x86_64-apple-darwin deno task exe:gen", 12 | "exe:macOSarm": "ARCN=MacOS-arm ARC=aarch64-apple-darwin deno task exe:gen", 13 | "exe:linux": "ARCN=linux ARC=x86_64-unknown-linux-gnu deno task exe:gen", 14 | "exe:linuxArm": "ARCN=linux-arm ARC=aarch64-unknown-linux-gnu deno task exe:gen", 15 | "exe:all": "deno task exe:windows & deno task exe:macOS & deno task exe:macOSarm & deno task exe:linux & deno task exe:linuxArm", 16 | "exe": "deno compile --include public --include dist --include server --include vendor --include deno.jsonc --include config.toml --include package.json --node-modules-dir --output incognito -A server/cli.ts", 17 | "start": "deno run --allow-net --allow-read --allow-env --allow-ffi --allow-sys server/cli.ts", 18 | "bstart": "deno task build && deno task start", 19 | "bstart:seo": "deno task build:seo && deno task start", 20 | "check:frontend": "astro check", 21 | "check:server": "deno check server/**/*.ts", 22 | "check": "deno task check:frontend && deno task check:server", 23 | "changeset:version": "changeset version", 24 | "changeset:publish": "changeset publish" 25 | }, 26 | "fmt": { 27 | "indentWidth": 4, 28 | "lineWidth": 100, 29 | "semiColons": true, 30 | "proseWrap": "preserve", 31 | "singleQuote": true 32 | }, 33 | "imports": { 34 | // Astro deps 35 | "@astrojs/check": "npm:@astrojs/check@^0.9.4", 36 | "@astrojs/sitemap": "npm:@astrojs/sitemap@^3.2.1", 37 | "@astrojs/tailwind": "npm:@astrojs/tailwind@^5.1.3", 38 | "astro": "npm:astro@^5.2.3", 39 | "astro-seo": "npm:astro-seo@^0.8.4", 40 | "astro-icon": "npm:astro-icon@^1.1.4", 41 | "astro-robots-txt": "npm:astro-robots-txt@^1.0.0", 42 | "tailwindcss": "npm:tailwindcss@^3.4.16", 43 | "vite-plugin-static-copy": "npm:vite-plugin-static-copy@^2.2.0", 44 | "@iconify-json/fa-solid": "npm:@iconify-json/fa-solid@^1.2.0", 45 | "@iconify-json/fa6-solid": "npm:@iconify-json/fa6-solid@^1.2.2", 46 | "@iconify-json/mdi": "npm:@iconify-json/mdi@1.2.1", 47 | "@playform/compress": "npm:@playform/compress@^0.1.6", 48 | "sharp": "npm:sharp@^0.33.5", 49 | //Server stuff 50 | "@fastify/compress": "npm:@fastify/compress@^8.0.1", 51 | "@fastify/cookie": "npm:@fastify/cookie@^11.0.1", 52 | "@fastify/http-proxy": "npm:@fastify/http-proxy@^10.0.1", 53 | "@fastify/middie": "npm:@fastify/middie@^9.0.2", 54 | "@fastify/static": "npm:@fastify/static@^8.0.2", 55 | "fastify": "npm:fastify@^5.1.0", 56 | //UV, bare-mux, RH, corlink & wisp 57 | "@mercuryworkshop/bare-mux": "npm:@mercuryworkshop/bare-mux@^2.1.7", 58 | "@mercuryworkshop/epoxy-transport": "npm:@mercuryworkshop/epoxy-transport@^2.1.27", 59 | "@mercuryworkshop/libcurl-transport": "npm:@mercuryworkshop/libcurl-transport@^1.3.14", 60 | "@titaniumnetwork-dev/ultraviolet": "npm:@titaniumnetwork-dev/ultraviolet@^3.2.10", 61 | "wisp-server-node": "npm:wisp-server-node@^1.1.7", 62 | //random other deps 63 | "chalk": "npm:chalk@^5.3.0", 64 | "@changesets/cli": "npm:@changesets/cli@^2.27.10", 65 | "smol-toml": "npm:smol-toml@^1.3.1", 66 | "@types/node": "npm:@types/node@^22.9.0", 67 | "gradient-string": "npm:gradient-string@^3.0.0", 68 | "@types/deno": "npm:@types/deno@^2.0.0", 69 | "typescript": "npm:typescript@^5.7.2" //I need this for astro typechecking for some damn reason 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /docker-builds.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | # This file is for building the docker images and pushing them to the registry. You can safely ignore this 4 | 5 | services: 6 | incognito: 7 | image: motortruck1221/incognito:latest 8 | build: 9 | context: . 10 | dockerfile: Dockerfile 11 | container_name: incognito 12 | restart: unless-stopped 13 | incognito-versioned: 14 | image: motortruck1221/incognito:1.1.7 15 | build: 16 | context: . 17 | dockerfile: Dockerfile 18 | container_name: incognito 19 | restart: unless-stopped 20 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | incognito: 5 | image: motortruck1221/incognito:latest 6 | container_name: incognito 7 | restart: unless-stopped 8 | ports: 9 | # The ports work like this: Host:Container (DO NOT MODIFY THE CONTAINER PORT) 10 | - '8081:8000' 11 | volumes: 12 | - ./config.toml:/app/config.toml # This can be removed if you need the image to be stateless 13 | #environment: 14 | #- DOMAIN=https://incog.works # If you don't have a config.toml file and you want to still use the SEO build, set this variable 15 | command: 16 | - "full" # Full is the server type. Change to "standalone" if you want something simpler and lighter. 17 | #- "--seo" # Enable this line if you wish to use the SEO build. (NOTE: the domain env var MUST be set OR SEO must be enabled in your config.toml) 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "incognito", 3 | "version": "1.1.8", 4 | "private": true, 5 | "type": "module", 6 | "dependencies": { 7 | "@iconify-json/fa-solid": "^1.1.8", 8 | "@iconify-json/fa6-solid": "^1.1.21", 9 | "@iconify-json/mdi": "^1.2.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | -------------------------------------------------------------------------------- /public/search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * @param {string} input 4 | * @param {string} template Template for a search query. 5 | * @returns {string} Fully qualified URL 6 | */ 7 | function search(input, template) { 8 | try { 9 | // input is a valid URL: 10 | // eg: https://example.com, https://example.com/test?q=param 11 | return new URL(input).toString(); 12 | } catch (err) { 13 | // input was not a valid URL 14 | } 15 | 16 | try { 17 | // input is a valid URL when http:// is added to the start: 18 | // eg: example.com, https://example.com/test?q=param 19 | const url = new URL(`http://${input}`); 20 | // only if the hostname has a TLD/subdomain 21 | if (url.hostname.includes('.')) return url.toString(); 22 | } catch (err) { 23 | // input was not valid URL 24 | } 25 | 26 | // input may have been a valid URL, however the hostname was invalid 27 | 28 | // Attempts to convert the input to a fully qualified URL have failed 29 | // Treat the input as a search query 30 | return template.replace('%s', encodeURIComponent(input)); 31 | } 32 | -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- 1 | importScripts( 2 | '/uv/uv.bundle.js', 3 | '/uv/uv.config.js', 4 | '/scram/scramjet.wasm.js', 5 | '/scram/scramjet.shared.js', 6 | '/scram/scramjet.worker.js' 7 | ); 8 | importScripts(__uv$config.sw || '/uv/uv.sw.js'); 9 | const uv = new UVServiceWorker(); 10 | const sj = new ScramjetServiceWorker(); 11 | (async function () { 12 | await sj.loadConfig(); 13 | })(); 14 | self.addEventListener('fetch', function (event) { 15 | if (event.request.url.startsWith(location.origin + __uv$config.prefix)) { 16 | event.respondWith( 17 | (async function () { 18 | return await uv.fetch(event); 19 | })(), 20 | ); 21 | } 22 | else if (sj.route(event)) { 23 | event.respondWith( 24 | (async function() { 25 | return await sj.fetch(event); 26 | })() 27 | ); 28 | } 29 | else { 30 | event.respondWith( 31 | (async function () { 32 | return await fetch(event.request); 33 | })() 34 | ); 35 | } 36 | }); 37 | 38 | assets = [ 39 | '/', 40 | '/favicon.ico', 41 | '/manifest.json', 42 | '/transports/bareTransport.js', 43 | 'sw.js', 44 | 'uv.config.js', 45 | 'logo.svg', 46 | ]; 47 | -------------------------------------------------------------------------------- /public/uv/uv.config.js: -------------------------------------------------------------------------------- 1 | self.__uv$config = { 2 | prefix: '/~/uv/', 3 | encodeUrl: function encode(str) { 4 | if (!str) return str; 5 | return encodeURIComponent( 6 | str 7 | .toString() 8 | .split('') 9 | .map((char, ind) => (ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 3) : char)) 10 | .join(''), 11 | ); 12 | }, 13 | decodeUrl: function decode(str) { 14 | if (!str) return str; 15 | let [input, ...search] = str.split('?'); 16 | 17 | return ( 18 | decodeURIComponent(input) 19 | .split('') 20 | .map((char, ind) => (ind % 2 ? String.fromCharCode(char.charCodeAt(0) ^ 3) : char)) 21 | .join('') + (search.length ? '?' + search.join('?') : '') 22 | ); 23 | }, 24 | handler: '/uv/uv.handler.js', 25 | client: '/uv/uv.client.js', 26 | bundle: '/uv/uv.bundle.js', 27 | config: '/uv/uv.config.js', 28 | sw: '/uv/uv.sw.js', 29 | }; 30 | -------------------------------------------------------------------------------- /server/cli.ts: -------------------------------------------------------------------------------- 1 | // A basic CLI to provide a custom config.toml & to choose between the two servers. 2 | // Hono or Fastify 3 | import chalk from 'chalk'; 4 | import gradient from 'npm:gradient-string'; 5 | import { message, messageColors } from "./message.ts"; 6 | import { parseArgs } from "jsr:@std/cli"; 7 | import { startServer as HonoServer } from './standalone/standalone.ts'; 8 | import { startServer as FastifyServer } from './full/server.ts'; 9 | import { fromFileUrl } from 'jsr:@std/path'; 10 | import ora, { Ora } from 'npm:ora'; 11 | 12 | import packageJSON from "../package.json" with { type: 'json' }; 13 | const { version } = packageJSON; 14 | 15 | interface CLIArgs { 16 | _: any[]; 17 | help: boolean; 18 | }; 19 | 20 | interface StartArgs extends CLIArgs { 21 | server: "full" | "standalone"; 22 | config?: string; 23 | seo?: boolean; 24 | }; 25 | 26 | interface UpgradeArgs extends CLIArgs { 27 | check: boolean; 28 | }; 29 | 30 | interface DescriptionType { 31 | name: string; 32 | description: string; 33 | default?: string; 34 | }; 35 | 36 | class CLI { 37 | #args: T; 38 | constructor(config: { booleans: string[], strings: string[] }) { 39 | this.#args = parseArgs(Deno.args, { 40 | boolean: config.booleans, 41 | string: config.strings 42 | }) as T; 43 | } 44 | 45 | #white(text: string) { 46 | return chalk.whiteBright.bold(text); 47 | } 48 | 49 | getArgs(): T { 50 | return this.#args; 51 | } 52 | 53 | help(descriptions?: DescriptionType[]) { 54 | console.log(chalk.whiteBright.bold('--help - Trigger this menu')); 55 | descriptions?.map((val) => console.log(`${this.#white('--')}${this.#white(val.name)} ${this.#white('-')} ${this.#white(val.description)} ${val.default ? this.#white(`(default: ${val.default})`) : ''}`)) 56 | Deno.exit(); 57 | } 58 | 59 | async start(fn: () => unknown) { 60 | await fn(); 61 | } 62 | } 63 | 64 | console.log(gradient(Object.values(messageColors)).multiline(message)); 65 | console.log(chalk.hex('#34b874')(`Welcome to the Incognito CLI!\nVersion: ${version}\n`)); 66 | 67 | const cli = new CLI({ 68 | booleans: [], strings: [] 69 | }); 70 | 71 | const args = cli.getArgs(); 72 | 73 | if (args._.length !== 0 && args._[0] !== "upgrade") { 74 | console.log(chalk.redBright.bold(`The command ${args._[0]} is not available.\nPlease use a valid command`)) 75 | Deno.exit(); 76 | } 77 | 78 | if (args._[0] === "upgrade") { 79 | const upgradeCLI = new CLI({ booleans: ["check"], strings: [] }); 80 | const uArgs = upgradeCLI.getArgs(); 81 | 82 | const checkForVersion = async (checkFlag: boolean) => { 83 | const res = await fetch("https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/refs/heads/main/package.json"); 84 | const resp = await res.json(); 85 | if (resp.version !== version) { 86 | checkFlag ? console.log(chalk.greenBright.bold(`A new version is available! Re-run this command without --check to upgrade!`)): ''; 87 | return resp.version; 88 | } 89 | else { 90 | console.log(chalk.redBright.bold('A new version is not available at this moment, check back later')); 91 | Deno.exit(); 92 | } 93 | } 94 | 95 | if (uArgs.help) { 96 | upgradeCLI.help([ 97 | { name: 'check', description: 'Check for a new version' } 98 | ]); 99 | } 100 | 101 | if (uArgs.check) { 102 | await checkForVersion(true); 103 | Deno.exit(); 104 | } 105 | 106 | const download = async (version: string, spinner: Ora) => { 107 | const os = Deno.build.os; 108 | const arch = Deno.build.arch; 109 | if (os === "aix" || os === "netbsd" || os === "android" || os === "freebsd" || os === "solaris" || os === "illumos") { 110 | spinner.fail('Your OS is NOT supported! Exiting...'); 111 | Deno.exit(); 112 | } 113 | if (os === "darwin") { 114 | const res = await fetch(`https://github.com/titaniumnetwork-dev/incognito/releases/download/v${version}/incognito-MacOS${arch === "aarch64" ? '-arm': ''}`); 115 | const resp = await res.bytes(); 116 | await Deno.writeFile(`incognito-MacOS${arch === "aarch64" ? '-arm': ''}`, resp, { mode: 0o775 }); 117 | } 118 | else { 119 | const res = await fetch(`https://github.com/titaniumnetwork-dev/incognito/releases/download/v${version}/incognito-${os}${arch === "aarch64" ? '-arm': ''}`); 120 | const resp = await res.bytes(); 121 | await Deno.writeFile(`incognito-${os}${arch === "aarch64" ? '-arm': ''}`, resp, { mode: 0o775 }); 122 | } 123 | }; 124 | 125 | await upgradeCLI.start(async () => { 126 | const version = await checkForVersion(false); 127 | const spinner = ora('Downloading...').start(); 128 | await download(version, spinner); 129 | spinner.succeed('Upgraded!'); 130 | }); 131 | 132 | Deno.exit(); 133 | } 134 | 135 | const startCLI = new CLI({ 136 | booleans: ["help", "seo"], strings: ["config"] 137 | }); 138 | 139 | const startArgs = startCLI.getArgs(); 140 | 141 | if (startArgs.help || !startArgs.server) { 142 | startCLI.help([ 143 | { name: "server [full|standalone]", description: "Select the server type", default: "full" }, 144 | { name: "config [path]", description: "The path to a custom config", default: 'config.toml' }, 145 | { name: "seo", description: "Enable SEO website regardless of the config file", default: 'false' } 146 | ]); 147 | } 148 | 149 | if (startArgs.config) { 150 | const path = await Deno.realPath(startArgs.config); 151 | startArgs.config = path; 152 | } 153 | 154 | if (startArgs.server !== "full" && startArgs.server !== "standalone") { 155 | console.log(chalk.redBright.bold(`Error with option --server!\nThe value: "${startArgs.server}" is not valid!\nThe only available options are: "full" or "standalone"`)); 156 | } 157 | 158 | startCLI.start(async () => { 159 | startArgs.server === "standalone" 160 | ? await HonoServer(startArgs.config ?? fromFileUrl(new URL('../config.toml', import.meta.url)), startArgs.seo) 161 | : await FastifyServer(startArgs.config ?? fromFileUrl(new URL('../config.toml', import.meta.url)), startArgs.seo) 162 | }); 163 | -------------------------------------------------------------------------------- /server/config/config.ts: -------------------------------------------------------------------------------- 1 | import { parse } from "smol-toml"; 2 | 3 | interface Data { 4 | buildOpts: { 5 | games: boolean; 6 | }; 7 | server: { 8 | wisp: boolean; 9 | port: number; 10 | }; 11 | seo: { 12 | enabled: boolean; 13 | domain: string; 14 | }; 15 | } 16 | 17 | const config = async (configFile: string): Promise => { 18 | const doc = await Deno.readTextFile(configFile); 19 | const parsedDoc = parse(doc) as unknown as Data; 20 | 21 | if (typeof parsedDoc.buildOpts !== "object") { 22 | throw new Error(`Invalid structure: "buildOpts" should be an object`); 23 | } 24 | if (typeof parsedDoc.server !== "object") { 25 | throw new Error(`Invalid structure: "server" should be an object`); 26 | } 27 | if (typeof parsedDoc.buildOpts.games !== "boolean") { 28 | throw new Error(`Invalid type for "buildOpts.games"! It should be a boolean (true/false)`); 29 | } 30 | if (typeof parsedDoc.server.wisp !== "boolean") { 31 | throw new Error(`Invalid type for "server.wisp"! It should be a boolean (true/false)`); 32 | } 33 | if (typeof parsedDoc.server.port !== "number") { 34 | throw new Error(`Invalid type for "server.port"! It should be a number`); 35 | } 36 | if (typeof parsedDoc.seo.enabled !== "boolean") { 37 | throw new Error(`Invalid type for "seo.enabled"! It should be an boolean (true/false)`); 38 | } 39 | if (typeof parsedDoc.seo.domain !== "string") { 40 | throw new Error(`Invalid type for "seo.domain"! It should be an string`); 41 | } else { 42 | try { 43 | new URL(parsedDoc.seo.domain); 44 | } catch (e: any) { 45 | throw new Error(e); 46 | } 47 | } 48 | return parsedDoc; 49 | } 50 | 51 | export { type Data as TOMLConfig, config }; 52 | -------------------------------------------------------------------------------- /server/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /server/full/server.ts: -------------------------------------------------------------------------------- 1 | import fastifyCompress from '@fastify/compress'; 2 | import fastifyCookie from '@fastify/cookie'; 3 | import fastifyHttpProxy from '@fastify/http-proxy'; 4 | import fastifyMiddie from '@fastify/middie'; 5 | import fastifyStatic from '@fastify/static'; 6 | import Fastify from 'fastify'; 7 | import { sFactory } from './serverFactory.ts'; 8 | import { listeningMessage } from "../message.ts"; 9 | import { config } from "../config/config.ts"; 10 | import { fromFileUrl } from "jsr:@std/path"; 11 | 12 | const startServer = async (configPath: string, seo?: boolean) => { 13 | const parsedDoc = await config(configPath); 14 | const serverFactory = await sFactory(configPath); 15 | const distPath = fromFileUrl(new URL('../../dist', import.meta.url)); 16 | const app = Fastify({ logger: false, serverFactory: serverFactory }); 17 | 18 | await app.register(fastifyCookie, { 19 | secret: Deno.env.get('COOKIE_SECRET') || 'yes', 20 | parseOptions: {} 21 | }); 22 | await app.register(fastifyCompress, { 23 | encodings: ['br', 'gzip', 'deflate'] 24 | }); 25 | 26 | await app.register(fastifyStatic, { 27 | root: `${distPath}/noseo`, 28 | etag: false, 29 | lastModified: false 30 | }); 31 | if (seo || parsedDoc.seo.enabled) { 32 | await app.register(fastifyStatic, { 33 | root: `${distPath}/seo`, 34 | constraints: { host: new URL(Deno.env.get('DOMAIN') || parsedDoc.seo.domain).host }, 35 | etag: false, 36 | lastModified: false, 37 | decorateReply: false 38 | }) 39 | } 40 | 41 | await app.register(fastifyMiddie); 42 | await app.register(fastifyHttpProxy, { 43 | upstream: 'https://rawcdn.githack.com/ruby-network/ruby-assets/main/', 44 | prefix: '/gms/', 45 | http2: false 46 | }); 47 | 48 | const port = parseInt(Deno.env.get('PORT') as string) || parsedDoc.server.port || 8000; 49 | 50 | app.listen({ port: port, host: '0.0.0.0' }).then(() => { 51 | listeningMessage(port, "fastify"); 52 | }); 53 | } 54 | 55 | export { startServer } 56 | -------------------------------------------------------------------------------- /server/full/serverFactory.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from 'node:http'; 2 | import { Socket } from 'node:net'; 3 | import { 4 | type FastifyServerFactory, 5 | FastifyServerFactoryHandler, 6 | RawServerDefault, 7 | } from 'npm:fastify'; 8 | import wisp from 'npm:wisp-server-node'; 9 | import { config } from '../config/config.ts'; 10 | 11 | const sFactory = async (configPath: string): Promise => { 12 | const parsedDoc = await config(configPath); 13 | const serverFactory: FastifyServerFactory = (handler: FastifyServerFactoryHandler): RawServerDefault => { 14 | return createServer() 15 | .on('request', (req, res) => { 16 | handler(req, res); 17 | }) 18 | .on('upgrade', (req, socket, head) => { 19 | if (req.url?.endsWith('/wisp/') && parsedDoc.server.wisp === true) { 20 | wisp.routeRequest(req, socket as Socket, head); 21 | } 22 | }); 23 | }; 24 | return serverFactory; 25 | } 26 | 27 | export { sFactory }; 28 | -------------------------------------------------------------------------------- /server/message.ts: -------------------------------------------------------------------------------- 1 | import gradient from 'npm:gradient-string'; 2 | import chalk from 'chalk'; 3 | const message = ` 4 | ___ _ _ 5 | |_ _|_ __ ___ ___ _ __ (_) |_ ___ 6 | | || '_ \\ / __/ _ \\| '_ \\| | __/ _ \\ 7 | | || | | | (_| (_) | | | | | || (_) | 8 | |___|_| |_|\\___\\___/|_| |_|_|\\__\\___/ 9 | `; 10 | const messageColors = { 11 | green: '#34b874', 12 | white: '#ffffff', 13 | blue: '#161923', 14 | }; 15 | 16 | const listeningMessage = (port: number, server: 'fastify' | 'hono') => { 17 | console.log( 18 | `${chalk.hex('#34b874')('Server listening on')} ${ 19 | chalk.white.bold('http://localhost:' + port) 20 | }`, 21 | ); 22 | console.log( 23 | chalk.white.bold( 24 | `Server also listening on ${chalk.hex('#34b874').bold('http://0.0.0.0:' + port)}`, 25 | ), 26 | ); 27 | console.log( 28 | chalk.hex('#34b874').bold( 29 | `The server in use ${ 30 | server === 'fastify' 31 | ? chalk.bold.whiteBright('Fastify (Not Deno native, includes wisp server)') 32 | : chalk.bold.whiteBright('Hono (no wisp server, deno-native)') 33 | }`, 34 | ), 35 | ); 36 | }; 37 | 38 | export { listeningMessage, message, messageColors }; 39 | -------------------------------------------------------------------------------- /server/standalone/standalone.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from 'jsr:@hono/hono'; 2 | import { serveStatic } from 'jsr:@hono/hono/deno'; 3 | import { compress } from 'jsr:@hono/hono/compress'; 4 | import { listeningMessage } from '../message.ts'; 5 | import { config } from '../config/config.ts'; 6 | import { fromFileUrl } from 'jsr:@std/path'; 7 | 8 | const startServer = async (configPath: string, seo?: boolean) => { 9 | const parsedDoc = await config(configPath); 10 | const app = new Hono(); 11 | const distPath = fromFileUrl(new URL('../../dist', import.meta.url)); 12 | 13 | app.use(compress({ 14 | encoding: 'gzip', 15 | })); 16 | 17 | app.use('/*', (ctx, next) => { 18 | if (new URL(ctx.req.url).host === new URL(Deno.env.get('DOMAIN') || parsedDoc.seo.domain).host && seo || parsedDoc.seo.enabled) { 19 | return serveStatic({ root: `${distPath}/seo` })(ctx, next); 20 | } 21 | else { 22 | return serveStatic({ root: `${distPath}/noseo` })(ctx, next); 23 | } 24 | }); 25 | 26 | Deno.serve({ 27 | hostname: '0.0.0.0', 28 | port: parseInt(Deno.env.get('PORT') as string) || parsedDoc.server.port || 8000, 29 | onListen() { 30 | listeningMessage(parseInt(Deno.env.get('PORT') as string) || parsedDoc.server.port || 8000, 'hono'); 31 | }, 32 | }, app.fetch); 33 | } 34 | 35 | export { startServer } 36 | -------------------------------------------------------------------------------- /src/assets/apps/coolmath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/coolmath.png -------------------------------------------------------------------------------- /src/assets/apps/discord.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/discord.jpg -------------------------------------------------------------------------------- /src/assets/apps/gfnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/gfnow.png -------------------------------------------------------------------------------- /src/assets/apps/google.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/google.jpg -------------------------------------------------------------------------------- /src/assets/apps/reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/reddit.png -------------------------------------------------------------------------------- /src/assets/apps/retroarch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/retroarch.png -------------------------------------------------------------------------------- /src/assets/apps/spotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/spotify.png -------------------------------------------------------------------------------- /src/assets/apps/tt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/tt.jpg -------------------------------------------------------------------------------- /src/assets/apps/twitch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/twitch.jpg -------------------------------------------------------------------------------- /src/assets/apps/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/twitter.png -------------------------------------------------------------------------------- /src/assets/apps/y8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/y8.png -------------------------------------------------------------------------------- /src/assets/apps/yt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/apps/yt.png -------------------------------------------------------------------------------- /src/assets/games/1v1-lol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/1v1-lol.png -------------------------------------------------------------------------------- /src/assets/games/2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/2048.png -------------------------------------------------------------------------------- /src/assets/games/basketball-stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/basketball-stars.png -------------------------------------------------------------------------------- /src/assets/games/binding-of-isaac.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/binding-of-isaac.jpeg -------------------------------------------------------------------------------- /src/assets/games/bloons.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/bloons.webp -------------------------------------------------------------------------------- /src/assets/games/bloonstd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/bloonstd.jpg -------------------------------------------------------------------------------- /src/assets/games/bloonstd2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/bloonstd2.png -------------------------------------------------------------------------------- /src/assets/games/bloxors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/bloxors.png -------------------------------------------------------------------------------- /src/assets/games/cat-ninja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/cat-ninja.png -------------------------------------------------------------------------------- /src/assets/games/chromedino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/chromedino.png -------------------------------------------------------------------------------- /src/assets/games/cookieclicker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/cookieclicker.jpg -------------------------------------------------------------------------------- /src/assets/games/crossy-road.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/crossy-road.png -------------------------------------------------------------------------------- /src/assets/games/cybertanks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/cybertanks.png -------------------------------------------------------------------------------- /src/assets/games/dadish-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/dadish-2.png -------------------------------------------------------------------------------- /src/assets/games/dadish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/dadish.png -------------------------------------------------------------------------------- /src/assets/games/doodle-jump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/doodle-jump.png -------------------------------------------------------------------------------- /src/assets/games/ducklife.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/ducklife.webp -------------------------------------------------------------------------------- /src/assets/games/ducklife2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/ducklife2.jpg -------------------------------------------------------------------------------- /src/assets/games/ducklife3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/ducklife3.jpg -------------------------------------------------------------------------------- /src/assets/games/eaglerx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/eaglerx.webp -------------------------------------------------------------------------------- /src/assets/games/emulatorjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/emulatorjs.png -------------------------------------------------------------------------------- /src/assets/games/flashtetris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/flashtetris.png -------------------------------------------------------------------------------- /src/assets/games/frogger.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/frogger.jpeg -------------------------------------------------------------------------------- /src/assets/games/fruit-ninja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/fruit-ninja.png -------------------------------------------------------------------------------- /src/assets/games/fruitninja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/fruitninja.jpg -------------------------------------------------------------------------------- /src/assets/games/geometrydash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/geometrydash.png -------------------------------------------------------------------------------- /src/assets/games/gm2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/gm2.jpeg -------------------------------------------------------------------------------- /src/assets/games/gpacman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/gpacman.jpg -------------------------------------------------------------------------------- /src/assets/games/hardestgame.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/hardestgame.jpg -------------------------------------------------------------------------------- /src/assets/games/hexgl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/hexgl.jpg -------------------------------------------------------------------------------- /src/assets/games/mario.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/mario.webp -------------------------------------------------------------------------------- /src/assets/games/miniagario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/miniagario.png -------------------------------------------------------------------------------- /src/assets/games/moke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/moke.png -------------------------------------------------------------------------------- /src/assets/games/monkey-mart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/monkey-mart.png -------------------------------------------------------------------------------- /src/assets/games/pacman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/pacman.jpg -------------------------------------------------------------------------------- /src/assets/games/paper.io2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/paper.io2.webp -------------------------------------------------------------------------------- /src/assets/games/portaltheflashversion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/portaltheflashversion.jpg -------------------------------------------------------------------------------- /src/assets/games/retrobowl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/retrobowl.jpg -------------------------------------------------------------------------------- /src/assets/games/riddleschool.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/riddleschool.webp -------------------------------------------------------------------------------- /src/assets/games/riddleschool2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/riddleschool2.webp -------------------------------------------------------------------------------- /src/assets/games/riddleschool3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/riddleschool3.jpg -------------------------------------------------------------------------------- /src/assets/games/riddleschool4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/riddleschool4.jpg -------------------------------------------------------------------------------- /src/assets/games/riddleschool5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/riddleschool5.webp -------------------------------------------------------------------------------- /src/assets/games/run3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/run3.png -------------------------------------------------------------------------------- /src/assets/games/shellshock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/shellshock.png -------------------------------------------------------------------------------- /src/assets/games/slitherio.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/slitherio.webp -------------------------------------------------------------------------------- /src/assets/games/slope-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/slope-2.png -------------------------------------------------------------------------------- /src/assets/games/slope.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/slope.jpg -------------------------------------------------------------------------------- /src/assets/games/snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/snake.png -------------------------------------------------------------------------------- /src/assets/games/ssf.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/ssf.jpeg -------------------------------------------------------------------------------- /src/assets/games/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/stack.png -------------------------------------------------------------------------------- /src/assets/games/sticmanhook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/sticmanhook.jpg -------------------------------------------------------------------------------- /src/assets/games/stuntcars2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/stuntcars2.png -------------------------------------------------------------------------------- /src/assets/games/stuntcars3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/stuntcars3.jpg -------------------------------------------------------------------------------- /src/assets/games/subway-surfers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/subway-surfers.png -------------------------------------------------------------------------------- /src/assets/games/superhot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/superhot.webp -------------------------------------------------------------------------------- /src/assets/games/supermario64.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/supermario64.jpeg -------------------------------------------------------------------------------- /src/assets/games/tanktrouble.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/tanktrouble.webp -------------------------------------------------------------------------------- /src/assets/games/tanuki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/tanuki.jpg -------------------------------------------------------------------------------- /src/assets/games/templerun2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/templerun2.jpg -------------------------------------------------------------------------------- /src/assets/games/the-worlds-hardest-game-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/the-worlds-hardest-game-2.jpg -------------------------------------------------------------------------------- /src/assets/games/tunnel-rush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/tunnel-rush.jpg -------------------------------------------------------------------------------- /src/assets/games/vex3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/vex3.png -------------------------------------------------------------------------------- /src/assets/games/vex4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/vex4.png -------------------------------------------------------------------------------- /src/assets/games/vex5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/vex5.jpeg -------------------------------------------------------------------------------- /src/assets/games/vex6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/vex6.jpeg -------------------------------------------------------------------------------- /src/assets/games/vex7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/vex7.png -------------------------------------------------------------------------------- /src/assets/games/watermelon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/watermelon.png -------------------------------------------------------------------------------- /src/assets/games/webretro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/webretro.png -------------------------------------------------------------------------------- /src/assets/games/yohoho.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Incognito/b386547f3285e9b15668f3ebd5c3bbdfbb196c24/src/assets/games/yohoho.jpeg -------------------------------------------------------------------------------- /src/components/FrameManager.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components'; 3 | --- 4 | 19 | 61 | -------------------------------------------------------------------------------- /src/components/Header.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components'; 3 | const { pageName, pageLocation } = Astro.props; 4 | --- 5 | 6 |
7 |
8 | 9 | 10 | 11 |

12 | {pageName} 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /src/components/MobileNav.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components'; 3 | const { options, page } = Astro.props; 4 | import { GAMES_LINK } from 'astro:env/client'; 5 | --- 6 |
7 | 10 |
11 | 29 | 39 | -------------------------------------------------------------------------------- /src/components/NavLinks.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { GAMES_LINK } from 'astro:env/client'; 3 | import NavMenu from '@components/MobileNav.astro'; 4 | import { Icon } from 'astro-icon/components'; 5 | --- 6 |
7 | 23 | 30 | 31 |
32 | -------------------------------------------------------------------------------- /src/components/Scripts.astro: -------------------------------------------------------------------------------- 1 | 57 | -------------------------------------------------------------------------------- /src/components/Search.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Icon } from 'astro-icon/components'; 3 | import FrameManager from '@components/FrameManager.astro'; 4 | --- 5 | 6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 | 16 |
17 | 18 |
19 | 114 |
115 | -------------------------------------------------------------------------------- /src/components/settings/Loader.astro: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /src/data/games.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "img": "/src/assets/games/tanuki.jpg", 4 | "baseFile": "index.html", 5 | "name": "Tanuki Sunset", 6 | "tags": ["arcade", "platformer", "static"], 7 | "cdn": "true", 8 | "proxy": "false", 9 | "url": "" 10 | }, 11 | { 12 | "img": "/src/assets/games/tunnel-rush.jpg", 13 | "baseFile": "index.html", 14 | "name": "Tunnel Rush", 15 | "tags": ["arcade", "platformer", "static"], 16 | "cdn": "true", 17 | "proxy": "false", 18 | "url": "" 19 | }, 20 | { 21 | "img": "/src/assets/games/stuntcars2.png", 22 | "baseFile": "index.html", 23 | "name": "Madalin Stunt Cars 2", 24 | "tags": ["racing", "static"], 25 | "cdn": "true", 26 | "proxy": "false", 27 | "url": "" 28 | }, 29 | { 30 | "img": "/src/assets/games/moke.png", 31 | "baseFile": "index.html", 32 | "name": "Spank The Monkey", 33 | "tags": ["arcade", "flash", "static"], 34 | "cdn": "true", 35 | "proxy": "false", 36 | "url": "" 37 | }, 38 | { 39 | "img": "/src/assets/games/slope.jpg", 40 | "baseFile": "index.html", 41 | "name": "Slope", 42 | "tags": ["arcade", "platformer", "static"], 43 | "cdn": "true", 44 | "proxy": "false", 45 | "url": "" 46 | }, 47 | { 48 | "img": "/src/assets/games/run3.png", 49 | "baseFile": "index.html", 50 | "name": "Run 3", 51 | "tags": ["arcade", "adventure", "platformer", "static"], 52 | "cdn": "true", 53 | "proxy": "false", 54 | "url": "" 55 | }, 56 | { 57 | "img": "/src/assets/games/pacman.jpg", 58 | "baseFile": "index.html", 59 | "name": "Pacman", 60 | "tags": ["arcade", "multiplayer", "static"], 61 | "cdn": "true", 62 | "proxy": "false", 63 | "url": "" 64 | }, 65 | { 66 | "img": "/src/assets/games/hexgl.jpg", 67 | "baseFile": "index.html", 68 | "name": "HexGL", 69 | "tags": ["racing", "static"], 70 | "cdn": "true", 71 | "proxy": "false", 72 | "url": "" 73 | }, 74 | { 75 | "img": "/src/assets/games/snake.png", 76 | "baseFile": "index.html", 77 | "name": "Google Snake", 78 | "tags": ["arcade", "static"], 79 | "cdn": "true", 80 | "proxy": "false", 81 | "url": "" 82 | }, 83 | { 84 | "img": "/src/assets/games/gpacman.jpg", 85 | "baseFile": "index.html", 86 | "name": "Google Pacman", 87 | "tags": ["arcade", "static"], 88 | "cdn": "true", 89 | "proxy": "false", 90 | "url": "" 91 | }, 92 | { 93 | "img": "/src/assets/games/mario.webp", 94 | "baseFile": "index.html", 95 | "name": "Super Mario Bros.", 96 | "tags": ["arcade", "adventure", "retro", "platformer", "emulation"], 97 | "cdn": "true", 98 | "proxy": "false", 99 | "url": "" 100 | }, 101 | { 102 | "img": "/src/assets/games/ducklife.webp", 103 | "baseFile": "index.html", 104 | "name": "DuckLife", 105 | "tags": ["arcade", "static"], 106 | "cdn": "tru", 107 | "proxy": "false", 108 | "url": "" 109 | }, 110 | { 111 | "img": "/src/assets/games/ducklife2.jpg", 112 | "baseFile": "index.html", 113 | "name": "DuckLife 2", 114 | "tags": ["arcade", "static"], 115 | "cdn": "true", 116 | "proxy": "false", 117 | "url": "" 118 | }, 119 | { 120 | "img": "/src/assets/games/ducklife3.jpg", 121 | "baseFile": "index.html", 122 | "name": "DuckLife 3", 123 | "tags": ["arcade", "static"], 124 | "cdn": "true", 125 | "proxy": "false", 126 | "url": "" 127 | }, 128 | { 129 | "img": "/src/assets/games/chromedino.png", 130 | "baseFile": "index.html", 131 | "name": "Google Dino Game", 132 | "tags": ["arcade", "static"], 133 | "cdn": "true", 134 | "proxy": "false", 135 | "url": "" 136 | }, 137 | { 138 | "img": "/src/assets/games/cookieclicker.jpg", 139 | "baseFile": "index.html", 140 | "name": "Cookie Clicker", 141 | "tags": ["arcade", "static"], 142 | "cdn": "true", 143 | "proxy": "false", 144 | "url": "" 145 | }, 146 | { 147 | "img": "/src/assets/games/retrobowl.jpg", 148 | "baseFile": "index.html", 149 | "name": "Retro Bowl", 150 | "tags": ["arcade", "sports", "static"], 151 | "cdn": "true", 152 | "proxy": "false", 153 | "url": "" 154 | }, 155 | { 156 | "img": "/src/assets/games/riddleschool.webp", 157 | "baseFile": "index.html", 158 | "name": "Riddle School", 159 | "tags": ["arcade", "adventure", "strategy", "static"], 160 | "cdn": "true", 161 | "proxy": "false", 162 | "url": "" 163 | }, 164 | { 165 | "img": "/src/assets/games/riddleschool2.webp", 166 | "baseFile": "index.html", 167 | "name": "Riddle School 2", 168 | "tags": ["arcade", "adventure", "strategy", "static"], 169 | "cdn": "true", 170 | "proxy": "false", 171 | "url": "" 172 | }, 173 | { 174 | "img": "/src/assets/games/riddleschool3.jpg", 175 | "baseFile": "index.html", 176 | "name": "Riddle School 3", 177 | "tags": ["arcade", "adventure", "strategy", "static"], 178 | "cdn": "true", 179 | "proxy": "false", 180 | "url": "" 181 | }, 182 | { 183 | "img": "/src/assets/games/riddleschool4.jpg", 184 | "baseFile": "index.html", 185 | "name": "Riddle School 4", 186 | "tags": ["arcade", "adventure", "strategy", "static"], 187 | "cdn": "true", 188 | "proxy": "false", 189 | "url": "" 190 | }, 191 | { 192 | "img": "/src/assets/games/riddleschool5.webp", 193 | "baseFile": "index.html", 194 | "name": "Riddle School 5", 195 | "tags": ["arcade", "adventure", "strategy", "static"], 196 | "cdn": "true", 197 | "proxy": "false", 198 | "url": "" 199 | }, 200 | { 201 | "img": "/src/assets/games/superhot.webp", 202 | "baseFile": "index.html", 203 | "name": "SuperHot", 204 | "tags": ["arcade", "shooter", "static"], 205 | "cdn": "true", 206 | "proxy": "false", 207 | "url": "" 208 | }, 209 | { 210 | "img": "/src/assets/games/tanktrouble.webp", 211 | "baseFile": "index.html", 212 | "name": "Tank Trouble 2", 213 | "tags": ["arcade", "shooter", "strategy", "static"], 214 | "cdn": "true", 215 | "proxy": "false", 216 | "url": "" 217 | }, 218 | { 219 | "img": "/src/assets/games/templerun2.jpg", 220 | "baseFile": "index.html", 221 | "name": "Temple Run 2", 222 | "tags": ["arcade", "adventure", "static"], 223 | "cdn": "true", 224 | "proxy": "false", 225 | "url": "" 226 | }, 227 | { 228 | "img": "/src/assets/games/flashtetris.png", 229 | "baseFile": "index.html", 230 | "name": "Flash Tetris", 231 | "tags": ["arcade", "strategy", "retro", "static"], 232 | "cdn": "true", 233 | "proxy": "false", 234 | "url": "" 235 | }, 236 | { 237 | "img": "/src/assets/games/emulatorjs.png", 238 | "baseFile": "index.html", 239 | "name": "EmulatorJS", 240 | "tags": ["arcade", "strategy", "retro", "emulation"], 241 | "cdn": "true", 242 | "proxy": "false", 243 | "url": "" 244 | }, 245 | { 246 | "img": "/src/assets/games/webretro.png", 247 | "baseFile": "index.html", 248 | "name": "WebRetro", 249 | "tags": ["arcade", "strategy", "retro", "emulation"], 250 | "cdn": "true", 251 | "proxy": "false", 252 | "url": "" 253 | }, 254 | { 255 | "img": "/src/assets/games/bloons.webp", 256 | "baseFile": "index.html", 257 | "name": "Bloons", 258 | "tags": ["arcade", "strategy", "static"], 259 | "cdn": "true", 260 | "proxy": "false", 261 | "url": "" 262 | }, 263 | { 264 | "img": "/src/assets/games/bloonstd.jpg", 265 | "baseFile": "index.html", 266 | "name": "Bloons TD", 267 | "tags": ["arcade", "strategy", "static"], 268 | "cdn": "true", 269 | "proxy": "false", 270 | "url": "" 271 | }, 272 | { 273 | "img": "/src/assets/games/bloonstd2.png", 274 | "baseFile": "index.html", 275 | "name": "Bloons TD 2", 276 | "tags": ["arcade", "strategy", "static"], 277 | "cdn": "true", 278 | "proxy": "false", 279 | "url": "" 280 | }, 281 | { 282 | "img": "/src/assets/games/2048.png", 283 | "baseFile": "index.html", 284 | "name": "2048", 285 | "tags": ["arcade", "strategy", "static"], 286 | "cdn": "true", 287 | "proxy": "false", 288 | "url": "" 289 | }, 290 | { 291 | "img": "/src/assets/games/bloxors.png", 292 | "baseFile": "index.html", 293 | "name": "Bloxors", 294 | "tags": ["arcade", "strategy", "static"], 295 | "cdn": "true", 296 | "proxy": "false", 297 | "url": "" 298 | }, 299 | { 300 | "img": "/src/assets/games/geometrydash.png", 301 | "baseFile": "index.html", 302 | "name": "Geometry Dash", 303 | "tags": ["arcade", "static"], 304 | "cdn": "true", 305 | "proxy": "false", 306 | "url": "" 307 | }, 308 | { 309 | "img": "/src/assets/games/sticmanhook.jpg", 310 | "baseFile": "index.html", 311 | "name": "Stickman Hook", 312 | "tags": ["arcade", "platformer", "static"], 313 | "cdn": "true", 314 | "proxy": "false", 315 | "url": "" 316 | }, 317 | { 318 | "img": "/src/assets/games/vex3.png", 319 | "baseFile": "index.html", 320 | "name": "Vex 3", 321 | "tags": ["arcade", "platformer", "static"], 322 | "cdn": "true", 323 | "proxy": "false", 324 | "url": "" 325 | }, 326 | { 327 | "img": "/src/assets/games/vex4.png", 328 | "baseFile": "index.html", 329 | "name": "Vex 4", 330 | "tags": ["arcade", "platformer", "static"], 331 | "cdn": "true", 332 | "proxy": "false", 333 | "url": "" 334 | }, 335 | { 336 | "img": "/src/assets/games/vex5.jpeg", 337 | "baseFile": "index.html", 338 | "name": "Vex 5", 339 | "tags": ["arcade", "platformer", "static"], 340 | "cdn": "true", 341 | "proxy": "false", 342 | "url": "" 343 | }, 344 | { 345 | "img": "/src/assets/games/vex6.jpeg", 346 | "baseFile": "index.html", 347 | "name": "Vex 6", 348 | "tags": ["arcade", "platformer", "static"], 349 | "cdn": "true", 350 | "proxy": "false", 351 | "url": "" 352 | }, 353 | { 354 | "img": "/src/assets/games/vex7.png", 355 | "baseFile": "index.html", 356 | "name": "Vex 7", 357 | "tags": ["arcade", "platformer", "static"], 358 | "cdn": "true", 359 | "proxy": "false", 360 | "url": "" 361 | }, 362 | { 363 | "img": "/src/assets/games/stuntcars3.jpg", 364 | "baseFile": "index.html", 365 | "name": "Madalin Stunt Cars 3", 366 | "tags": ["arcade", "racing", "static"], 367 | "cdn": "true", 368 | "proxy": "false", 369 | "url": "" 370 | }, 371 | { 372 | "img": "/src/assets/games/hardestgame.jpg", 373 | "baseFile": "index.html", 374 | "name": "Worlds Hardest Game", 375 | "tags": ["arcade", "strategy", "static"], 376 | "cdn": "true", 377 | "proxy": "false", 378 | "url": "" 379 | }, 380 | { 381 | "img": "/src/assets/games/portaltheflashversion.jpg", 382 | "baseFile": "index.html", 383 | "name": "Portal Flash", 384 | "tags": ["arcade", "strategy", "static"], 385 | "cdn": "true", 386 | "proxy": "false", 387 | "url": "" 388 | }, 389 | { 390 | "img": "/src/assets/games/crossy-road.png", 391 | "baseFile": "index.html", 392 | "name": "Crossy Road", 393 | "tags": ["arcade", "platformer", "static"], 394 | "cdn": "true", 395 | "proxy": "false", 396 | "url": "" 397 | }, 398 | { 399 | "img": "/src/assets/games/subway-surfers.png", 400 | "baseFile": "index.html", 401 | "name": "Subway Surfers", 402 | "tags": ["arcade", "platformer", "static"], 403 | "cdn": "true", 404 | "proxy": "false", 405 | "url": "" 406 | }, 407 | { 408 | "img": "/src/assets/games/dadish.png", 409 | "baseFile": "index.html", 410 | "name": "Dadish", 411 | "tags": ["arcade", "platformer", "static"], 412 | "cdn": "true", 413 | "proxy": "false", 414 | "url": "" 415 | }, 416 | { 417 | "img": "/src/assets/games/dadish-2.png", 418 | "baseFile": "index.html", 419 | "name": "Dadish 2", 420 | "tags": ["arcade", "platformer", "static"], 421 | "cdn": "true", 422 | "proxy": "false", 423 | "url": "" 424 | }, 425 | { 426 | "img": "/src/assets/games/doodle-jump.png", 427 | "baseFile": "index.html", 428 | "name": "Doodle Jump", 429 | "tags": ["arcade", "platformer", "static"], 430 | "cdn": "true", 431 | "proxy": "false", 432 | "url": "" 433 | }, 434 | { 435 | "img": "/src/assets/games/fruit-ninja.png", 436 | "baseFile": "index.html", 437 | "name": "Fruit Ninja", 438 | "tags": ["arcade", "static"], 439 | "cdn": "true", 440 | "proxy": "false", 441 | "url": "" 442 | }, 443 | { 444 | "img": "/src/assets/games/basketball-stars.png", 445 | "baseFile": "index.html", 446 | "name": "Basketball Stars", 447 | "tags": ["arcade", "static"], 448 | "cdn": "true", 449 | "proxy": "false", 450 | "url": "" 451 | }, 452 | { 453 | "img": "/src/assets/games/slope-2.png", 454 | "baseFile": "index.html", 455 | "name": "Slope 2", 456 | "tags": ["arcade", "platformer", "multiplayer", "static"], 457 | "cdn": "true", 458 | "proxy": "false", 459 | "url": "" 460 | }, 461 | { 462 | "img": "/src/assets/games/stack.png", 463 | "baseFile": "index.html", 464 | "name": "Stack", 465 | "tags": ["static", "strategy"], 466 | "cdn": "true", 467 | "proxy": "false", 468 | "url": "" 469 | }, 470 | { 471 | "img": "/src/assets/games/1v1-lol.png", 472 | "baseFile": "none", 473 | "name": "1v1.lol", 474 | "tags": ["multiplayer", "strategy", "proxied"], 475 | "cdn": "false", 476 | "proxy": "true", 477 | "url": "https://1v1.lol" 478 | }, 479 | { 480 | "img": "/src/assets/games/shellshock.png", 481 | "baseFile": "none", 482 | "name": "Shell Shockers", 483 | "tags": ["multiplayer", "strategy", "proxied"], 484 | "cdn": "false", 485 | "proxy": "true", 486 | "url": "https://shellshock.io" 487 | }, 488 | { 489 | "img": "/src/assets/games/binding-of-isaac.jpeg", 490 | "baseFile": "index.html", 491 | "name": "The Binding of Isaac", 492 | "tags": ["arcade", "strategy", "shooter", "flash", "static"], 493 | "cdn": "true", 494 | "proxy": "false", 495 | "url": "" 496 | }, 497 | { 498 | "img": "/src/assets/games/eaglerx.webp", 499 | "baseFile": "index.html", 500 | "name": "EaglerCraft X", 501 | "tags": ["static", "sandbox", "multiplayer"], 502 | "cdn": "true", 503 | "proxy": "false", 504 | "url": "" 505 | }, 506 | { 507 | "img": "/src/assets/games/miniagario.png", 508 | "baseFile": "dots.html", 509 | "name": "Mini Agario", 510 | "tags": ["static", "arcade", "strategy"], 511 | "cdn": "true", 512 | "proxy": "false", 513 | "url": "" 514 | }, 515 | { 516 | "img": "/src/assets/games/yohoho.jpeg", 517 | "baseFile": "", 518 | "name": "YoHoHo", 519 | "tags": ["multiplayer", "strategy", "proxied"], 520 | "cdn": "false", 521 | "proxy": "true", 522 | "url": "https://yohoho.io" 523 | }, 524 | { 525 | "img": "/src/assets/games/paper.io2.webp", 526 | "baseFile": "", 527 | "name": "Paper.io 2", 528 | "tags": ["multiplayer", "strategy", "proxied"], 529 | "cdn": "false", 530 | "proxy": "true", 531 | "url": "https://paper-io.com/" 532 | }, 533 | { 534 | "img": "/src/assets/games/frogger.jpeg", 535 | "baseFile": "index.html", 536 | "name": "Frogger", 537 | "tags": ["arcade", "platformer", "retro", "static"], 538 | "cdn": "true", 539 | "proxy": "false", 540 | "url": "" 541 | }, 542 | { 543 | "img": "/src/assets/games/supermario64.jpeg", 544 | "baseFile": "index.html", 545 | "name": "Super Mario 64", 546 | "tags": ["arcade", "platformer", "retro", "emulation"], 547 | "cdn": "true", 548 | "proxy": "false", 549 | "url": "" 550 | }, 551 | { 552 | "img": "/src/assets/games/cybertanks.png", 553 | "baseFile": "", 554 | "name": "CyberTanks", 555 | "tags": ["multiplayer", "strategy", "proxied"], 556 | "cdn": "false", 557 | "proxy": "true", 558 | "url": "https://xigency.herokuapp.com/" 559 | }, 560 | { 561 | "img": "/src/assets/games/slitherio.webp", 562 | "baseFile": "", 563 | "name": "Slither.io", 564 | "tags": ["multiplayer", "strategy", "proxied"], 565 | "cdn": "false", 566 | "proxy": "true", 567 | "url": "http://slither.io" 568 | }, 569 | { 570 | "img": "/src/assets/games/cat-ninja.png", 571 | "baseFile": "index.html", 572 | "name": "Cat Ninja", 573 | "tags": ["arcade", "static"], 574 | "cdn": "true", 575 | "proxy": "false", 576 | "url": "" 577 | }, 578 | { 579 | "img": "/src/assets/games/gm2.jpeg", 580 | "baseFile": "index.html", 581 | "name": "Gun Mayhem 2", 582 | "tags": ["arcade", "shooter", "static"], 583 | "cdn": "true", 584 | "proxy": "false", 585 | "url": "" 586 | }, 587 | { 588 | "img": "/src/assets/games/ssf.jpeg", 589 | "baseFile": "index.html", 590 | "name": "Super Smash Flash", 591 | "tags": ["arcade", "shooter", "static"], 592 | "cdn": "true", 593 | "proxy": "false", 594 | "url": "" 595 | }, 596 | { 597 | "img": "/src/assets/games/monkey-mart.png", 598 | "baseFile": "index.html", 599 | "name": "Monkey Mart", 600 | "tags": ["arcade"], 601 | "cdn": "true", 602 | "proxy": "false", 603 | "url": "" 604 | }, 605 | { 606 | "img": "/src/assets/games/watermelon.png", 607 | "baseFile": "index.html", 608 | "name": "Watermelon Game", 609 | "tags": ["arcade"], 610 | "cdn": "true", 611 | "proxy": "false", 612 | "url": "" 613 | } 614 | ] 615 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | declare var search: (input: string, template: string) => any; 6 | type Suggestion = { 7 | phrase: string; 8 | }; 9 | declare var BareMux: any; 10 | declare var EpxMod: any; 11 | declare var ScramjetController: any; 12 | declare var $scramjet: any; 13 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | openApp(url: string): void; 4 | searchApps(val: string): void; 5 | setTheme(val: string): void; 6 | setProxy(val: string): void; 7 | transport(val: string): void; 8 | changeTitle(val: string): void; 9 | changeFavicon(val: string): void; 10 | searchGames(val: string): void; 11 | fullScreenGame(val: string): void; 12 | loadProxyScripts(): Promise; 13 | setTransport(transport: string): Promise; 14 | loadMobileNav(): void; 15 | closeMobileNav(): void; 16 | createLink(): void; 17 | exitIframe(): void; 18 | refreshIframe(): void; 19 | setTitle(): void; 20 | __uv: any; 21 | __get$ProxyUrl: any; 22 | $scramjet: any; 23 | } 24 | } 25 | 26 | export {}; 27 | -------------------------------------------------------------------------------- /src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { SEO } from 'astro-seo'; 3 | import { SEO as SEOConf } from "astro:env/client"; 4 | 5 | interface Props { 6 | title?: string; 7 | redirect?: { 8 | path: string; 9 | interval: number; 10 | } 11 | } 12 | const { title, redirect } = Astro.props; 13 | import { ClientRouter } from 'astro:transitions'; 14 | import LoadScripts from '@components/Scripts.astro'; 15 | import SettingsLoader from '@components/settings/Loader.astro'; 16 | --- 17 | 18 | 19 | 20 | 21 | 22 | 63 | {redirect !== undefined && } 64 | 65 | 66 | 67 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /src/pages/apps.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { Image } from 'astro:assets'; 3 | import Layout from '@layouts/Layout.astro'; 4 | import type { ImageMetadata } from 'astro'; 5 | import { Icon } from 'astro-icon/components'; 6 | const images = import.meta.glob<{ default: ImageMetadata }>( 7 | '/src/assets/apps/*.{jpeg,jpg,png,gif}' 8 | ); 9 | --- 10 | 11 | 12 |
13 |
14 |
15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 | Google Logo 24 |
25 |
26 | YT Logo 27 |
28 |
29 | Spotify Logo 30 |
31 |
32 | Twitter Logo 33 |
34 |
35 | Reddit Logo 36 |
37 |
38 | Twitch Logo 39 |
40 |
41 | Discord Logo 42 |
43 |
44 | TikTok Logo 45 |
46 |
47 | Retroarch Logo 48 |
49 |
50 | Coolmath Games Logo 51 |
52 |
53 | GeforceNow Logo 54 |
55 |
56 | Y8 Games Logo 57 |
58 |
59 |
60 | 86 |
87 | -------------------------------------------------------------------------------- /src/pages/community.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Header from '@components/Header.astro'; 3 | import Layout from '@layouts/Layout.astro'; 4 | --- 5 | 6 |
7 |
8 |
9 |

You are being taken to another website (discord.gg/unblock)

10 |

11 | Are you sure you want to proceed? 17 |

18 |
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /src/pages/gs/[game].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import type { GetStaticPaths } from 'astro'; 3 | import games from '../../data/games.json'; 4 | const { game } = Astro.params; 5 | import { GAMES_LINK } from 'astro:env/client'; 6 | import Layout from '@layouts/Layout.astro'; 7 | import { Icon } from 'astro-icon/components'; 8 | function decode(val: string) { 9 | try { 10 | return atob(val); 11 | } catch (_) {} 12 | } 13 | if (!GAMES_LINK) { 14 | return Astro.redirect('/'); 15 | } 16 | interface Game { 17 | params: { game: string }; 18 | } 19 | export const getStaticPaths = (async () => { 20 | const gamesList: Game[] = games.map((game) => ({ 21 | params: { game: btoa(game.name) }, 22 | })); 23 | return gamesList; 24 | }) satisfies GetStaticPaths; 25 | --- 26 | 27 |
28 |
29 |
30 | 35 | 36 | 37 |

38 | {decode(game as string)} 39 |

40 | 47 |
48 |
49 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /src/pages/load.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '@layouts/Layout.astro'; 3 | --- 4 | 5 |
6 |
7 |

8 | Please wait a moment. Our service maybe under high load causing some slowness on our 9 | servers. Your request will eventually be responded to. 10 |

11 |
12 | 38 |
39 | -------------------------------------------------------------------------------- /src/pages/options/about.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import NavMenu from '@components/MobileNav.astro'; 3 | import Layout from '@layouts/Layout.astro'; 4 | import { Icon } from 'astro-icon/components'; 5 | --- 6 | 7 | 8 |
9 |
10 |
11 | 12 | 13 | 14 |

15 | Options 16 |

17 |
18 | 38 |
39 | 40 |
41 |
42 |
43 | About Incognito 44 |

45 | Access the world wide web, Incognito is an anti-censorship web service. 46 |

47 |
48 |
49 |
50 | Authors 51 |

52 | MotorTruck1221 - Incognito developer & maintainer 58 |

59 |

60 | Rifting/Riftriot - Incognito developer & maintainer 66 |

67 |

68 | e9x - Creator of TompHTTP 79 |

80 |

81 | Caracal.js - Creator of Ultraviolet & Incognito 92 |

93 |
94 |
95 |
96 |
97 | -------------------------------------------------------------------------------- /src/pages/options/bt.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import NavMenu from '@components/MobileNav.astro'; 3 | import Layout from '@layouts/Layout.astro'; 4 | import { Icon } from 'astro-icon/components'; 5 | --- 6 | 7 | 8 |
9 |
10 |
11 | 12 | 13 | 14 |

15 | Options 16 |

17 |
18 | 23 |
24 | 25 |
26 |
27 |
28 |

Tab Title

29 | 30 |

Change the title of Incognito's browser tab title

31 |
32 |
33 |

Tab Icon

34 | 35 |

Change the icon of Incognito's browser tab. To change it into something like Google, type in "https://www.google.com/favicon.ico"

36 |
37 |
38 |
39 | 71 |
72 | -------------------------------------------------------------------------------- /src/pages/options/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import NavMenu from '@components/MobileNav.astro'; 3 | import Layout from '@layouts/Layout.astro'; 4 | import { Icon } from 'astro-icon/components'; 5 | --- 6 | 7 |
8 |
9 |
10 | 11 | 12 | 13 |

14 | Options 15 |

16 |
17 | 34 |
35 | 36 |
37 |
38 |
39 |

Theme

40 |
43 |
    44 |
  • 49 | Ocean 50 |
  • 51 |
  • 56 | Midnight 57 |
  • 58 |
  • 63 | Space 64 |
  • 65 |
  • 70 | Morning 71 |
  • 72 |
  • 77 | Terminal 78 |
  • 79 |
  • 84 | Resilient 85 |
  • 86 |
87 |
88 |
89 |
90 |

Proxy

91 |
94 |
    95 |
  • 100 | Ultraviolet 101 |
  • 102 |
  • 107 | Scramjet (BETA, MAY BREAK) 108 |
  • 109 |
110 |
111 |
112 |
113 |

Transport

114 |
117 |
    118 |
  • 123 | Epoxy 124 |
  • 125 |
  • 130 | Libcurl.js 131 |
  • 132 |
133 |
134 |
135 |
136 |
137 | 178 |
179 | -------------------------------------------------------------------------------- /src/pages/support.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Header from '@components/Header.astro'; 3 | import Layout from '@layouts/Layout.astro'; 4 | --- 5 | 6 |
7 |
8 |
9 | A page is not functioning or loading on the proxy 11 |

12 | Our web servers are either under really high load, or the web page you tried 13 | accessing is not currently supported. 14 |

15 |
16 |
17 |
18 | Is my information secure 19 |

20 | Yes, your informaton is as secure as the sites visiting them. Our end to end TLS encryption through websockets ensures even more that it is 26 | secure. 27 |

28 |
29 |
30 |
31 | Where can I obtain more links to Incognito 33 |

34 | Links to incognito can be generated at Titanium Network 40 | 41 |

42 |
43 |
44 |
45 | Why is GeForce Now is responding with "403 Access Denied" 47 |

48 | At the moment, too many people are using GeForce Now on our servers. Try again 49 | later. 50 |

51 |
52 |
53 |
54 | Google login is saying this app is insecure 56 |

57 | At the moment, Google login is not supported for @gmail accounts. However, it is 58 | supported for GSuite accounts (School / work email). 59 |

60 |
61 |
62 |
63 |
64 | -------------------------------------------------------------------------------- /src/ts/serviceWorker.ts: -------------------------------------------------------------------------------- 1 | function initServiceWorker() { 2 | return new Promise((resolve) => { 3 | if ('serviceWorker' in navigator) { 4 | console.log('OOGOGOGOGO'); 5 | //@ts-ignore these are a fucking thing 6 | //wait for the scripts to load 7 | const scram = window.loadProxyScripts().then(async (): typeof ScramjetController => { 8 | const scramjet = new ScramjetController({ 9 | prefix: "/~/scramjet/", 10 | files: { 11 | wasm: "/scram/scramjet.wasm.js", 12 | worker: "/scram/scramjet.worker.js", 13 | client: "/scram/scramjet.client.js", 14 | shared: "/scram/scramjet.shared.js", 15 | sync: "/scram/scramjet.sync.js" 16 | } 17 | }); 18 | //@ts-ignore these fucking exist 19 | //make sure the transport is set before continuing 20 | await window.setTransport(localStorage.getItem('incog||transport')); 21 | await scramjet.init('/sw.js'); 22 | return scramjet; 23 | }); 24 | return resolve(scram); 25 | }; 26 | }); 27 | } 28 | 29 | export { initServiceWorker }; 30 | -------------------------------------------------------------------------------- /src/ts/settings.ts: -------------------------------------------------------------------------------- 1 | function setTheme(name: string | null) { 2 | if (name === 'ocean') { 3 | localStorage.setItem('incog||currentTheme', 'ocean'); 4 | document.documentElement.className = ''; 5 | } else { 6 | localStorage.setItem('incog||currentTheme', name as string); 7 | document.documentElement.className = name as string; 8 | } 9 | } 10 | function setProxy(name: string) { 11 | localStorage.setItem('incog||proxy', name); 12 | } 13 | function setTransport(name: string) { 14 | localStorage.setItem('incog||transport', name); 15 | } 16 | function changeTitle(title: string | null) { 17 | localStorage.setItem('incog||title', title as string); 18 | if (title === null || title === 'null') { 19 | return; 20 | } else { 21 | document.title = title; 22 | } 23 | } 24 | function changeFavicon(url: string | null) { 25 | localStorage.setItem('incog||favicon', url as string); 26 | if (url === null || url === 'null') { 27 | return; 28 | } else { 29 | let favicon = document.getElementById('favicon') as HTMLLinkElement; 30 | favicon.href = url; 31 | } 32 | } 33 | 34 | export { changeFavicon, changeTitle, setProxy, setTheme, setTransport }; 35 | -------------------------------------------------------------------------------- /tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | variants: { 9 | extend: { 10 | display: ['group-hover'], 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "exclude": ["server/**/*", "dist/**/*", "node_modules/**/*", "public/**/*", "vendor/**"], 4 | "compilerOptions": { 5 | "baseUrl": ".", 6 | "paths": { 7 | "@components/*": ["src/components/*"], 8 | "@layouts/*": ["src/layouts/*"], 9 | "@scripts/*": ["src/ts/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vendor/scramjet/LICENSE: -------------------------------------------------------------------------------- 1 | Scramjet is licensed under AGPL-3. You can view the full terms of the license here: https://www.gnu.org/licenses/agpl-3.0.txt 2 | 3 | - Your project using scramjet MUST be open source, and must be compatible with GPL 4 | - Your project must include clear and prominent credit to the scramjet project, with a link to https://discord.gg/88CapFYSEd 5 | 6 | If these terms cannot work for your project, email contact@mercurywork.shop for information on a obtaining a dual license. 7 | -------------------------------------------------------------------------------- /vendor/scramjet/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | --- 6 | 7 | > [!WARNING] 8 | > Scramjet is not currently production ready, DO NOT USE THIS AS THE MAIN OPTION IN YOUR SITE. 9 | 10 | npm version 11 | 12 | Scramjet is an experimental interception based web proxy that aims to be the successor to Ultraviolet. It is designed with security, developer friendliness, and performance in mind. Scramjet strives to have a clean, organized codebase to improve maintainability. Scramjet is made to evade internet censorship and bypass arbitrary web browser restrictions. 13 | 14 | ## Supported Sites 15 | 16 | Some of the popular websites that Scramjet supports include: 17 | 18 | - [Google](https://google.com) 19 | - [Youtube](https://www.youtube.com) 20 | - [Spotify](https://spotify.com) 21 | - [Discord](https://discord.com) 22 | - [Reddit](https://reddit.com) 23 | - [GeForce NOW](https://play.geforcenow.com/) 24 | - [now.gg](https://now.gg) 25 | 26 | ## Development 27 | 28 | ### Dependencies 29 | 30 | - Recent versions of `node.js` and `pnpm` 31 | - `rustup` 32 | - `wasm-bindgen` 33 | - `wasm-opt` 34 | - [this `wasm-snip` fork](https://github.com/r58Playz/wasm-snip) 35 | 36 | #### Building 37 | 38 | - Clone the repository with `git clone --recursive https://github.com/MercuryWorkshop/scramjet` 39 | - Install the dependencies with `pnpm i` 40 | - Build the rewriter with `pnpm rewriter:build` 41 | - Build Scramjet with `pnpm build` 42 | 43 | ### Running Scramjet Locally 44 | 45 | You can run the Scramjet dev server with the command 46 | 47 | ```sh 48 | pnpm dev 49 | ``` 50 | 51 | Scramjet should now be running at `localhost:1337` and should rebuild upon a file being changed (excluding the rewriter). 52 | -------------------------------------------------------------------------------- /vendor/scramjet/dist/scramjet.controller.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var e={1762:function(e,r,t){t.d(r,{Z:function(){return o}});let o={fmt:function(e,r,...t){let o=Error.prepareStackTrace;Error.prepareStackTrace=(e,r)=>{r.shift(),r.shift(),r.shift();let t="";for(let e=1;e `+t);return t+=r[0].getFunctionName()||"Anonymous"};let n=function(){try{throw Error()}catch(e){return e.stack}}();Error.prepareStackTrace=o;let c=console[e]||console.log;c(`%c${n}%c ${r}`,` 2 | background-color: ${{log:"#000",warn:"#f80",error:"#f00",debug:"transparent"}[e]}; 3 | color: ${{log:"#fff",warn:"#fff",error:"#fff",debug:"gray"}[e]}; 4 | padding: ${{log:2,warn:4,error:4,debug:0}[e]}px; 5 | font-weight: bold; 6 | font-family: monospace; 7 | font-size: 0.9em; 8 | `,`${"debug"===e?"color: gray":""}`,...t)},log:function(e,...r){this.fmt("log",e,...r)},warn:function(e,...r){this.fmt("warn",e,...r)},error:function(e,...r){this.fmt("error",e,...r)},debug:function(e,...r){this.fmt("debug",e,...r)}}}},r={};function t(o){var n=r[o];if(void 0!==n)return n.exports;var c=r[o]={exports:{}};return e[o](c,c.exports,t),c.exports}t.d=function(e,r){for(var o in r)t.o(r,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)};let o=Symbol.for("scramjet client global"),n=Symbol.for("scramjet frame handle");var c=t(1762).Z;class s extends EventTarget{controller;frame;constructor(e,r){super(),this.controller=e,this.frame=r,r[n]=this}get client(){return this.frame.contentWindow.window[o]}get url(){return this.client.url}go(e){e instanceof URL&&(e=e.toString()),c.log("navigated to",e),this.frame.src=this.controller.encodeUrl(e)}back(){this.frame.contentWindow?.history.back()}forward(){this.frame.contentWindow?.history.forward()}reload(){this.frame.contentWindow?.location.reload()}}!("$scramjet"in self)&&(self.$scramjet={version:{build:"1efcf85",version:"1.0.2-dev"},codec:{},flagEnabled:function(e,r){let t=i.config.flags[e];for(let t in i.config.siteFlags){let o=i.config.siteFlags[t];if(new RegExp(t).test(r.href)&&e in o)return o[e]}return t}});let i=self.$scramjet,a=Function;function f(){i.codec.encode=a("url",i.config.codec.encode),i.codec.decode=a("url",i.config.codec.decode)}var l=t(1762).Z;window.ScramjetController=class e{db;constructor(e){let r={prefix:"/scramjet/",globals:{wrapfn:"$scramjet$wrap",wrapthisfn:"$scramjet$wrapthis",trysetfn:"$scramjet$tryset",importfn:"$scramjet$import",rewritefn:"$scramjet$rewrite",metafn:"$scramjet$meta",setrealmfn:"$scramjet$setrealm",pushsourcemapfn:"$scramjet$pushsourcemap"},files:{wasm:"/scramjet.wasm.js",shared:"/scramjet.shared.js",worker:"/scramjet.worker.js",client:"/scramjet.client.js",sync:"/scramjet.sync.js"},flags:{serviceworkers:!1,syncxhr:!1,naiiveRewriter:!1,strictRewrites:!0,rewriterLogs:!0,captureErrors:!0,cleanErrors:!1,scramitize:!1,sourcemaps:!1},siteFlags:{},codec:{encode:`if (!url) return url; 9 | return encodeURIComponent(url);`,decode:`if (!url) return url; 10 | return decodeURIComponent(url);`}},t=(e,r)=>{for(let o in r)r[o]instanceof Object&&o in e&&Object.assign(r[o],t(e[o],r[o]));return Object.assign(e||{},r)};i.config=t(r,e)}async init(e){f(),await this.openIDB();let r=await navigator.serviceWorker.register(e);return l.log("service worker registered"),r}createFrame(e){return!e&&(e=document.createElement("iframe")),new s(this,e)}encodeUrl(e){return e instanceof URL&&(e=e.toString()),i.config.prefix+i.codec.encode(e)}decodeUrl(e){return e instanceof URL&&(e=e.toString()),i.codec.decode(e)}async openIDB(){let e=indexedDB.open("$scramjet",1);return new Promise((r,t)=>{e.onsuccess=async()=>{this.db=e.result,await this.#e(),r(e.result)},e.onupgradeneeded=()=>{let r=e.result;!r.objectStoreNames.contains("config")&&r.createObjectStore("config"),!r.objectStoreNames.contains("cookies")&&r.createObjectStore("cookies")},e.onerror=()=>t(e.error)})}async #e(){if(!this.db){console.error("Store not ready!");return}let e=this.db.transaction("config","readwrite").objectStore("config").put(i.config,"config");return new Promise((r,t)=>{e.onsuccess=r,e.onerror=t})}async modifyConfig(e){i.config=Object.assign({},i.config,e),f(),await this.#e()}}})(); 11 | //# sourceMappingURL=scramjet.controller.js.map -------------------------------------------------------------------------------- /vendor/scramjet/dist/scramjet.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"scramjet.controller.js","sources":["webpack://@mercuryworkshop/scramjet/./src/log.ts","webpack://@mercuryworkshop/scramjet/./src/symbols.ts","webpack://@mercuryworkshop/scramjet/./src/controller/frame.ts","webpack://@mercuryworkshop/scramjet/./src/scramjet.ts","webpack://@mercuryworkshop/scramjet/./src/controller/index.ts"],"sourcesContent":["export default {\n\tfmt: function (severity: string, message: string, ...args: any[]) {\n\t\tconst old = Error.prepareStackTrace;\n\n\t\tError.prepareStackTrace = (_, stack) => {\n\t\t\tstack.shift(); // stack();\n\t\t\tstack.shift(); // fmt();\n\t\t\tstack.shift();\n\n\t\t\tlet fmt = \"\";\n\t\t\tfor (let i = 1; i < Math.min(2, stack.length); i++) {\n\t\t\t\tif (stack[i].getFunctionName()) {\n\t\t\t\t\t// const f = stack[i].getThis()?.constructor?.name;\n\t\t\t\t\t// if (f) fmt += `${f}.`\n\t\t\t\t\tfmt += `${stack[i].getFunctionName()} -> ` + fmt;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfmt += stack[0].getFunctionName() || \"Anonymous\";\n\n\t\t\treturn fmt;\n\t\t};\n\n\t\tconst fmt = (function stack() {\n\t\t\ttry {\n\t\t\t\tthrow new Error();\n\t\t\t} catch (e) {\n\t\t\t\treturn e.stack;\n\t\t\t}\n\t\t})();\n\n\t\tError.prepareStackTrace = old;\n\n\t\tconst fn = console[severity] || console.log;\n\t\tconst bg = {\n\t\t\tlog: \"#000\",\n\t\t\twarn: \"#f80\",\n\t\t\terror: \"#f00\",\n\t\t\tdebug: \"transparent\",\n\t\t}[severity];\n\t\tconst fg = {\n\t\t\tlog: \"#fff\",\n\t\t\twarn: \"#fff\",\n\t\t\terror: \"#fff\",\n\t\t\tdebug: \"gray\",\n\t\t}[severity];\n\t\tconst padding = {\n\t\t\tlog: 2,\n\t\t\twarn: 4,\n\t\t\terror: 4,\n\t\t\tdebug: 0,\n\t\t}[severity];\n\n\t\tfn(\n\t\t\t`%c${fmt}%c ${message}`,\n\t\t\t`\n\t\tbackground-color: ${bg};\n\t\tcolor: ${fg};\n\t\tpadding: ${padding}px;\n\t\tfont-weight: bold;\n\t\tfont-family: monospace;\n\t\tfont-size: 0.9em;\n\t`,\n\t\t\t`${severity === \"debug\" ? \"color: gray\" : \"\"}`,\n\t\t\t...args\n\t\t);\n\t},\n\tlog: function (message: string, ...args: any[]) {\n\t\tthis.fmt(\"log\", message, ...args);\n\t},\n\twarn: function (message: string, ...args: any[]) {\n\t\tthis.fmt(\"warn\", message, ...args);\n\t},\n\terror: function (message: string, ...args: any[]) {\n\t\tthis.fmt(\"error\", message, ...args);\n\t},\n\tdebug: function (message: string, ...args: any[]) {\n\t\tthis.fmt(\"debug\", message, ...args);\n\t},\n};\n","// see types.d.ts for what these mean\nexport const SCRAMJETCLIENT = Symbol.for(\"scramjet client global\");\nexport const SCRAMJETFRAME = Symbol.for(\"scramjet frame handle\");\n","import { ScramjetController } from \".\";\nimport type { ScramjetClient } from \"../client/client\";\nimport { SCRAMJETCLIENT, SCRAMJETFRAME } from \"../symbols\";\n\nexport class ScramjetFrame extends EventTarget {\n\tconstructor(\n\t\tprivate controller: ScramjetController,\n\t\tpublic frame: HTMLIFrameElement\n\t) {\n\t\tsuper();\n\t\tframe[SCRAMJETFRAME] = this;\n\t}\n\n\tget client(): ScramjetClient {\n\t\treturn this.frame.contentWindow.window[SCRAMJETCLIENT];\n\t}\n\n\tget url(): URL {\n\t\treturn this.client.url;\n\t}\n\n\tgo(url: string | URL) {\n\t\tif (url instanceof URL) url = url.toString();\n\n\t\tdbg.log(\"navigated to\", url);\n\n\t\tthis.frame.src = this.controller.encodeUrl(url);\n\t}\n\n\tback() {\n\t\tthis.frame.contentWindow?.history.back();\n\t}\n\n\tforward() {\n\t\tthis.frame.contentWindow?.history.forward();\n\t}\n\n\treload() {\n\t\tthis.frame.contentWindow?.location.reload();\n\t}\n}\n","import { ScramjetFlags } from \"./types\";\n\nif (!(\"$scramjet\" in self)) {\n\t// @ts-expect-error ts stuff\n\tself.$scramjet = {\n\t\tversion: {\n\t\t\tbuild: COMMITHASH,\n\t\t\tversion: VERSION,\n\t\t},\n\t\tcodec: {},\n\t\tflagEnabled,\n\t};\n}\n\nexport const $scramjet = self.$scramjet;\n\nconst nativeFunction = Function;\nexport function loadCodecs() {\n\t$scramjet.codec.encode = nativeFunction(\n\t\t\"url\",\n\t\t$scramjet.config.codec.encode\n\t) as any;\n\t$scramjet.codec.decode = nativeFunction(\n\t\t\"url\",\n\t\t$scramjet.config.codec.decode\n\t) as any;\n}\n\nexport function flagEnabled(flag: keyof ScramjetFlags, url: URL): boolean {\n\tconst value = $scramjet.config.flags[flag];\n\tfor (const regex in $scramjet.config.siteFlags) {\n\t\tconst partialflags = $scramjet.config.siteFlags[regex];\n\t\tif (new RegExp(regex).test(url.href) && flag in partialflags) {\n\t\t\treturn partialflags[flag];\n\t\t}\n\t}\n\n\treturn value;\n}\n","import { ScramjetConfig } from \"../types\";\nimport { ScramjetFrame } from \"./frame\";\nimport { $scramjet, loadCodecs } from \"../scramjet\";\n\nexport class ScramjetController {\n\tprivate db: IDBDatabase;\n\n\tconstructor(config: Partial) {\n\t\t// sane ish defaults\n\t\tconst defaultConfig: ScramjetConfig = {\n\t\t\tprefix: \"/scramjet/\",\n\t\t\tglobals: {\n\t\t\t\twrapfn: \"$scramjet$wrap\",\n\t\t\t\twrapthisfn: \"$scramjet$wrapthis\",\n\t\t\t\ttrysetfn: \"$scramjet$tryset\",\n\t\t\t\timportfn: \"$scramjet$import\",\n\t\t\t\trewritefn: \"$scramjet$rewrite\",\n\t\t\t\tmetafn: \"$scramjet$meta\",\n\t\t\t\tsetrealmfn: \"$scramjet$setrealm\",\n\t\t\t\tpushsourcemapfn: \"$scramjet$pushsourcemap\",\n\t\t\t},\n\t\t\tfiles: {\n\t\t\t\twasm: \"/scramjet.wasm.js\",\n\t\t\t\tshared: \"/scramjet.shared.js\",\n\t\t\t\tworker: \"/scramjet.worker.js\",\n\t\t\t\tclient: \"/scramjet.client.js\",\n\t\t\t\tsync: \"/scramjet.sync.js\",\n\t\t\t},\n\t\t\tflags: {\n\t\t\t\tserviceworkers: false,\n\t\t\t\tsyncxhr: false,\n\t\t\t\tnaiiveRewriter: false,\n\t\t\t\tstrictRewrites: true,\n\t\t\t\trewriterLogs: true,\n\t\t\t\tcaptureErrors: true,\n\t\t\t\tcleanErrors: false,\n\t\t\t\tscramitize: false,\n\t\t\t\tsourcemaps: false,\n\t\t\t},\n\t\t\tsiteFlags: {},\n\t\t\tcodec: {\n\t\t\t\tencode: `if (!url) return url;\n\t\t\t\t\treturn encodeURIComponent(url);`,\n\t\t\t\tdecode: `if (!url) return url;\n\t\t\t\t\treturn decodeURIComponent(url);`,\n\t\t\t},\n\t\t};\n\n\t\tconst deepMerge = (target: any, source: any): any => {\n\t\t\tfor (const key in source) {\n\t\t\t\tif (source[key] instanceof Object && key in target) {\n\t\t\t\t\tObject.assign(source[key], deepMerge(target[key], source[key]));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn Object.assign(target || {}, source);\n\t\t};\n\n\t\t$scramjet.config = deepMerge(defaultConfig, config);\n\t}\n\n\tasync init(serviceWorkerPath: string): Promise {\n\t\tloadCodecs();\n\n\t\tawait this.openIDB();\n\n\t\tconst reg = await navigator.serviceWorker.register(serviceWorkerPath);\n\t\tdbg.log(\"service worker registered\");\n\n\t\treturn reg;\n\t}\n\n\tcreateFrame(frame?: HTMLIFrameElement): ScramjetFrame {\n\t\tif (!frame) {\n\t\t\tframe = document.createElement(\"iframe\");\n\t\t}\n\n\t\treturn new ScramjetFrame(this, frame);\n\t}\n\n\tencodeUrl(url: string | URL): string {\n\t\tif (url instanceof URL) url = url.toString();\n\n\t\treturn $scramjet.config.prefix + $scramjet.codec.encode(url);\n\t}\n\n\tdecodeUrl(url: string | URL) {\n\t\tif (url instanceof URL) url = url.toString();\n\n\t\treturn $scramjet.codec.decode(url);\n\t}\n\n\tasync openIDB(): Promise {\n\t\tconst db = indexedDB.open(\"$scramjet\", 1);\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tdb.onsuccess = async () => {\n\t\t\t\tthis.db = db.result;\n\t\t\t\tawait this.#saveConfig();\n\t\t\t\tresolve(db.result);\n\t\t\t};\n\t\t\tdb.onupgradeneeded = () => {\n\t\t\t\tconst res = db.result;\n\t\t\t\tif (!res.objectStoreNames.contains(\"config\")) {\n\t\t\t\t\tres.createObjectStore(\"config\");\n\t\t\t\t}\n\t\t\t\tif (!res.objectStoreNames.contains(\"cookies\")) {\n\t\t\t\t\tres.createObjectStore(\"cookies\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tdb.onerror = () => reject(db.error);\n\t\t});\n\t}\n\n\tasync #saveConfig() {\n\t\tif (!this.db) {\n\t\t\tconsole.error(\"Store not ready!\");\n\n\t\t\treturn;\n\t\t}\n\t\tconst tx = this.db.transaction(\"config\", \"readwrite\");\n\t\tconst store = tx.objectStore(\"config\");\n\t\tconst req = store.put($scramjet.config, \"config\");\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treq.onsuccess = resolve;\n\t\t\treq.onerror = reject;\n\t\t});\n\t}\n\n\tasync modifyConfig(config: ScramjetConfig) {\n\t\t$scramjet.config = Object.assign({}, $scramjet.config, config);\n\t\tloadCodecs();\n\n\t\tawait this.#saveConfig();\n\t}\n}\n\nwindow.ScramjetController = ScramjetController;\n"],"names":["severity","message","args","old","Error","_","stack","fmt","i","Math","e","fn","console","SCRAMJETCLIENT","Symbol","SCRAMJETFRAME","ScramjetFrame","EventTarget","controller","frame","url","URL","dbg","self","COMMITHASH","VERSION","flagEnabled","flag","value","$scramjet","regex","partialflags","RegExp","nativeFunction","Function","loadCodecs","window","ScramjetController","config","defaultConfig","deepMerge","target","source","key","Object","serviceWorkerPath","reg","navigator","document","db","indexedDB","Promise","resolve","reject","res","req","store","tx"],"mappings":"+EAAA,MAAe,CACd,IAAK,SAAUA,CAAgB,CAAEC,CAAe,CAAE,GAAGC,CAAW,EAC/D,IAAMC,EAAMC,MAAM,iBAAiB,AAEnCA,CAAAA,MAAM,iBAAiB,CAAG,CAACC,EAAGC,KAC7BA,EAAM,KAAK,GACXA,EAAM,KAAK,GACXA,EAAM,KAAK,GAEX,IAAIC,EAAM,GACV,IAAK,IAAIC,EAAI,EAAGA,EAAIC,KAAK,GAAG,CAAC,EAAGH,EAAM,MAAM,EAAGE,IAC1CF,CAAK,CAACE,EAAE,CAAC,eAAe,IAG3BD,CAAAA,GAAO,CAAC,EAAED,CAAK,CAACE,EAAE,CAAC,eAAe,GAAG,IAAI,CAAC,CAAGD,CAAE,EAKjD,OAFAA,GAAOD,CAAK,CAAC,EAAE,CAAC,eAAe,IAAM,WAGtC,EAEA,IAAMC,EAAO,WACZ,GAAI,CACH,MAAM,AAAIH,OACX,CAAE,MAAOM,EAAG,CACX,OAAOA,EAAE,KAAK,AACf,CACD,GAEAN,CAAAA,MAAM,iBAAiB,CAAGD,EAE1B,IAAMQ,EAAKC,OAAO,CAACZ,EAAS,EAAIY,QAAQ,GAAG,CAoB3CD,EACC,CAAC,EAAE,EAAEJ,EAAI,GAAG,EAAEN,EAAQ,CAAC,CACvB;oBACiB,EAtBP,CACV,IAAK,OACL,KAAM,OACN,MAAO,OACP,MAAO,aACR,CAAC,CAACD,EAAS,CAiBY;SAChB,EAjBI,CACV,IAAK,OACL,KAAM,OACN,MAAO,OACP,MAAO,MACR,CAAC,CAACA,EAAS,CAYC;WACH,EAZO,CACf,IAAK,EACL,KAAM,EACN,MAAO,EACP,MAAO,CACR,CAAC,CAACA,EAAS,CAOQ;;;;CAIpB,CAAC,CACC,CAAC,EAAEA,AAAa,UAAbA,EAAuB,cAAgB,GAAG,CAAC,IAC3CE,EAEL,EACA,IAAK,SAAUD,CAAe,CAAE,GAAGC,CAAW,EAC7C,IAAI,CAAC,GAAG,CAAC,MAAOD,KAAYC,EAC7B,EACA,KAAM,SAAUD,CAAe,CAAE,GAAGC,CAAW,EAC9C,IAAI,CAAC,GAAG,CAAC,OAAQD,KAAYC,EAC9B,EACA,MAAO,SAAUD,CAAe,CAAE,GAAGC,CAAW,EAC/C,IAAI,CAAC,GAAG,CAAC,QAASD,KAAYC,EAC/B,EACA,MAAO,SAAUD,CAAe,CAAE,GAAGC,CAAW,EAC/C,IAAI,CAAC,GAAG,CAAC,QAASD,KAAYC,EAC/B,CACD,C,4SC7EO,IAAMW,EAAiBC,OAAO,GAAG,CAAC,0BAC5BC,EAAgBD,OAAO,GAAG,CAAC,yB,eCEjC,OAAME,UAAsBC,Y,gBAClC,aACSC,CAA8B,CAC/BC,CAAwB,CAC9B,CACD,KAAK,QAHGD,UAAU,CAAVA,EAAAA,IAAAA,CACDC,KAAK,CAALA,EAGPA,CAAK,CAACJ,EAAc,CAAG,IAAI,AAC5B,CAEA,IAAI,QAAyB,CAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAACF,EAAe,AACvD,CAEA,IAAI,KAAW,CACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,AACvB,CAEA,GAAGO,CAAiB,CAAE,CACjBA,aAAeC,KAAKD,CAAAA,EAAMA,EAAI,QAAQ,EAAC,EAE3CE,EAAI,GAAG,CAAC,eAAgBF,GAExB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAACA,EAC5C,CAEA,MAAO,CACN,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,QAAQ,MACnC,CAEA,SAAU,CACT,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,QAAQ,SACnC,CAEA,QAAS,CACR,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,SAAS,QACpC,CACD,CCtCI,CAAE,eAAeG,IAAG,GAEvBA,CAAAA,KAAK,SAAS,CAAG,CAChB,QAAS,CACR,MAAOC,UACP,QAASC,WACV,EACA,MAAO,CAAC,EACRC,YAkBK,SAAqBC,CAAyB,CAAEP,CAAQ,EAC9D,IAAMQ,EAAQC,EAAU,MAAM,CAAC,KAAK,CAACF,EAAK,CAC1C,IAAK,IAAMG,KAASD,EAAU,MAAM,CAAC,SAAS,CAAE,CAC/C,IAAME,EAAeF,EAAU,MAAM,CAAC,SAAS,CAACC,EAAM,CACtD,GAAI,IAAIE,OAAOF,GAAO,IAAI,CAACV,EAAI,IAAI,GAAKO,KAAQI,EAC/C,OAAOA,CAAY,CAACJ,EAAK,AAE3B,CAEA,OAAOC,CACR,CA3BC,GAGM,IAAMC,EAAYN,KAAK,SAAS,CAEjCU,EAAiBC,SAChB,SAASC,IACfN,EAAU,KAAK,CAAC,MAAM,CAAGI,EACxB,MACAJ,EAAU,MAAM,CAAC,KAAK,CAAC,MAAM,EAE9BA,EAAU,KAAK,CAAC,MAAM,CAAGI,EACxB,MACAJ,EAAU,MAAM,CAAC,KAAK,CAAC,MAAM,CAE/B,C,eCgHAO,CAAAA,OAAO,kBAAkB,CAtIlB,MAAMC,EACJ,EAAgB,AAExB,aAAYC,CAA+B,CAAE,CAE5C,IAAMC,EAAgC,CACrC,OAAQ,aACR,QAAS,CACR,OAAQ,iBACR,WAAY,qBACZ,SAAU,mBACV,SAAU,mBACV,UAAW,oBACX,OAAQ,iBACR,WAAY,qBACZ,gBAAiB,yBAClB,EACA,MAAO,CACN,KAAM,oBACN,OAAQ,sBACR,OAAQ,sBACR,OAAQ,sBACR,KAAM,mBACP,EACA,MAAO,CACN,eAAgB,GAChB,QAAS,GACT,eAAgB,GAChB,eAAgB,GAChB,aAAc,GACd,cAAe,GACf,YAAa,GACb,WAAY,GACZ,WAAY,EACb,EACA,UAAW,CAAC,EACZ,MAAO,CACN,OAAQ,CAAC;oCACuB,CAAC,CACjC,OAAQ,CAAC;oCACuB,CAAC,AAClC,CACD,EAEMC,EAAY,CAACC,EAAaC,KAC/B,IAAK,IAAMC,KAAOD,EACbA,CAAM,CAACC,EAAI,WAAYC,QAAUD,KAAOF,GAC3CG,OAAO,MAAM,CAACF,CAAM,CAACC,EAAI,CAAEH,EAAUC,CAAM,CAACE,EAAI,CAAED,CAAM,CAACC,EAAI,GAI/D,OAAOC,OAAO,MAAM,CAACH,GAAU,CAAC,EAAGC,EACpC,CAEAb,CAAAA,EAAU,MAAM,CAAGW,EAAUD,EAAeD,EAC7C,CAEA,MAAM,KAAKO,CAAyB,CAAsC,CACzEV,IAEA,MAAM,IAAI,CAAC,OAAO,GAElB,IAAMW,EAAM,MAAMC,UAAU,aAAa,CAAC,QAAQ,CAACF,GAGnD,OAFAvB,EAAI,GAAG,CAAC,6BAEDwB,CACR,CAEA,YAAY3B,CAAyB,CAAiB,CAKrD,MAJI,CAACA,GACJA,CAAAA,EAAQ6B,SAAS,aAAa,CAAC,SAAQ,EAGjC,IAAIhC,EAAc,IAAI,CAAEG,EAChC,CAEA,UAAUC,CAAiB,CAAU,CAGpC,OAFIA,aAAeC,KAAKD,CAAAA,EAAMA,EAAI,QAAQ,EAAC,EAEpCS,EAAU,MAAM,CAAC,MAAM,CAAGA,EAAU,KAAK,CAAC,MAAM,CAACT,EACzD,CAEA,UAAUA,CAAiB,CAAE,CAG5B,OAFIA,aAAeC,KAAKD,CAAAA,EAAMA,EAAI,QAAQ,EAAC,EAEpCS,EAAU,KAAK,CAAC,MAAM,CAACT,EAC/B,CAEA,MAAM,SAAgC,CACrC,IAAM6B,EAAKC,UAAU,IAAI,CAAC,YAAa,GAEvC,OAAO,IAAIC,QAAqB,CAACC,EAASC,KACzCJ,EAAG,SAAS,CAAG,UACd,IAAI,CAAC,EAAE,CAAGA,EAAG,MAAM,CACnB,MAAM,IAAI,CAAC,EAAW,GACtBG,EAAQH,EAAG,MAAM,CAClB,EACAA,EAAG,eAAe,CAAG,KACpB,IAAMK,EAAML,EAAG,MAAM,AACjB,EAACK,EAAI,gBAAgB,CAAC,QAAQ,CAAC,WAClCA,EAAI,iBAAiB,CAAC,UAEnB,CAACA,EAAI,gBAAgB,CAAC,QAAQ,CAAC,YAClCA,EAAI,iBAAiB,CAAC,UAExB,EACAL,EAAG,OAAO,CAAG,IAAMI,EAAOJ,EAAG,KAAK,CACnC,EACD,CAEA,MAAM,EAAW,GAChB,GAAI,CAAC,IAAI,CAAC,EAAE,CAAE,CACbrC,QAAQ,KAAK,CAAC,oBAEd,MACD,CAGA,IAAM2C,EAAMC,AADEC,AADH,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,SAAU,aACxB,WAAW,CAAC,UACX,GAAG,CAAC5B,EAAU,MAAM,CAAE,UAExC,OAAO,IAAIsB,QAAQ,CAACC,EAASC,KAC5BE,EAAI,SAAS,CAAGH,EAChBG,EAAI,OAAO,CAAGF,CACf,EACD,CAEA,MAAM,aAAaf,CAAsB,CAAE,CAC1CT,EAAU,MAAM,CAAGe,OAAO,MAAM,CAAC,CAAC,EAAGf,EAAU,MAAM,CAAES,GACvDH,IAEA,MAAM,IAAI,CAAC,EAAW,EACvB,CACD,C"} -------------------------------------------------------------------------------- /vendor/scramjet/dist/scramjet.sync.js: -------------------------------------------------------------------------------- 1 | addEventListener("message",({data:{sab:e,args:[t,n,s,r,o],body:a,headers:g}})=>{let i=new DataView(e),l=new Uint8Array(e),d=new XMLHttpRequest;if(d.responseType="arraybuffer",d.open(t,n,!0,r,o),g)for(let[e,t]of Object.entries(g))d.setRequestHeader(e,t);d.send(a),d.onload=()=>{let t=1;i.setUint16(t,d.status),t+=2;let n=d.getAllResponseHeaders();i.setUint32(t,n.length),t+=4,e.byteLength{console.error("xhr failed"),i.setUint8(0,1)}}); 2 | //# sourceMappingURL=scramjet.sync.js.map -------------------------------------------------------------------------------- /vendor/scramjet/dist/scramjet.sync.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"scramjet.sync.js","sources":["webpack://@mercuryworkshop/scramjet/./src/sync.ts"],"sourcesContent":["addEventListener(\n\t\"message\",\n\t({\n\t\tdata: {\n\t\t\tsab,\n\t\t\targs: [method, url, _, username, password],\n\t\t\tbody,\n\t\t\theaders,\n\t\t},\n\t}) => {\n\t\tconst view = new DataView(sab);\n\t\tconst u8view = new Uint8Array(sab);\n\n\t\tconst xhr = new XMLHttpRequest();\n\t\txhr.responseType = \"arraybuffer\";\n\n\t\t// force async since we need it to resolve to the sw\n\t\txhr.open(method, url, true, username, password);\n\n\t\tif (headers)\n\t\t\tfor (const [k, v] of Object.entries(headers)) {\n\t\t\t\txhr.setRequestHeader(k, v as string);\n\t\t\t}\n\n\t\txhr.send(body);\n\n\t\txhr.onload = () => {\n\t\t\tlet cursor = 1; // first byte is the lock\n\n\t\t\tview.setUint16(cursor, xhr.status);\n\t\t\tcursor += 2;\n\n\t\t\t// next write the header string\n\t\t\tconst headers = xhr.getAllResponseHeaders();\n\t\t\tview.setUint32(cursor, headers.length);\n\t\t\tcursor += 4;\n\n\t\t\tif (sab.byteLength < cursor + headers.length)\n\t\t\t\tsab.grow(cursor + headers.length);\n\t\t\tu8view.set(new TextEncoder().encode(headers), cursor);\n\t\t\tcursor += headers.length;\n\n\t\t\tview.setUint32(cursor, xhr.response.byteLength);\n\t\t\tcursor += 4;\n\n\t\t\tif (sab.byteLength < cursor + xhr.response.byteLength)\n\t\t\t\tsab.grow(cursor + xhr.response.byteLength);\n\t\t\tu8view.set(new Uint8Array(xhr.response), cursor);\n\n\t\t\t// release the lock, main thread will stop spinning now\n\t\t\tview.setUint8(0, 1);\n\t\t};\n\t\txhr.ontimeout =\n\t\t\txhr.onerror =\n\t\t\txhr.onabort =\n\t\t\t\t() => {\n\t\t\t\t\tconsole.error(\"xhr failed\");\n\t\t\t\t\tview.setUint8(0, 1);\n\t\t\t\t};\n\t}\n);\n"],"names":["addEventListener","sab","method","url","_","username","password","body","headers","view","DataView","u8view","Uint8Array","xhr","XMLHttpRequest","k","v","Object","cursor","TextEncoder","console"],"mappings":"AAAAA,iBACC,UACA,CAAC,CACA,KAAM,CACLC,IAAAA,CAAG,CACH,KAAM,CAACC,EAAQC,EAAKC,EAAGC,EAAUC,EAAS,CAC1CC,KAAAA,CAAI,CACJC,QAAAA,CAAO,CACP,CACD,IACA,IAAMC,EAAO,IAAIC,SAAST,GACpBU,EAAS,IAAIC,WAAWX,GAExBY,EAAM,IAAIC,eAMhB,GALAD,EAAI,YAAY,CAAG,cAGnBA,EAAI,IAAI,CAACX,EAAQC,EAAK,GAAME,EAAUC,GAElCE,EACH,IAAK,GAAM,CAACO,EAAGC,EAAE,GAAIC,OAAO,OAAO,CAACT,GACnCK,EAAI,gBAAgB,CAACE,EAAGC,GAG1BH,EAAI,IAAI,CAACN,GAETM,EAAI,MAAM,CAAG,KACZ,IAAIK,EAAS,EAEbT,EAAK,SAAS,CAACS,EAAQL,EAAI,MAAM,EACjCK,GAAU,EAGV,IAAMV,EAAUK,EAAI,qBAAqB,GACzCJ,EAAK,SAAS,CAACS,EAAQV,EAAQ,MAAM,EACrCU,GAAU,EAENjB,EAAI,UAAU,CAAGiB,EAASV,EAAQ,MAAM,EAC3CP,EAAI,IAAI,CAACiB,EAASV,EAAQ,MAAM,EACjCG,EAAO,GAAG,CAAC,IAAIQ,cAAc,MAAM,CAACX,GAAUU,GAC9CA,GAAUV,EAAQ,MAAM,CAExBC,EAAK,SAAS,CAACS,EAAQL,EAAI,QAAQ,CAAC,UAAU,EAC9CK,GAAU,EAENjB,EAAI,UAAU,CAAGiB,EAASL,EAAI,QAAQ,CAAC,UAAU,EACpDZ,EAAI,IAAI,CAACiB,EAASL,EAAI,QAAQ,CAAC,UAAU,EAC1CF,EAAO,GAAG,CAAC,IAAIC,WAAWC,EAAI,QAAQ,EAAGK,GAGzCT,EAAK,QAAQ,CAAC,EAAG,EAClB,EACAI,EAAI,SAAS,CACZA,EAAI,OAAO,CACXA,EAAI,OAAO,CACV,KACCO,QAAQ,KAAK,CAAC,cACdX,EAAK,QAAQ,CAAC,EAAG,EAClB,CACH"} -------------------------------------------------------------------------------- /vendor/scramjet/dist/scramjet.worker.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var e={1762:function(e,t,r){r.d(t,{Z:function(){return o}});let o={fmt:function(e,t,...r){let o=Error.prepareStackTrace;Error.prepareStackTrace=(e,t)=>{t.shift(),t.shift(),t.shift();let r="";for(let e=1;e `+r);return r+=t[0].getFunctionName()||"Anonymous"};let s=function(){try{throw Error()}catch(e){return e.stack}}();Error.prepareStackTrace=o;let n=console[e]||console.log;n(`%c${s}%c ${t}`,` 2 | background-color: ${{log:"#000",warn:"#f80",error:"#f00",debug:"transparent"}[e]}; 3 | color: ${{log:"#fff",warn:"#fff",error:"#fff",debug:"gray"}[e]}; 4 | padding: ${{log:2,warn:4,error:4,debug:0}[e]}px; 5 | font-weight: bold; 6 | font-family: monospace; 7 | font-size: 0.9em; 8 | `,`${"debug"===e?"color: gray":""}`,...r)},log:function(e,...t){this.fmt("log",e,...t)},warn:function(e,...t){this.fmt("warn",e,...t)},error:function(e,...t){this.fmt("error",e,...t)},debug:function(e,...t){this.fmt("debug",e,...t)}}}},t={};function r(o){var s=t[o];if(void 0!==s)return s.exports;var n=t[o]={exports:{}};return e[o](n,n.exports,r),n.exports}r.d=function(e,t){for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};class o{handle;origin;syncToken;promises;messageChannel;connected;constructor(e,t){this.handle=e,this.origin=t,this.syncToken=0,this.promises={},this.messageChannel=new MessageChannel,this.connected=!1,this.messageChannel.port1.addEventListener("message",e=>{"scramjet$type"in e.data&&("init"===e.data.scramjet$type?this.connected=!0:this.handleMessage(e.data))}),this.messageChannel.port1.start(),this.handle.postMessage({scramjet$type:"init",scramjet$port:this.messageChannel.port2},[this.messageChannel.port2])}handleMessage(e){let t=this.promises[e.scramjet$token];t&&(t(e),delete this.promises[e.scramjet$token])}async fetch(e){let t=this.syncToken++,r={scramjet$type:"fetch",scramjet$token:t,scramjet$request:{url:e.url,body:e.body,headers:Array.from(e.headers.entries()),method:e.method,mode:e.mode,destinitation:e.destination}},o=e.body?[e.body]:[];this.handle.postMessage(r,o);let{scramjet$response:s}=await new Promise(e=>{this.promises[t]=e});return!!s&&new Response(s.body,{headers:s.headers,status:s.status,statusText:s.statusText})}}!("$scramjet"in self)&&(self.$scramjet={version:{build:"1efcf85",version:"1.0.2-dev"},codec:{},flagEnabled:function(e,t){let r=s.config.flags[e];for(let r in s.config.siteFlags){let o=s.config.siteFlags[r];if(new RegExp(r).test(t.href)&&e in o)return o[e]}return r}});let s=self.$scramjet,n=Function,{util:{BareClient:i,ScramjetHeaders:a,BareMuxConnection:c},url:{rewriteUrl:l,unrewriteUrl:d,rewriteBlob:u,unrewriteBlob:h},rewrite:{rewriteCss:p,unrewriteCss:f,rewriteHtml:m,unrewriteHtml:g,rewriteSrcset:y,rewriteJs:b,rewriteHeaders:w,rewriteWorkers:v,htmlRules:k},CookieStore:x}=s.shared;function R(e){return{origin:e,base:e}}async function S(e,t){let r=new URLSearchParams(new URL(e.url).search);if(r.has("url"))return Response.redirect(l(r.get("url"),R(new URL(r.get("url")))));try{let o=new URL(e.url),n="";if(o.searchParams.has("type")&&(n=o.searchParams.get("type"),o.searchParams.delete("type")),o.searchParams.has("dest")&&o.searchParams.delete("dest"),o.pathname.startsWith(this.config.prefix+"blob:")||o.pathname.startsWith(this.config.prefix+"data:")){let r,s=o.pathname.substring(this.config.prefix.length);s.startsWith("blob:")&&(s=h(s));let i=await fetch(s,{}),a=s.startsWith("blob:")?s:"(data url)";i.finalURL=a,i.body&&(r=await $(i,t?{base:new URL(new URL(t.url).origin),origin:new URL(new URL(t.url).origin)}:R(new URL(d(e.referrer))),e.destination,n,this.cookieStore));let c=Object.fromEntries(i.headers.entries());return crossOriginIsolated&&(c["Cross-Origin-Opener-Policy"]="same-origin",c["Cross-Origin-Embedder-Policy"]="require-corp"),new Response(r,{status:i.status,statusText:i.statusText,headers:c})}let i=new URL(d(o)),c=this.serviceWorkers.find(e=>e.origin===i.origin);if(c&&c.connected&&"swruntime"!==r.get("from")){let t=await c.fetch(e);if(t)return t}if(i.origin==new URL(e.url).origin)throw Error("attempted to fetch from same origin - this means the site has obtained a reference to the real origin, aborting");let l=new a;for(let[t,r]of e.headers.entries())l.set(t,r);if(t&&new URL(t.url).pathname.startsWith(s.config.prefix)){let e=new URL(d(t.url));e.toString().includes("youtube.com")||(l.set("Referer",e.toString()),l.set("Origin",e.origin?`${e.protocol}//${e.host}`:"null"))}let u=this.cookieStore.getCookies(i,!1);u.length&&l.set("Cookie",u),l.set("Sec-Fetch-Dest",e.destination),l.set("Sec-Fetch-Site","same-origin"),l.set("Sec-Fetch-Mode","cors"===e.mode?e.mode:"same-origin");let p=new E(i,e.body,e.method,e.destination,t,l.headers);this.dispatchEvent(p);let f=p.response||await this.client.fetch(p.url,{method:p.method,body:p.body,headers:p.requestHeaders,credentials:"omit",mode:"cors"===e.mode?e.mode:"same-origin",cache:e.cache,redirect:"manual",duplex:"half"});return await C(i,n,e.destination,f,this.cookieStore,t,this)}catch(r){let t={message:r.message,url:e.url,destination:e.destination,timestamp:new Date().toISOString()};if(r.stack&&(t.stack=r.stack),console.error("ERROR FROM SERVICE WORKER FETCH: ",t),!["document","iframe"].includes(e.destination))return new Response(void 0,{status:500});return function(e,t){let r={"content-type":"text/html"};return crossOriginIsolated&&(r["Cross-Origin-Embedder-Policy"]="require-corp"),new Response(function(e,t){let r=` 9 | errorTrace.value = ${JSON.stringify(e)}; 10 | fetchedURL.textContent = ${JSON.stringify(t)}; 11 | for (const node of document.querySelectorAll("#hostname")) node.textContent = ${JSON.stringify(location.hostname)}; 12 | reload.addEventListener("click", () => location.reload()); 13 | version.textContent = ${JSON.stringify(s.version.version)}; 14 | build.textContent = ${JSON.stringify(s.version.build)}; 15 | 16 | document.getElementById('copy-button').addEventListener('click', async () => { 17 | const text = document.getElementById('errorTrace').value; 18 | await navigator.clipboard.writeText(text); 19 | const btn = document.getElementById('copy-button'); 20 | btn.textContent = 'Copied!'; 21 | setTimeout(() => btn.textContent = 'Copy', 2000); 22 | }); 23 | `;return` 24 | 25 | 26 | 27 | Scramjet 28 | 141 | 142 | 143 |
144 |
145 |

Uh oh!

146 |

There was an error loading

147 | 148 | 149 |
150 |
151 | 152 | 153 |
154 |
155 |

Try:

156 |
    157 |
  • Checking your internet connection
  • 158 |
  • Verifying you entered the correct address
  • 159 |
  • Clearing the site data
  • 160 |
  • Contacting 's administrator
  • 161 |
  • Verify the server isn't censored
  • 162 |
163 |

If you're the administrator of , try:

164 |
    165 |
  • Restarting your server
  • 166 |
  • Updating Scramjet
  • 167 |
  • Troubleshooting the error on the GitHub repository
  • 168 |
169 |
170 |
171 |
172 | 173 |
174 |

Scramjet v (build )

175 | 176 | 177 | 178 | `}(String(e),t),{status:500,headers:r})}(Object.entries(t).map(([e,t])=>`${e.charAt(0).toUpperCase()+e.slice(1)}: ${t}`).join("\n\n"),d(e.url))}}async function C(e,t,r,o,s,n,i){let a;let c=w(o.rawHeaders,R(e)),l=c["set-cookie"]||[];for(let t in l)n&&n.postMessage({scramjet$type:"cookie",cookie:t,url:e.href});for(let t in await s.setCookies(l instanceof Array?l:[l],e),c)Array.isArray(c[t])&&(c[t]=c[t][0]);if(o.body&&(a=await $(o,R(e),r,t,s)),["document","iframe"].includes(r)){let e=c["content-disposition"];if(!/\s*?((inline|attachment);\s*?)filename=/i.test(e)){let t=/^\s*?attachment/i.test(e)?"attachment":"inline",[r]=new URL(o.finalURL).pathname.split("/").slice(-1);c["content-disposition"]=`${t}; filename=${JSON.stringify(r)}`}}"text/event-stream"===c.accept&&(c["content-type"]="text/event-stream"),delete c["permissions-policy"],crossOriginIsolated&&["document","iframe","worker","sharedworker","style","script"].includes(r)&&(c["Cross-Origin-Embedder-Policy"]="require-corp",c["Cross-Origin-Opener-Policy"]="same-origin");let d=new j(a,c,o.status,o.statusText,r,e,o,n);return i.dispatchEvent(d),new Response(d.responseBody,{headers:d.responseHeaders,status:d.status,statusText:d.statusText})}async function $(e,t,r,o,s){switch(r){case"iframe":case"document":if(e.headers.get("content-type")?.startsWith("text/html"))return m(await e.text(),s,t,!0);return e.body;case"script":return b(await e.arrayBuffer(),e.finalURL,t);case"style":return p(await e.text(),t);case"sharedworker":case"worker":return v(await e.arrayBuffer(),o,e.finalURL,t);default:return e.body}}s.config;class j extends Event{responseBody;responseHeaders;status;statusText;destination;url;rawResponse;client;constructor(e,t,r,o,s,n,i,a){super("handleResponse"),this.responseBody=e,this.responseHeaders=t,this.status=r,this.statusText=o,this.destination=s,this.url=n,this.rawResponse=i,this.client=a}}class E extends Event{url;body;method;destination;client;requestHeaders;constructor(e,t,r,o,s,n){super("request"),this.url=e,this.body=t,this.method=r,this.destination=o,this.client=s,this.requestHeaders=n}response}var T=r(1762).Z;class O extends EventTarget{client;config;syncPool={};synctoken=0;cookieStore=new s.shared.CookieStore;serviceWorkers=[];constructor(){super(),this.client=new s.shared.util.BareClient;let e=indexedDB.open("$scramjet",1);e.onsuccess=()=>{let t=e.result.transaction("cookies","readonly").objectStore("cookies").get("cookies");t.onsuccess=()=>{t.result&&(this.cookieStore.load(t.result),T.log("Loaded cookies from IDB!"))}},addEventListener("message",async({data:t})=>{if("scramjet$type"in t){if("registerServiceWorker"===t.scramjet$type){this.serviceWorkers.push(new o(t.port,t.origin));return}"cookie"===t.scramjet$type&&(this.cookieStore.setCookies([t.cookie],new URL(t.url)),e.result.transaction("cookies","readwrite").objectStore("cookies").put(JSON.parse(this.cookieStore.dump()),"cookies"))}})}async loadConfig(){if(this.config)return;let e=indexedDB.open("$scramjet",1);return new Promise((t,r)=>{e.onsuccess=async()=>{let o=e.result.transaction("config","readonly").objectStore("config").get("config");o.onsuccess=()=>{this.config=o.result,s.config=o.result,s.codec.encode=n("url",s.config.codec.encode),s.codec.decode=n("url",s.config.codec.decode),t()},o.onerror=()=>r(o.error)},e.onerror=()=>r(e.error)})}route({request:e}){return!!e.url.startsWith(location.origin+this.config.prefix)||!1}async fetch({request:e,clientId:t}){let r=await self.clients.get(t);return S.call(this,e,r)}}self.ScramjetServiceWorker=O})(); 179 | //# sourceMappingURL=scramjet.worker.js.map -------------------------------------------------------------------------------- /vendor/scramjet/lib/index.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { resolve } = require("node:path"); 4 | 5 | const scramjetPath = resolve(__dirname, "..", "dist"); 6 | 7 | exports.scramjetPath = scramjetPath; 8 | -------------------------------------------------------------------------------- /vendor/scramjet/lib/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const scramjetPath: string; 2 | 3 | export { scramjetPath }; 4 | -------------------------------------------------------------------------------- /vendor/scramjet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mercuryworkshop/scramjet", 3 | "version": "1.0.2-dev", 4 | "description": "An experimental web proxy that aims to be the successor to Ultraviolet", 5 | "main": "./lib/index.cjs", 6 | "types": "./lib/index.d.js", 7 | "type": "module", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/MercuryWorkshop/scramjet" 11 | }, 12 | "files": [ 13 | "dist/*.js", 14 | "dist/*.js.map", 15 | "lib" 16 | ], 17 | "keywords": [], 18 | "author": "", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "@eslint/eslintrc": "^3.2.0", 22 | "@eslint/js": "^9.17.0", 23 | "@estruyf/github-actions-reporter": "^1.9.2", 24 | "@fastify/static": "^8.0.3", 25 | "@mercuryworkshop/bare-as-module3": "^2.2.5", 26 | "@mercuryworkshop/epoxy-transport": "^2.1.27", 27 | "@mercuryworkshop/libcurl-transport": "^1.3.14", 28 | "@nebula-services/bare-server-node": "^2.0.4", 29 | "@playwright/test": "^1.49.1", 30 | "@rsdoctor/rspack-plugin": "^0.4.11", 31 | "@rspack/cli": "^1.1.6", 32 | "@rspack/core": "^1.1.6", 33 | "@types/eslint": "^9.6.1", 34 | "@types/estree": "^1.0.6", 35 | "@types/node": "^22.10.2", 36 | "@types/serviceworker": "^0.0.107", 37 | "@typescript-eslint/eslint-plugin": "^8.18.0", 38 | "@typescript-eslint/parser": "^8.18.0", 39 | "dotenv": "^16.4.7", 40 | "eslint": "^9.17.0", 41 | "fastify": "^5.1.0", 42 | "playwright": "^1.49.1", 43 | "prettier": "^3.4.2", 44 | "tslib": "^2.8.1", 45 | "typescript": "^5.7.2", 46 | "wisp-server-node": "^1.1.7" 47 | }, 48 | "dependencies": { 49 | "@mercuryworkshop/bare-mux": "^2.1.7", 50 | "dom-serializer": "^2.0.0", 51 | "domhandler": "^5.0.3", 52 | "domutils": "^3.1.0", 53 | "htmlparser2": "^9.1.0", 54 | "parse-domain": "^8.2.2", 55 | "set-cookie-parser": "^2.7.1" 56 | }, 57 | "scripts": { 58 | "build": "rspack build --mode production", 59 | "rewriter:build": "cd rewriter/wasm/ && bash build.sh && cd ../../", 60 | "dev": "node server.js", 61 | "pub": "npm publish --no-git-checks --access public", 62 | "format": "prettier --config .prettierrc.js --write .", 63 | "lint": "eslint ./src/", 64 | "lint:fix": "eslint ./src/ --fix", 65 | "test": "npx playwright test" 66 | } 67 | } --------------------------------------------------------------------------------