├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── action.yaml ├── build.ts ├── inject.ts ├── package.json ├── src ├── installer.ts └── service-worker.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "printWidth": 120, 4 | "arrowParens": "avoid", 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Menci 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Service Worker to Redirect Origin 2 | 3 | This is a tool for your static website which could intercept all `GET` requests of the origin domain and redirect them to a given base-URL. Such as `/index.html` to `https://cdn.example.com/index.html`. 4 | 5 | It's useful when your origin domain have no ICP license but you want to optimize mainland China routing. 6 | 7 | # Usage 8 | 9 | ## GitHub Action 10 | 11 | ```yaml 12 | - name: Inject Service Worker 13 | uses: Menci/service-worker-redirect-origin@beta-v2 14 | with: 15 | # The directory containing your built static website files. 16 | www-root: public 17 | 18 | # The target base-URL to redirect to. 19 | target-base-url: https://cdn.example.com/ # Remember to end with a "/" 20 | 21 | # If https://cdn.example.com/ responds with 404, it's will be fetched. 22 | # Omit to fallback to origin. 23 | http-404-page: 404.html 24 | 25 | # The script filename of service worker. Will be written to the `www-root` directory. 26 | # By default `sw.js`. 27 | service-worker-filename: sw.js 28 | ``` 29 | 30 | ## Node.js 31 | 32 | 33 | ```bash 34 | $ yarn inject [404Page] [serviceWorkerFilename] 35 | ``` 36 | 37 | See the explanation for each arguments above in GitHub Action usage. 38 | 39 | # Limitations 40 | 41 | It will replace your existing Service Worker in your site (if any). 42 | -------------------------------------------------------------------------------- /action.yaml: -------------------------------------------------------------------------------- 1 | name: Inject redirect-origin Service Worker to static website 2 | description: Redirect requests of origin to another given base URL. 3 | branding: 4 | icon: corner-up-right 5 | color: purple 6 | inputs: 7 | www-root: 8 | description: The directory containing the files of your static website. 9 | required: true 10 | target-base-url: 11 | description: The target base url you want to redirect your origin requests to. Should end with a "/". 12 | required: true 13 | http-404-page: 14 | description: The page to fetch when the redirected request responds with HTTP 404. By default fallback to origin. 15 | default: "" 16 | service-worker-filename: 17 | description: The script filename of service worker. Will be written to the `www-root` directory. By default `sw.js`. 18 | default: sw.js 19 | runs: 20 | using: composite 21 | steps: 22 | - shell: bash 23 | run: | 24 | echo "::group::Resolve www root full path" 25 | 26 | # Since we'll run `cd` 27 | WWW_ROOT="$(realpath "$WWW_ROOT")" 28 | echo "$WWW_ROOT" 29 | 30 | echo "::endgroup" 31 | 32 | echo "::group::Install dependencies" 33 | 34 | cd "$ACTION_PATH" 35 | yarn 36 | 37 | echo "::endgroup" 38 | 39 | echo "::group::Run inject" 40 | 41 | yarn inject "$WWW_ROOT" "$TARGET_BASE_URL" "$HTTP_404_PAGE" "$SERVICE_WORKER_FILENAME" 42 | 43 | echo "::endgroup" 44 | env: 45 | ACTION_PATH: ${{ github.action_path }} 46 | WWW_ROOT: ${{ inputs.www-root }} 47 | TARGET_BASE_URL: ${{ inputs.target-base-url }} 48 | HTTP_404_PAGE: ${{ inputs.http-404-page }} 49 | SERVICE_WORKER_FILENAME: ${{ inputs.service-worker-filename }} 50 | -------------------------------------------------------------------------------- /build.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs"; 3 | 4 | import * as esbuild from "esbuild"; 5 | import terser from "terser"; 6 | 7 | export const buildPromise = (async () => { 8 | const distDir = path.resolve(__dirname, "dist"); 9 | fs.rmSync(distDir, { recursive: true, force: true }); 10 | fs.mkdirSync(distDir); 11 | 12 | const result = await esbuild.build({ 13 | entryPoints: ["installer.ts", "service-worker.ts"].map(file => path.resolve(__dirname, "src", file)), 14 | bundle: true, 15 | platform: "node", 16 | outdir: distDir, 17 | logLevel: "info", 18 | write: false 19 | }); 20 | 21 | for (const outputFile of result.outputFiles) { 22 | const minifyOutput = await terser.minify(outputFile.text, { toplevel: true }); 23 | fs.writeFileSync(outputFile.path, minifyOutput.code); 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /inject.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | import { parse } from "node-html-parser"; 5 | import terser from "terser"; 6 | import klaw from "klaw"; 7 | 8 | import { buildPromise } from "./build"; 9 | 10 | const wwwRoot = process.argv[2]; 11 | const targetBaseUrl = process.argv[3]; 12 | const _404Page = process.argv[4] || ""; 13 | const serviceWorkerFilename = process.argv[5] || "sw.js"; 14 | 15 | if (!wwwRoot || !targetBaseUrl) { 16 | console.error("Usage: yarn inject [404Page] [serviceWorkerFilename]"); 17 | process.exit(1); 18 | } 19 | 20 | function isHtmlFilePath(filePath: string) { 21 | const normalizedFilePath = filePath.toLowerCase(); 22 | return normalizedFilePath.endsWith(".html") || normalizedFilePath.endsWith(".htm"); 23 | } 24 | 25 | (async () => { 26 | // Wait for build 27 | await buildPromise; 28 | 29 | // Prepare installer script 30 | 31 | const installerTemplate = fs.readFileSync(path.resolve(__dirname, "dist/installer.js"), "utf-8"); 32 | 33 | const replacedInstaller = installerTemplate 34 | .split("__service_worker__") 35 | .join(JSON.stringify(serviceWorkerFilename)) 36 | .split("__target__") 37 | .join(JSON.stringify(encodeURIComponent(targetBaseUrl))) 38 | .split("__404_page__") 39 | .join(JSON.stringify(encodeURIComponent(_404Page))); 40 | const installer = (await terser.minify(replacedInstaller, { toplevel: true })).code; 41 | 42 | // Write service worker JS file 43 | 44 | const serviceWorkerJs = path.resolve(__dirname, "dist/service-worker.js"); 45 | await fs.promises.copyFile(serviceWorkerJs, path.resolve(wwwRoot, serviceWorkerFilename)); 46 | 47 | // Inject installer script to HTML files 48 | 49 | async function processFile(filePath: string) { 50 | const html = parse(await fs.promises.readFile(filePath, "utf-8")); 51 | 52 | const body = html.querySelector("body"); 53 | if (!body) return; 54 | 55 | body.insertAdjacentHTML("beforeend", ``); 56 | await fs.promises.writeFile(filePath, html.outerHTML); 57 | } 58 | 59 | klaw(wwwRoot) 60 | .on("data", async file => { 61 | if (!(file.stats.isFile() && !file.stats.isSymbolicLink() && isHtmlFilePath(file.path))) return; 62 | 63 | console.log(`Processing file ${JSON.stringify(file.path)}`); 64 | await processFile(file.path); 65 | }) 66 | .on("error", err => { 67 | console.error("Error from klaw():", err.stack); 68 | process.exit(1); 69 | }); 70 | })(); 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "service-worker-redirect-origin", 3 | "version": "1.0.0", 4 | "main": "build.ts", 5 | "repository": "https://github.com/Menci/service-worker-redirect-origin.git", 6 | "author": "Menci ", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "esr build.ts", 10 | "inject": "esr inject.ts", 11 | "format": "prettier --write \"**/*.ts\"" 12 | }, 13 | "devDependencies": { 14 | "@types/klaw": "^3.0.3", 15 | "@types/node": "16", 16 | "esbuild": "^0.14.10", 17 | "esbuild-runner": "^2.2.1", 18 | "prettier": "^2.5.1", 19 | "terser": "^5.10.0" 20 | }, 21 | "dependencies": { 22 | "klaw": "^4.0.1", 23 | "node-html-parser": "^5.2.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/installer.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // The keyword __service_worker__ and __target__ will be replaced 6 | declare var __service_worker__: string; 7 | declare var __target__: string; 8 | declare var __404_page__: string; 9 | 10 | if ("serviceWorker" in navigator) { 11 | window.addEventListener("load", function () { 12 | if (__404_page__) 13 | navigator.serviceWorker.register("/" + __service_worker__ + "?t=" + __target__ + "&404=" + __404_page__); 14 | else navigator.serviceWorker.register("/" + __service_worker__ + "?t=" + __target__); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /src/service-worker.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | const sw = self as unknown as ServiceWorkerGlobalScope & typeof globalThis; 6 | 7 | const query = new URL(sw.location.href).searchParams; 8 | const targetBaseUrl = query.get("t"); 9 | const _404Page = query.get("404"); 10 | 11 | if (!Promise.any) { 12 | Promise.any = (promises: Iterable>): Promise> => { 13 | return Promise.all( 14 | [...promises].map(promise => { 15 | return new Promise((resolve, reject) => 16 | Promise.resolve(promise) 17 | // When a promise fulfilled, we call reject to bail out Promise.all 18 | // When a promise rejected, we call resolve to continue Promise.all 19 | .then(reject, resolve) 20 | ); 21 | }) 22 | ).then( 23 | // The resolved are actually aggregated errors 24 | errors => Promise.reject(errors), 25 | // The reject is the first fulfilled promise (which causes the bail out) 26 | fastest => Promise.resolve>(fastest) 27 | ); 28 | }; 29 | } 30 | 31 | sw.addEventListener("install", event => { 32 | event.waitUntil(sw.skipWaiting()); 33 | }); 34 | 35 | sw.addEventListener("fetch", event => { 36 | const url = new URL(event.request.url); 37 | if (event.request.method !== "GET" || url.origin !== sw.origin) { 38 | return; 39 | } 40 | 41 | const abortEvent = new Event("abortFetch"); 42 | const eventTarget = new EventTarget(); 43 | const withAbort = Promise>( 44 | fetchWithSignal: (signal: AbortSignal) => F 45 | ): ((...args: Parameters) => Promise) => { 46 | // Abort other doFetch()-es when the first doFetch() resolved with true 47 | const abortController = typeof AbortController === "function" && new AbortController(); 48 | 49 | // When the abort event triggered, don't abort the current fetch() if `fetchSucceed` is true 50 | let fetchSucceed = false; 51 | if (abortController) { 52 | eventTarget.addEventListener(abortEvent.type, () => { 53 | if (!fetchSucceed) abortController.abort(); 54 | }); 55 | } 56 | 57 | const doFetch = fetchWithSignal(abortController ? abortController.signal : undefined); 58 | return async (...args: Parameters) => { 59 | const response = await doFetch(...args); 60 | if (response) { 61 | // Abort other fetch()-es 62 | fetchSucceed = true; 63 | eventTarget.dispatchEvent(abortEvent); 64 | return response; 65 | } 66 | }; 67 | }; 68 | 69 | const fetchOrigin = withAbort(signal => async () => { 70 | const resp = await fetch(event.request, { signal }); 71 | return resp; 72 | }); 73 | const fetchRedirected = withAbort(signal => async () => { 74 | const newUrl = 75 | targetBaseUrl + 76 | url.pathname.slice(1) + // Remove leading "/" 77 | url.search; 78 | 79 | // Handle redirects like "https://cdn/path" to "https://cdn/path/" 80 | // NOTE: or return a transformed redirect response? 81 | const fetchOptions: RequestInit = { 82 | redirect: "follow", 83 | signal 84 | }; 85 | 86 | let response = await fetch(newUrl, fetchOptions); 87 | 88 | // Handle 404 for static sites 89 | if (response.status === 404 && _404Page) { 90 | response = await fetch(targetBaseUrl + _404Page, fetchOptions); 91 | } 92 | 93 | if (!response.ok) { 94 | // Oops! the service worker CDN may not available now 95 | // Fallback to the original URL 96 | 97 | // This error won't be used, just to indicate the fetch failed 98 | throw null; 99 | } 100 | 101 | return response; 102 | }); 103 | 104 | async function postProcessResponse(response: Response) { 105 | if (event.request.mode === "same-origin") { 106 | return new Response(response.body, { 107 | headers: response.headers, 108 | status: response.status, 109 | statusText: response.statusText 110 | }); 111 | } 112 | return response; 113 | } 114 | 115 | event.respondWith(Promise.any([fetchOrigin(), fetchRedirected()]).then(postProcessResponse)); 116 | }); 117 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "skipLibCheck": true, 5 | "esModuleInterop": true, 6 | "target": "es2019", 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "baseUrl": "./", 10 | "strict": false 11 | }, 12 | "exclude": ["node_modules", "dist"] 13 | } 14 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/klaw@^3.0.3": 6 | version "3.0.3" 7 | resolved "https://registry.yarnpkg.com/@types/klaw/-/klaw-3.0.3.tgz#68c02ecfe6b313ef5b93876d44ac5266808c6a01" 8 | integrity sha512-mXlRDFbTLpVysvxahXUQav0hFctgu3Fqr2xmSrpf/ptO/FwOp7SFEGsJkEihwshMbof3/BIiVJ/o42cuOOuv6g== 9 | dependencies: 10 | "@types/node" "*" 11 | 12 | "@types/node@*": 13 | version "17.0.8" 14 | resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b" 15 | integrity sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg== 16 | 17 | "@types/node@16": 18 | version "16.11.19" 19 | resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.19.tgz#1afa165146997b8286b6eabcb1c2d50729055169" 20 | integrity sha512-BPAcfDPoHlRQNKktbsbnpACGdypPFBuX4xQlsWDE7B8XXcfII+SpOLay3/qZmCLb39kV5S1RTYwXdkx2lwLYng== 21 | 22 | boolbase@^1.0.0: 23 | version "1.0.0" 24 | resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" 25 | integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= 26 | 27 | buffer-from@^1.0.0: 28 | version "1.1.2" 29 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" 30 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== 31 | 32 | commander@^2.20.0: 33 | version "2.20.3" 34 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" 35 | integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 36 | 37 | css-select@^4.1.3: 38 | version "4.2.1" 39 | resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd" 40 | integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ== 41 | dependencies: 42 | boolbase "^1.0.0" 43 | css-what "^5.1.0" 44 | domhandler "^4.3.0" 45 | domutils "^2.8.0" 46 | nth-check "^2.0.1" 47 | 48 | css-what@^5.1.0: 49 | version "5.1.0" 50 | resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" 51 | integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== 52 | 53 | dom-serializer@^1.0.1: 54 | version "1.3.2" 55 | resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" 56 | integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== 57 | dependencies: 58 | domelementtype "^2.0.1" 59 | domhandler "^4.2.0" 60 | entities "^2.0.0" 61 | 62 | domelementtype@^2.0.1, domelementtype@^2.2.0: 63 | version "2.2.0" 64 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" 65 | integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== 66 | 67 | domhandler@^4.2.0, domhandler@^4.3.0: 68 | version "4.3.0" 69 | resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" 70 | integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== 71 | dependencies: 72 | domelementtype "^2.2.0" 73 | 74 | domutils@^2.8.0: 75 | version "2.8.0" 76 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" 77 | integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== 78 | dependencies: 79 | dom-serializer "^1.0.1" 80 | domelementtype "^2.2.0" 81 | domhandler "^4.2.0" 82 | 83 | entities@^2.0.0: 84 | version "2.2.0" 85 | resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" 86 | integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== 87 | 88 | esbuild-android-arm64@0.14.10: 89 | version "0.14.10" 90 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.10.tgz#c854db57dc2d4df6f4f62185ca812f26a132bf1e" 91 | integrity sha512-vzkTafHKoiMX4uIN1kBnE/HXYLpNT95EgGanVk6DHGeYgDolU0NBxjO7yZpq4ZGFPOx8384eAdDrBYhO11TAlQ== 92 | 93 | esbuild-darwin-64@0.14.10: 94 | version "0.14.10" 95 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.10.tgz#c44fab6b8bfc83e5d083f513e4acbff14fb82eac" 96 | integrity sha512-DJwzFVB95ZV7C3PQbf052WqaUuuMFXJeZJ0LKdnP1w+QOU0rlbKfX0tzuhoS//rOXUj1TFIwRuRsd0FX6skR7A== 97 | 98 | esbuild-darwin-arm64@0.14.10: 99 | version "0.14.10" 100 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.10.tgz#9454b3763b36407dc395c4c3529fb5ddd4a6225f" 101 | integrity sha512-RNaaoZDg3nsqs5z56vYCjk/VJ76npf752W0rOaCl5lO5TsgV9zecfdYgt7dtUrIx8b7APhVaNYud+tGsDOVC9g== 102 | 103 | esbuild-freebsd-64@0.14.10: 104 | version "0.14.10" 105 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.10.tgz#04eef46d5d5e4152c6b5a6a12f432db0fe7c89de" 106 | integrity sha512-10B3AzW894u6bGZZhWiJOHw1uEHb4AFbUuBdyml1Ht0vIqd+KqWW+iY/yMwQAzILr2WJZqEhbOXRkJtY8aRqOw== 107 | 108 | esbuild-freebsd-arm64@0.14.10: 109 | version "0.14.10" 110 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.10.tgz#67ca88529543ada948737c95819253ead16494a7" 111 | integrity sha512-mSQrKB7UaWvuryBTCo9leOfY2uEUSimAvcKIaUWbk5Hth9Sg+Try+qNA/NibPgs/vHkX0KFo/Rce6RPea+P15g== 112 | 113 | esbuild-linux-32@0.14.10: 114 | version "0.14.10" 115 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.10.tgz#8f3d5fb0b9b616d6b604da781d71767d7679f64f" 116 | integrity sha512-lktF09JgJLZ63ANYHIPdYe339PDuVn19Q/FcGKkXWf+jSPkn5xkYzAabboNGZNUgNqSJ/vY7VrOn6UrBbJjgYA== 117 | 118 | esbuild-linux-64@0.14.10: 119 | version "0.14.10" 120 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.10.tgz#c1c60a079c4709164bdd89fbb007a2edeea7c34a" 121 | integrity sha512-K+gCQz2oLIIBI8ZM77e9sYD5/DwEpeYCrOQ2SYXx+R4OU2CT9QjJDi4/OpE7ko4AcYMlMW7qrOCuLSgAlEj4Wg== 122 | 123 | esbuild-linux-arm64@0.14.10: 124 | version "0.14.10" 125 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.10.tgz#d8f1f89190f6d8b6e06a1214aafba454e5daa990" 126 | integrity sha512-+qocQuQvcp5wo/V+OLXxqHPc+gxHttJEvbU/xrCGE03vIMqraL4wMua8JQx0SWEnJCWP+Nhf//v8OSwz1Xr5kA== 127 | 128 | esbuild-linux-arm@0.14.10: 129 | version "0.14.10" 130 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.10.tgz#43192a00019a4553fb44e67f628fff0f560f16c2" 131 | integrity sha512-BYa60dZ/KPmNKYxtHa3LSEdfKWHcm/RzP0MjB4AeBPhjS0D6/okhaBesZIY9kVIGDyeenKsJNOmnVt4+dhNnvQ== 132 | 133 | esbuild-linux-mips64le@0.14.10: 134 | version "0.14.10" 135 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.10.tgz#f57bb8b2f1a3063cc91cfd787c8a9130cf863c16" 136 | integrity sha512-nmUd2xoBXpGo4NJCEWoaBj+n4EtDoLEvEYc8Z3aSJrY0Oa6s04czD1flmhd0I/d6QEU8b7GQ9U0g/rtBfhtxBg== 137 | 138 | esbuild-linux-ppc64le@0.14.10: 139 | version "0.14.10" 140 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.10.tgz#becd965bfe3425d41e026f1c4678b393127fecbd" 141 | integrity sha512-vsOWZjm0rZix7HSmqwPph9arRVCyPtUpcURdayQDuIhMG2/UxJxpbdRaa//w4zYqcJzAWwuyH2PAlyy0ZNuxqQ== 142 | 143 | esbuild-linux-s390x@0.14.10: 144 | version "0.14.10" 145 | resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.10.tgz#cc4228ac842febc48b84757814bed964a619be62" 146 | integrity sha512-knArKKZm0ypIYWOWyOT7+accVwbVV1LZnl2FWWy05u9Tyv5oqJ2F5+X2Vqe/gqd61enJXQWqoufXopvG3zULOg== 147 | 148 | esbuild-netbsd-64@0.14.10: 149 | version "0.14.10" 150 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.10.tgz#6ec50d9e4547a7579f447307b19f66bbedfd868b" 151 | integrity sha512-6Gg8neVcLeyq0yt9bZpReb8ntZ8LBEjthxrcYWVrBElcltnDjIy1hrzsujt0+sC2rL+TlSsE9dzgyuvlDdPp2w== 152 | 153 | esbuild-openbsd-64@0.14.10: 154 | version "0.14.10" 155 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.10.tgz#925ac3d2326cc219d514e1ca806e80e5143aa043" 156 | integrity sha512-9rkHZzp10zI90CfKbFrwmQjqZaeDmyQ6s9/hvCwRkbOCHuto6RvMYH9ghQpcr5cUxD5OQIA+sHXi0zokRNXjcg== 157 | 158 | esbuild-runner@^2.2.1: 159 | version "2.2.1" 160 | resolved "https://registry.yarnpkg.com/esbuild-runner/-/esbuild-runner-2.2.1.tgz#3866fca62cbf9645e939b43a0c65446f9a53064f" 161 | integrity sha512-VP0VfJJZiZ3cKzdOH59ZceDxx/GzBKra7tiGM8MfFMLv6CR1/cpsvtQ3IsJI3pz7HyeYxtbPyecj3fHwR+3XcQ== 162 | dependencies: 163 | source-map-support "0.5.19" 164 | tslib "2.3.1" 165 | 166 | esbuild-sunos-64@0.14.10: 167 | version "0.14.10" 168 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.10.tgz#8d3576d8cac5c4f9f2a84be81b9078d424dbc739" 169 | integrity sha512-mEU+pqkhkhbwpJj5DiN3vL0GUFR/yrL3qj8ER1amIVyRibKbj02VM1QaIuk1sy5DRVIKiFXXgCaHvH3RNWCHIw== 170 | 171 | esbuild-windows-32@0.14.10: 172 | version "0.14.10" 173 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.10.tgz#8a67fca4cb594a340566d66eef3f568f65057a48" 174 | integrity sha512-Z5DieUL1N6s78dOSdL95KWf8Y89RtPGxIoMF+LEy8ChDsX+pZpz6uAVCn+YaWpqQXO+2TnrcbgBIoprq2Mco1g== 175 | 176 | esbuild-windows-64@0.14.10: 177 | version "0.14.10" 178 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.10.tgz#5e6d7c475ff6a71ad0aa4046894364e6c40a9249" 179 | integrity sha512-LE5Mm62y0Bilu7RDryBhHIX8rK3at5VwJ6IGM3BsASidCfOBTzqcs7Yy0/Vkq39VKeTmy9/66BAfVoZRNznoDw== 180 | 181 | esbuild-windows-arm64@0.14.10: 182 | version "0.14.10" 183 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.10.tgz#50ab9a83f6ccf71c272e58489ecc4d7375075f32" 184 | integrity sha512-OJOyxDtabvcUYTc+O4dR0JMzLBz6G9+gXIHA7Oc5d5Fv1xiYa0nUeo8+W5s2e6ZkPRdIwOseYoL70rZz80S5BA== 185 | 186 | esbuild@^0.14.10: 187 | version "0.14.10" 188 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.10.tgz#10268d2b576b25ed6f8554553413988628a7767b" 189 | integrity sha512-ibZb+NwFqBwHHJlpnFMtg4aNmVK+LUtYMFC9CuKs6lDCBEvCHpqCFZFEirpqt1jOugwKGx8gALNGvX56lQyfew== 190 | optionalDependencies: 191 | esbuild-android-arm64 "0.14.10" 192 | esbuild-darwin-64 "0.14.10" 193 | esbuild-darwin-arm64 "0.14.10" 194 | esbuild-freebsd-64 "0.14.10" 195 | esbuild-freebsd-arm64 "0.14.10" 196 | esbuild-linux-32 "0.14.10" 197 | esbuild-linux-64 "0.14.10" 198 | esbuild-linux-arm "0.14.10" 199 | esbuild-linux-arm64 "0.14.10" 200 | esbuild-linux-mips64le "0.14.10" 201 | esbuild-linux-ppc64le "0.14.10" 202 | esbuild-linux-s390x "0.14.10" 203 | esbuild-netbsd-64 "0.14.10" 204 | esbuild-openbsd-64 "0.14.10" 205 | esbuild-sunos-64 "0.14.10" 206 | esbuild-windows-32 "0.14.10" 207 | esbuild-windows-64 "0.14.10" 208 | esbuild-windows-arm64 "0.14.10" 209 | 210 | he@1.2.0: 211 | version "1.2.0" 212 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 213 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 214 | 215 | klaw@^4.0.1: 216 | version "4.0.1" 217 | resolved "https://registry.yarnpkg.com/klaw/-/klaw-4.0.1.tgz#8dc6f5723f05894e8e931b516a8ff15c2976d368" 218 | integrity sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw== 219 | 220 | node-html-parser@^5.2.0: 221 | version "5.2.0" 222 | resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.2.0.tgz#6f29fd00d79f65334e7e20200964644207925607" 223 | integrity sha512-fmiwLfQu+J2A0zjwSEkztSHexAf5qq/WoiL/Hgo1K7JpfEP+OGWY5maG0kGaM+IFVdixF/1QbyXaQ3h4cGfeLw== 224 | dependencies: 225 | css-select "^4.1.3" 226 | he "1.2.0" 227 | 228 | nth-check@^2.0.1: 229 | version "2.0.1" 230 | resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" 231 | integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== 232 | dependencies: 233 | boolbase "^1.0.0" 234 | 235 | prettier@^2.5.1: 236 | version "2.5.1" 237 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" 238 | integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== 239 | 240 | source-map-support@0.5.19: 241 | version "0.5.19" 242 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" 243 | integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== 244 | dependencies: 245 | buffer-from "^1.0.0" 246 | source-map "^0.6.0" 247 | 248 | source-map-support@~0.5.20: 249 | version "0.5.21" 250 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" 251 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== 252 | dependencies: 253 | buffer-from "^1.0.0" 254 | source-map "^0.6.0" 255 | 256 | source-map@^0.6.0: 257 | version "0.6.1" 258 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 259 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 260 | 261 | source-map@~0.7.2: 262 | version "0.7.3" 263 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" 264 | integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== 265 | 266 | terser@^5.10.0: 267 | version "5.10.0" 268 | resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc" 269 | integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA== 270 | dependencies: 271 | commander "^2.20.0" 272 | source-map "~0.7.2" 273 | source-map-support "~0.5.20" 274 | 275 | tslib@2.3.1: 276 | version "2.3.1" 277 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" 278 | integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== 279 | --------------------------------------------------------------------------------