Repository: EtherDream/jsproxy-browser
Branch: master
Commit: a7a6d740e2c6
Files: 49
Total size: 242.9 KB
Directory structure:
gitextract_qe_rn8jq/
├── .gitignore
├── .vscode/
│ ├── launch.json
│ └── tasks.json
├── LICENSE
├── README.md
├── src/
│ ├── home/
│ │ ├── 404.html
│ │ ├── README.md
│ │ ├── assets/
│ │ │ ├── cors_v1.txt
│ │ │ └── index_v3.html
│ │ ├── build.sh
│ │ ├── conf.js
│ │ └── sw.js
│ └── proxy/
│ ├── .eslintrc.json
│ ├── .package-lock.json
│ ├── .package.json
│ ├── README.md
│ ├── debug.sh
│ ├── gen-tld/
│ │ ├── README.md
│ │ └── gen.js
│ ├── jsconfig.json
│ ├── release.sh
│ └── src/
│ ├── cdn.js
│ ├── client.js
│ ├── cookie.js
│ ├── database.js
│ ├── env.js
│ ├── fakeloc.js
│ ├── hook.js
│ ├── index.js
│ ├── inject.js
│ ├── jsfilter.js
│ ├── msg.js
│ ├── network.js
│ ├── page.js
│ ├── path.js
│ ├── route.js
│ ├── signal.js
│ ├── storage.js
│ ├── sw.js
│ ├── tld-data.js
│ ├── tld.js
│ ├── urlx.js
│ └── util.js
└── www/
├── 404.html
├── assets/
│ ├── README.md
│ ├── cors_v1.txt
│ └── index_v3.html
├── conf.js
└── sw.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
._*
node_modules
src/home/assets/bundle.debug.js
www/assets/bundle.*.js
================================================
FILE: .vscode/launch.json
================================================
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "clang build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb",
"preLaunchTask": "clang build active file"
}
]
}
================================================
FILE: .vscode/tasks.json
================================================
{
"tasks": [
{
"type": "shell",
"label": "clang build active file",
"command": "/usr/bin/clang",
"args": [
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "/usr/bin"
}
}
],
"version": "2.0.0"
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 EtherDream
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
JsProxy 浏览器端程序
# 修改配置
修改 `www/conf.js` 配置,发布 `www` 目录即可。
## TODO
* 增加网络错误重试、优先选择空闲节点功能
* 在 SW 中替换 HTML 的 URL 属性,并支持流模式
* CDN 文件使用 brotli 压缩
* 使用 AST 修改 JS 代码
* 动态页面压缩传输(反模板引擎,只传输变量,模板本身存储在 CDN)
* 使用更多的免费图床作为 CDN 资源存储,并支持 Hash 校验
* 计算程序使用 wasm 实现
* 支持 blob/data/javascript 协议
* 增加 qos 功能,优先满足资料查询网站流量
* 改进同源策略的安全性,增加部分 API 的授权界面
* 重新设计首页,增加更多功能
* 完整的测试案例
# 已知问题
* 文件下载对话框取消后 SW 仍在下载(fetch.signal 的 onabort 未触发,可能是浏览器 BUG)
* Chrome 图片无法保存
* 非 UTF8 编码的 JS 会出现乱码(MIME 未指定 charset 的情况下出现)
* Google 登陆页无法下一步
* Google reCAPTCHA 无法执行
* Google Maps 切换到卫星地图后卡死
* iOS Safari 无法播放 Youtube 视频
* twitter 在 Chrome 普通模式下无法登陆,但隐身模式可以
* twitter iframe 经常加载不出来
* SVG 脚本没有处理
* Youtube 视频全屏播放会卡住
* twitch.tv 首页报错(JS 代码修改导致错误,需要在 AST 层面修改)
================================================
FILE: src/home/404.html
================================================
================================================
FILE: src/home/README.md
================================================
## 404.html
该页面用于安装 Service Worker。
因为本项目只有一个 404.html,所以访问任意路径都会触发该页面。通过该页面安装 Service Worker 然后自动刷新,后续所有流量都可被 Service Worker 接管。
## sw.js
Service Worker 脚本。
虽然 Web 标准规定 Service Worker 脚本必须和站点同源,但是 JS 可通过 `importScripts` 加载站外脚本。因此可将主体代码部署在 CDN,该脚本仅仅作为一个加载器。
## build.sh
压缩当前目录 404.html 到 ../../www/404.html
## conf.js
配置文件。该文件首次运行时动态加载
## assets
该目录存放静态资源,可部署到 CDN。
================================================
FILE: src/home/assets/cors_v1.txt
================================================
# HTTP 返回头存在 access-control-allow-origin: * 的站点,不走代理直接连接
# 收集了部分,实验中...
# google
ssl.google-analytics.com
# [public]
cdn.jsdelivr.net
unpkg.com
cdnjs.cloudflare.com
cdn.bootcss.com
use.fontawesome.com
fast.fonts.net
script.hotjar.com
# github
github.githubassets.com
avatars0.githubusercontent.com
avatars1.githubusercontent.com
avatars2.githubusercontent.com
avatars3.githubusercontent.com
desktop.github.com
# flickr
status.flickr.net
# ali
at.alicdn.com
img.alicdn.com
g.alicdn.com
i.alicdn.com
atanx.alicdn.com
wwc.alicdn.com
gw.alicdn.com
assets.alicdn.com
aeis.alicdn.com
atanx.alicdn.com
hudong.alicdn.com
gma.alicdn.com
sc01.alicdn.com
sc02.alicdn.com
sc03.alicdn.com
sc04.alicdn.com
cbu01.alicdn.com
cbu02.alicdn.com
cbu03.alicdn.com
cbu04.alicdn.com
# baidu
# img*.bdimg.com
img0.bdimg.com
img1.bdimg.com
img2.bdimg.com
img3.bdimg.com
img4.bdimg.com
img5.bdimg.com
webmap0.bdimg.com
webmap1.bdimg.com
iknowpc.bdimg.com
bkssl.bdimg.com
baikebcs.bdimg.com
gh.bdstatic.com
# qq
3gimg.qq.com
combo.b.qq.com
# taotiao
images.taboola.com
images.taboola.com.cn
images-dup.taboola.com
# zhihu
static.zhihu.com
pic1.zhimg.com
pic2.zhimg.com
pic3.zhimg.com
pic4.zhimg.com
pic5.zhimg.com
pic7.zhimg.com
# jd
img11.360buyimg.com
# jianshu
upload.jianshu.io
upload-images.jianshu.io
cdn2.jianshu.io
# 163
urswebzj.nosdn.127.net
static.ws.126.net
img1.cache.netease.com
img2.cache.netease.com
img3.cache.netease.com
img4.cache.netease.com
img5.cache.netease.com
img6.cache.netease.com
# sina
js.t.sinajs.cn
mjs.sinaimg.cn
h5.sinaimg.cn
# sohu
0d077ef9e74d8.cdn.sohucs.com
39d0825d09f05.cdn.sohucs.com
5b0988e595225.cdn.sohucs.com
caaceed4aeaf2.cdn.sohucs.com
img01.sogoucdn.com
img02.sogoucdn.com
img03.sogoucdn.com
img04.sogoucdn.com
img05.sogoucdn.com
# hupu
w1.hoopchina.com.cn
w2.hoopchina.com.cn
w3.hoopchina.com.cn
w4.hoopchina.com.cn
shihuo.hupucdn.com
# uc
image.uc.cn
# ...
static.cnodejs.org
static2.cnodejs.org
2b.zol-img.com.cn
img.pconline.com.cn
angular.cn
img1.dxycdn.com
cdn.kastatic.org
static.geetest.com
cdn.registerdisney.go.com
secure-us.imrworldwide.com
img1.doubanio.com
qnwww2.autoimg.cn
qnwww3.autoimg.cn
s.autoimg.cn
hb.imgix.net
main.qcloudimg.com
vz-cdn2.contentabc.com
twemoji.maxcdn.com
fgn.cdn.serverable.com
s1.hdslb.com
s2.hdslb.com
s3.hdslb.com
# cnblogs
common.cnblogs.com
mathjax.cnblogs.com
# csdn
csdnimg.cn
g.csdnimg.cn
img-ads.csdn.net
img-bss.csdn.net
img-blog.csdn.net
# ...
static.geekbang.org
static001.infoq.cn
static.docs.com
cdn1.developermedia.com
cdn2.developermedia.com
cdn.optimizely.com
cdn.ampproject.org
camshowverse.to
static.camshowhub-cdn.to
xqimg.imedao.com
xavatar.imedao.com
# ???
img-l3.xvideos-cdn.com
static-egc.xvideos-cdn.com
img-hw.xvideos-cdn.com
img-hw.xnxx-cdn.com
static-egc.xnxx-cdn.com
di.phncdn.com
cv.phncdn.com
roomimg.stream.highwebmedia.com
w3.cdn.anvato.net
================================================
FILE: src/home/assets/index_v3.html
================================================
Page Sandbox
切换线路:
================================================
FILE: src/home/build.sh
================================================
# 压缩 404.html
html-minifier \
--collapse-whitespace \
--remove-comments \
--remove-redundant-attributes \
--remove-script-type-attributes \
--remove-tag-whitespace \
--use-short-doctype \
--remove-attribute-quotes \
--minify-css true \
--minify-js '{"toplevel": true, "ie8": true}' \
-o ../../www/404.html \
404.html
================================================
FILE: src/home/conf.js
================================================
jsproxy_config({
// 当前配置的版本(记录在日志中,用于排查问题)
// 每次修改配置,该值需要增加,否则不会生效。
// 默认每隔 5 分钟自动下载配置,若想立即验证,可通过隐私模式访问。
ver: '105',
// 通过 CDN 加速常用网站的静态资源(实验中)
static_boost: {
enable: true,
ver: 56
},
// 节点配置
node_map: {
'demo-hk': {
label: '演示服务-香港节点',
lines: {
// 主机:权重
'node-aliyun-hk-0.etherdream.com:8443': 1,
'node-aliyun-hk-1.etherdream.com:8443': 1,
'node-aliyun-hk-2.etherdream.com:8443': 1,
}
},
'demo-sg': {
label: '演示服务-新加坡节点',
lines: {
'node-aliyun-sg.etherdream.com:8443': 1,
},
},
'demo-la': {
label: '演示服务-洛杉矶节点',
lines: {
'node-bwh-la.etherdream.com:8443': 1,
},
},
'mysite': {
label: '当前站点',
lines: {
[location.host]: 1,
}
},
// 该节点用于加载大体积的静态资源
'cfworker': {
label: '',
hidden: true,
lines: {
'node-cfworker-2.etherdream.com': 1,
}
}
},
/**
* 默认节点
*/
node_default: 'demo-hk',
/**
* 加速节点
*/
node_acc: 'cfworker',
/**
* 静态资源 CDN 地址
* 用于加速 `assets` 目录中的资源访问
*/
// assets_cdn: 'https://cdn.jsdelivr.net/gh/zjcqoo/zjcqoo.github.io@master/assets/',
// 本地测试时打开,否则访问的是线上的
assets_cdn: 'assets/',
// 首页路径
index_path: 'index_v3.html',
// 支持 CORS 的站点列表(实验中...)
direct_host_list: 'cors_v1.txt',
/**
* 自定义注入页面的 HTML
*/
inject_html: '',
/**
* URL 自定义处理(设计中)
*/
url_handler: {
'https://www.baidu.com/img/baidu_resultlogo@2.png': {
replace: 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png'
},
'https://www.pornhub.com/': {
redir: 'https://php.net/'
},
'http://haha.com/': {
content: 'Hello World'
},
}
})
================================================
FILE: src/home/sw.js
================================================
jsproxy_config=x=>{__CONF__=x;importScripts(__FILE__=x.assets_cdn+'bundle.debug.js')};importScripts('conf.js')
================================================
FILE: src/proxy/.eslintrc.json
================================================
{
"env": {
"browser": true,
"es6": true,
"serviceworker": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module"
},
"rules": {
"no-console": "warn",
"no-empty": "warn",
"no-unused-vars": "warn",
"no-debugger": "warn",
"no-constant-condition": "warn"
}
}
================================================
FILE: src/proxy/.package-lock.json
================================================
{
"name": "jsproxy-client",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/code-frame": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
"integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
"dev": true,
"requires": {
"@babel/highlight": "^7.0.0"
}
},
"@babel/highlight": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
"integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
"dev": true,
"requires": {
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
}
},
"acorn": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
"integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
"dev": true
},
"acorn-jsx": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
"integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
"dev": true
},
"ajv": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
"dev": true
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
},
"astral-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
"dev": true
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"chardet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"dev": true,
"requires": {
"restore-cursor": "^2.0.0"
}
},
"cli-width": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
"dev": true
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
"dev": true,
"requires": {
"esutils": "^2.0.2"
}
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"eslint": {
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz",
"integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"ajv": "^6.9.1",
"chalk": "^2.1.0",
"cross-spawn": "^6.0.5",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"eslint-scope": "^4.0.3",
"eslint-utils": "^1.3.1",
"eslint-visitor-keys": "^1.0.0",
"espree": "^5.0.1",
"esquery": "^1.0.1",
"esutils": "^2.0.2",
"file-entry-cache": "^5.0.1",
"functional-red-black-tree": "^1.0.1",
"glob": "^7.1.2",
"globals": "^11.7.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"inquirer": "^6.2.2",
"js-yaml": "^3.13.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.3.0",
"lodash": "^4.17.11",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
"optionator": "^0.8.2",
"path-is-inside": "^1.0.2",
"progress": "^2.0.0",
"regexpp": "^2.0.1",
"semver": "^5.5.1",
"strip-ansi": "^4.0.0",
"strip-json-comments": "^2.0.1",
"table": "^5.2.3",
"text-table": "^0.2.0"
}
},
"eslint-scope": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
"integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
},
"eslint-utils": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
"dev": true
},
"eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
"dev": true
},
"espree": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz",
"integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==",
"dev": true,
"requires": {
"acorn": "^6.0.7",
"acorn-jsx": "^5.0.0",
"eslint-visitor-keys": "^1.0.0"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"esquery": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
"integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
"dev": true,
"requires": {
"estraverse": "^4.0.0"
}
},
"esrecurse": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
"dev": true,
"requires": {
"estraverse": "^4.1.0"
}
},
"estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
"dev": true
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true
},
"external-editor": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
"integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
"dev": true,
"requires": {
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
"tmp": "^0.0.33"
}
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
},
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
"dev": true,
"requires": {
"escape-string-regexp": "^1.0.5"
}
},
"file-entry-cache": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
"integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
"dev": true,
"requires": {
"flat-cache": "^2.0.1"
}
},
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
"integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
"dev": true,
"requires": {
"flatted": "^2.0.0",
"rimraf": "2.6.3",
"write": "1.0.3"
}
},
"flatted": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz",
"integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==",
"dev": true
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
"import-fresh": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
"integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
}
},
"imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"inquirer": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.3.1.tgz",
"integrity": "sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA==",
"dev": true,
"requires": {
"ansi-escapes": "^3.2.0",
"chalk": "^2.4.2",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
"external-editor": "^3.0.3",
"figures": "^2.0.0",
"lodash": "^4.17.11",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
"rxjs": "^6.4.0",
"string-width": "^2.1.0",
"strip-ansi": "^5.1.0",
"through": "^2.3.6"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true
},
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"dev": true,
"requires": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"mute-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
"dev": true
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"onetime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"dev": true,
"requires": {
"mimic-fn": "^1.0.0"
}
},
"optionator": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
"dev": true,
"requires": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.4",
"levn": "~0.3.0",
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2",
"wordwrap": "~1.0.0"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
"callsites": "^3.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-is-inside": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
"dev": true
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
"dev": true
},
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"dev": true
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"regexpp": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"dev": true
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"dev": true,
"requires": {
"onetime": "^2.0.0",
"signal-exit": "^3.0.2"
}
},
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
"integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
"dev": true,
"requires": {
"is-promise": "^2.1.0"
}
},
"rxjs": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.1.tgz",
"integrity": "sha512-y0j31WJc83wPu31vS1VlAFW5JGrnGC+j+TtGAa1fRQphy48+fDYiDmX8tjGloToEsMkxnouOg/1IzXGKkJnZMg==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"dev": true,
"requires": {
"shebang-regex": "^1.0.0"
}
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
"slice-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
"integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"astral-regex": "^1.0.0",
"is-fullwidth-code-point": "^2.0.0"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"table": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
"integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==",
"dev": true,
"requires": {
"ajv": "^6.9.1",
"lodash": "^4.17.11",
"slice-ansi": "^2.1.0",
"string-width": "^3.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"dev": true,
"requires": {
"os-tmpdir": "~1.0.2"
}
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true
},
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
"dev": true,
"requires": {
"prelude-ls": "~1.1.2"
}
},
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"write": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
"integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
"dev": true,
"requires": {
"mkdirp": "^0.5.1"
}
}
}
}
================================================
FILE: src/proxy/.package.json
================================================
{
"name": "jsproxy-client",
"version": "0.0.1",
"description": "",
"main": "boot.js",
"directories": {
"lib": "lib"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "EtherDream",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"eslint": "^5.16.0"
}
}
================================================
FILE: src/proxy/README.md
================================================
浏览器脚本的源文件
# src
源文件目录
# debug.sh
开发模式。
# release.sh
发布脚本。
================================================
FILE: src/proxy/debug.sh
================================================
webpack \
--o "../home/assets/bundle.debug.js" \
--mode development -w
================================================
FILE: src/proxy/gen-tld/README.md
================================================
顶级域名后缀生成脚本
# 生成
```bash
node gen
```
# TODO
该列表较大,导致最终 JS 增加上百 KB 的体积。
未考虑 `!` 和 `*.` 开头的域名。
未来考虑将结果保存为单独的文件,JS 运行时动态加载。
================================================
FILE: src/proxy/gen-tld/gen.js
================================================
const https = require('https')
const fs = require('fs')
const {stdout} = process
const SRC_PATH = 'https://publicsuffix.org/list/effective_tld_names.dat'
const DST_PATH = '../src/tld-data.js'
stdout.write('loading')
https.get(SRC_PATH, res => {
let str = ''
res.on('data', s => {
str += s
stdout.write('.');
}).on('end', _ => {
proc(str)
}).setEncoding('utf8')
})
function proc(str) {
const list = str
.split('\n')
.filter(v => v)
.filter(v => !v.startsWith('//'))
.filter(v => !v.startsWith('!'))
.map(v => v.replace('*.', ''))
.sort((a, b) => a > b ? 1 : -1)
.join(',')
const result = `\
// THIS FILE WAS GENERATED BY './tools/tld/gen.js'
export default '${list}'
`
fs.writeFileSync(DST_PATH, result)
console.log('\nok')
}
================================================
FILE: src/proxy/jsconfig.json
================================================
{
"compilerOptions": {
"checkJs": true,
"lib": ["webworker", "dom", "es2017"],
"target": "es2017"
},
"include": [
"src/*"
]
}
================================================
FILE: src/proxy/release.sh
================================================
DST=../../www/assets
rm $DST/bundle.*.js
webpack \
--o "$DST/bundle.[hash:8].js" \
--mode production
cd $DST
for i in bundle.*.js; do
printf "\
jsproxy_config=\
x=>{\
__CONF__=x;\
importScripts(__FILE__=x.assets_cdn+'$i')\
};\
importScripts('conf.js')\
" > ../sw.js
done
================================================
FILE: src/proxy/src/cdn.js
================================================
import * as util from './util'
// 暂时先用 jsdelivr 试验。之后换成速度很快、容量更大的免费图床
const CDN = 'https://cdn.jsdelivr.net/npm/jsproxy-cache-01@0.0.'
let mCurVer = -1
/** @type {Map} */
let mUrlHashVerMap = new Map()
/** @type {Set} */
let mDirectHostSet = new Set()
async function loadDirectList(conf) {
const url = conf.assets_cdn + conf.direct_host_list
const res = await fetch(url)
const txt = await res.text()
for (const host of txt.split('\n')) {
if (host && host[0] !== '#') {
mDirectHostSet.add(host)
}
}
}
async function loadStaticList(conf) {
const info = conf.static_boost
if (!info || !info.enable) {
return
}
const latest = info.ver
if (mCurVer >= latest) {
return
}
mCurVer = latest
console.log('[jsproxy] cdn cache ver:', latest)
const res = await fetch(CDN + latest + '/full')
const buf = await res.arrayBuffer()
const u32 = new Uint32Array(buf)
let p = 0
for (let ver = 0; ver <= latest; ver++) {
const num = u32[p++]
for (let i = 0; i < num; i++) {
const urlHash = u32[p++]
mUrlHashVerMap.set(urlHash, ver)
}
}
}
export function setConf(conf) {
return Promise.all([
loadStaticList(conf),
loadDirectList(conf),
])
}
/**
* @param {string} host
*/
export function isDirectHost(host) {
return mDirectHostSet.has(host)
}
/**
* @param {string} url
*/
export async function proxyDirect(url) {
try {
const res = await fetch(url, {
referrerPolicy: 'no-referrer',
})
const {status} = res
if (status === 200 || status === 206) {
return res
}
console.warn('direct status:', status, url)
} catch (err) {
console.warn('direct fail:', url)
}
}
/**
* @param {number} urlHash
*/
export function getFileVer(urlHash) {
return mUrlHashVerMap.get(urlHash)
}
/**
* @param {number} urlHash
* @param {number} urlVer
*/
async function proxyStaticMain(urlHash, urlVer) {
const hashHex = util.numToHex(urlHash, 8)
const res = await fetch(CDN + urlVer + '/' + hashHex + '.txt')
if (res.status !== 200) {
throw 'bad status: ' + res.status
}
const buf = await res.arrayBuffer()
const b = new Uint8Array(buf)
const hdrLen = b[0] << 8 | b[1]
const hdrBuf = b.subarray(2, 2 + hdrLen)
const hdrStr = util.bytesToStr(hdrBuf)
const hdrObj = JSON.parse(hdrStr)
const body = b.subarray(2 + hdrLen)
hdrObj['date'] = new Date().toUTCString()
return new Response(body, {
headers: hdrObj
})
}
/**
* @param {number} urlHash
* @param {number} urlVer
*/
export async function proxyStatic(urlHash, urlVer) {
// TODO: 使用多个 CDN
try {
return await proxyStaticMain(urlHash, urlVer)
} catch(err) {
console.warn('cdn fail:', err)
}
}
================================================
FILE: src/proxy/src/client.js
================================================
import * as urlx from './urlx.js'
import * as route from './route.js'
import * as env from './env.js'
import * as hook from './hook.js'
import {createFakeLoc} from './fakeloc.js'
import {createStorage} from './storage.js'
const {
apply,
construct,
} = Reflect
/**
* Hook 页面和 Worker 相同的 API
*
* @param {Window} global WindowOrWorkerGlobalScope
* @param {string} origin
*/
export function init(global, origin) {
// lockNative(win)
// hook Storage API
createStorage(global, origin)
// hook Location API
const fakeLoc = createFakeLoc(global)
// hook Performance API
const perfProto = global['PerformanceEntry'].prototype
hook.prop(perfProto, 'name',
getter => function() {
const val = getter.call(this)
if (/^https?:/.test(val)) {
return urlx.decUrlStrAbs(val)
}
return val
}
)
// hook AJAX API
const xhrProto = global['XMLHttpRequest'].prototype
hook.func(xhrProto, 'open', oldFn => function(_0, url) {
if (url) {
arguments[1] = urlx.encUrlStrRel(url, this)
}
return apply(oldFn, this, arguments)
})
hook.prop(xhrProto, 'responseURL',
getter => function(oldFn) {
const val = getter.call(this)
return urlx.decUrlStrRel(val, this)
}
)
hook.func(global, 'fetch', oldFn => function(v) {
if (v) {
if (v.url) {
// v is Request
const newUrl = urlx.encUrlStrAbs(v.url)
arguments[0] = new Request(newUrl, v)
} else {
// v is string
// TODO: 字符串不传引用,无法获取创建时的 constructor
arguments[0] = urlx.encUrlStrRel(v, v)
}
}
return apply(oldFn, this, arguments)
})
hook.func(global, 'WebSocket', oldFn => function(url) {
const urlObj = urlx.newUrl(url)
if (urlObj) {
const {ori} = env.get(this)
if (ori) {
const args = {
'origin': ori.origin,
}
arguments[0] = route.genWsUrl(urlObj, args)
}
}
return construct(oldFn, arguments)
})
/**
* @param {string} type
*/
function hookWorker(type) {
hook.func(global, type, oldFn => function(url) {
if (url) {
console.log('[jsproxy] new %s: %s', type, url)
arguments[0] = urlx.encUrlStrRel(url, this)
}
return construct(oldFn, arguments)
})
}
hookWorker('Worker')
hookWorker('SharedWorker')
hook.func(global, 'importScripts', oldFn => function(...args) {
const urls = args.map(urlx.encUrlStrRel)
console.log('[jsproxy] importScripts:', urls)
return apply(oldFn, this, urls)
})
}
================================================
FILE: src/proxy/src/cookie.js
================================================
import {Database} from './database.js'
/** @type {Set} */
let mDirtySet = new Set()
function Cookie() {
this.id = ''
this.name = ''
this.value = ''
this.domain = ''
this.hostOnly = false
this.path = ''
this.expires = NaN
this.isExpired = false
this.secure = false
this.httpOnly = false
this.sameSite = ''
}
/**
* @param {Cookie} src
* @param {Cookie} dst
*/
function copy(dst, src) {
dst.id = src.id
dst.name = src.name
dst.value = src.value
dst.domain = src.domain
dst.hostOnly = src.hostOnly
dst.path = src.path
dst.expires = src.expires
dst.isExpired = src.isExpired
dst.secure = src.secure
dst.httpOnly = src.httpOnly
dst.sameSite = src.sameSite
}
/**
* @param {string} cookiePath
* @param {string} urlPath
*/
function isSubPath(cookiePath, urlPath) {
if (urlPath === cookiePath) {
return true
}
if (!cookiePath.endsWith('/')) {
cookiePath += '/'
}
return urlPath.startsWith(cookiePath)
}
/**
* @param {string} cookieDomain
* @param {string} urlDomain
*/
function isSubDomain(cookieDomain, urlDomain) {
return urlDomain === cookieDomain ||
urlDomain.endsWith('.' + cookieDomain)
}
/**
* @param {Cookie} item
* @param {number} now
*/
function isExpire(item, now) {
const v = item.expires
return !isNaN(v) && v < now
}
class CookieDomainNode {
constructor() {
/** @type {Cookie[]} */
this.items = null
/** @type {Object} */
this.children = {}
}
/**
* @param {string} name
*/
nextChild(name) {
return this.children[name] || (
this.children[name] = new CookieDomainNode
)
}
/**
* @param {string} name
*/
getChild(name) {
return this.children[name]
}
/**
* @param {Cookie} cookie
*/
addCookie(cookie) {
if (this.items) {
this.items.push(cookie)
} else {
this.items = [cookie]
}
}
}
/** @type {Map} */
const mIdCookieMap = new Map()
const mCookieNodeRoot = new CookieDomainNode()
export function getNonHttpOnlyItems() {
const ret = []
for (const item of mIdCookieMap.values()) {
if (!item.httpOnly) {
ret.push(item)
}
}
return ret
}
/**
* @param {string} str
* @param {URL} urlObj
* @param {number} now
*/
export function parse(str, urlObj, now) {
const item = new Cookie()
const arr = str.split(';')
for (let i = 0; i < arr.length; i++) {
let key, val
const s = arr[i].trim()
const p = s.indexOf('=')
if (p !== -1) {
key = s.substr(0, p)
val = s.substr(p + 1)
} else {
//
// cookie = 's; secure; httponly'
// 0: { key: '', val: 's' }
// 1: { key: 'secure', val: '' }
// 2: { key: 'httponly', val: '' }
//
key = (i === 0) ? '' : s
val = (i === 0) ? s : ''
}
if (i === 0) {
item.name = key
item.value = val
continue
}
switch (key.toLocaleLowerCase()) {
case 'expires':
if (isNaN(item.expires)) {
item.expires = Date.parse(val)
}
break
case 'domain':
if (val[0] === '.') {
val = val.substr(1)
}
item.domain = val
break
case 'path':
item.path = val
break
case 'httponly':
item.httpOnly = true
break
case 'secure':
item.secure = true
break
case 'max-age':
item.expires = now + (+val) * 1000
break
case 'samesite':
item.sameSite = val
break
}
}
if (isExpire(item, now)) {
item.isExpired = true
}
// https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie
if (item.name.startsWith('__Secure-')) {
if (!(
urlObj.protocol === 'https:' &&
item.secure
)) {
return
}
}
if (item.name.startsWith('__Host-')) {
if (!(
urlObj.protocol === 'https:' &&
item.secure &&
item.domain === '' &&
item.path === '/'
)) {
return
}
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Compatibility_notes
if (item.secure && urlObj.protocol === 'http:') {
return
}
// check hostname
const domain = urlObj.hostname
if (item.domain) {
if (!isSubDomain(item.domain, domain)) {
console.warn('[jsproxy] invalid cookie domain! `%s` ⊄ `%s`',
item.domain, domain)
return
}
} else {
item.domain = domain
item.hostOnly = true
}
// check pathname
const path = urlObj.pathname
if (item.path) {
if (!isSubPath(item.path, path)) {
console.warn('[jsproxy] invalid cookie path! `%s` ⊄ `%s`',
item.path, path)
return
}
} else {
item.path = path
}
item.id = (item.secure ? ';' : '') +
item.name + ';' +
item.domain +
item.path
return item
}
/**
* @param {Cookie} item
*/
export function set(item) {
// console.log('set:', item)
const id = item.id
const matched = mIdCookieMap.get(id)
if (matched) {
if (item.isExpired) {
// delete
mIdCookieMap.delete(id)
matched.isExpired = true
// TODO: remove node
} else {
// update
copy(matched, item)
}
mDirtySet.add(matched)
} else {
// create
const labels = item.domain.split('.')
let labelPos = labels.length
let node = mCookieNodeRoot
do {
node = node.nextChild(labels[--labelPos])
} while (labelPos !== 0)
node.addCookie(item)
mIdCookieMap.set(id, item)
mDirtySet.add(item)
}
}
/**
* @param {URL} urlObj
*/
export function query(urlObj) {
const ret = []
const now = Date.now()
const domain = urlObj.hostname
const path = urlObj.pathname
const isHttps = (urlObj.protocol === 'https:')
const labels = domain.split('.')
let labelPos = labels.length
let node = mCookieNodeRoot
do {
node = node.getChild(labels[--labelPos])
if (!node) {
break
}
const items = node.items
if (!items) {
continue
}
for (let i = 0; i < items.length; i++) {
const item = items[i]
// https url | secure flag | carry
// ✔ | ✔ | ✔
// ✔ | ✘ | ✔
// ✘ | ✘ | ✔
// ✘ | ✔ | ✘
if (!isHttps && item.secure) {
continue
}
// HostOnly Cookie 需匹配完整域名
if (item.hostOnly && labelPos !== 0) {
continue
}
if (!isSubPath(item.path, path)) {
continue
}
if (item.isExpired) {
continue
}
if (isExpire(item, now)) {
item.isExpired = true
continue
}
// TODO: same site
let str = item.value
if (item.name) {
str = item.name + '=' + str
}
ret.push(str)
}
} while (labelPos !== 0)
return ret.join('; ')
}
/** @type {Database} */
let mDB
export async function setDB(db) {
mDB = db
const now = Date.now()
await mDB.enum('cookie', v => {
if (isExpire(v, now)) {
mDB.delete('cookie', v.id)
} else {
set(v)
}
return true
})
setInterval(save, 1000 * 3)
}
export async function save() {
if (mDirtySet.size === 0) {
return
}
const tmp = mDirtySet
mDirtySet = new Set()
for (const item of tmp) {
if (item.isExpired) {
await mDB.delete('cookie', item.id)
} else if (!isNaN(item.expires)) {
// 不保存 session cookie
await mDB.put('cookie', item)
}
}
}
================================================
FILE: src/proxy/src/database.js
================================================
import {Signal} from './signal.js'
export class Database {
/**
* @param {string} name
*/
constructor(name) {
this._name = name
/** @type {IDBDatabase} */
this._db = null
}
/**
* @param {string} table
* @param {IDBTransactionMode} mode
*/
_getStore(table, mode) {
return this._db
.transaction(table, mode)
.objectStore(table)
}
/**
* @param {Object} opts
*/
open(opts) {
const s = new Signal()
const req = indexedDB.open(this._name)
req.onsuccess = (e) => {
const idb = req.result
this._db = idb
idb.onclose = (e) => {
console.warn('[jsproxy] indexedDB disconnected, reopen...')
this.open(opts)
}
s.notify()
}
req.onerror = (e) => {
console.warn('req.onerror:', e)
s.abort(req.error)
}
req.onupgradeneeded = (e) => {
const idb = req.result
for (const [k, v] of Object.entries(opts)) {
idb.createObjectStore(k, v)
}
}
return s.wait()
}
close() {
this._db.close()
}
/**
* @param {string} table
* @param {any} key
*/
get(table, key) {
const s = new Signal()
const obj = this._getStore(table, 'readonly')
const req = obj.get(key)
req.onsuccess = (e) => {
s.notify(req.result)
}
req.onerror = (e) => {
s.abort(req.error)
}
return s.wait()
}
/**
* @param {string} table
* @param {any} record
*/
put(table, record) {
const s = new Signal()
const obj = this._getStore(table, 'readwrite')
const req = obj.put(record)
req.onsuccess = (e) => {
s.notify()
}
req.onerror = (e) => {
s.abort(req.error)
}
return s.wait()
}
/**
* @param {string} table
* @param {any} key
*/
delete(table, key) {
const s = new Signal()
const obj = this._getStore(table, 'readwrite')
const req = obj.delete(key)
req.onsuccess = (e) => {
s.notify()
}
req.onerror = (e) => {
s.abort(req.error)
}
return s.wait()
}
/**
* @param {string} table
* @param {(any) => boolean} callback
*/
enum(table, callback, ...args) {
const s = new Signal()
const obj = this._getStore(table, 'readonly')
const req = obj.openCursor(...args)
req.onsuccess = (e) => {
const {result} = req
if (result) {
if (callback(result.value) !== false) {
result.continue()
}
} else {
s.notify()
}
}
req.onerror = (e) => {
s.abort(req.error)
}
return s.wait()
}
}
================================================
FILE: src/proxy/src/env.js
================================================
export const ENV_PAGE = 1
export const ENV_WORKER = 2
export const ENV_SW = 3
let mEnvType = 0
export function setEnvType(v) {
mEnvType = v
}
export function isSwEnv() {
return mEnvType === ENV_SW
}
export function isWorkerEnv() {
return mEnvType === ENV_WORKER
}
/**
* @type {WeakMap