Adding Routing. Fixes #16
Avoiding index.html redirect. Fixes #17 Adding Favicon. Fixes #14
This commit is contained in:
parent
ada3df08dc
commit
2d8bb48ba8
|
@ -5,7 +5,7 @@ extern crate serde_derive;
|
|||
extern crate grep;
|
||||
extern crate time;
|
||||
|
||||
use actix_web::{fs, http, server, App, HttpResponse, Query};
|
||||
use actix_web::{fs, fs::NamedFile, http, server, App, HttpRequest, HttpResponse, Query};
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
|
@ -15,14 +15,17 @@ use grep::searcher::{BinaryDetection, SearcherBuilder};
|
|||
use std::fs::File;
|
||||
|
||||
fn main() {
|
||||
|
||||
server::new(|| {
|
||||
App::new()
|
||||
.route("/service/search", http::Method::GET, search)
|
||||
.resource("/favicon.ico", |r| r.f(favicon))
|
||||
.resource("/", |r| r.f(index))
|
||||
.handler(
|
||||
"/",
|
||||
"/static",
|
||||
fs::StaticFiles::new("../ui/dist/")
|
||||
.unwrap()
|
||||
.index_file("index.html"),
|
||||
// .index_file("index.html"),
|
||||
)
|
||||
.finish()
|
||||
}).bind("127.0.0.1:8080")
|
||||
|
@ -30,6 +33,14 @@ fn main() {
|
|||
.run();
|
||||
}
|
||||
|
||||
fn index(_req: &HttpRequest) -> Result<NamedFile, actix_web::error::Error> {
|
||||
Ok(NamedFile::open("../ui/dist/index.html")?)
|
||||
}
|
||||
|
||||
fn favicon(_req: &HttpRequest) -> Result<NamedFile, actix_web::error::Error> {
|
||||
Ok(NamedFile::open("../ui/src/favicon.ico")?)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SearchQuery {
|
||||
q: String,
|
||||
|
|
|
@ -27,10 +27,10 @@ Sparky.task('config', _ => {
|
|||
plugins: [
|
||||
EnvPlugin({ NODE_ENV: isProduction ? 'production' : 'development' }),
|
||||
CSSPlugin(),
|
||||
CopyPlugin({ files: ["src/favicon.ico"] }),
|
||||
WebIndexPlugin({
|
||||
title: 'Inferno Typescript FuseBox Example',
|
||||
template: 'src/index.html',
|
||||
path: isProduction ? "/static" : "/"
|
||||
}),
|
||||
isProduction &&
|
||||
QuantumPlugin({
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"dependencies": {
|
||||
"classcat": "^1.1.3",
|
||||
"inferno": "^6.0.3",
|
||||
"inferno-router": "^6.3.1",
|
||||
"moment": "^2.22.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { Component } from 'inferno';
|
||||
|
||||
export class Home extends Component<any, any> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="container">
|
||||
{this.onboard()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
onboard() {
|
||||
let site: string = "https://gitlab.com/dessalines/torrents.csv";
|
||||
return (
|
||||
<div>
|
||||
<br />
|
||||
<a href={site}>Torrents.csv</a> is a collaborative, <b>vetted</b> git repository of torrents, consisting of a single, searchable <code>torrents.csv</code> file. Its initially populated with a January 2017 backup of the pirate bay, and new torrents are periodically added from various torrents sites via a rust script.<br></br><br></br>
|
||||
<a href={site}>Torrents.csv</a> will only store torrents with at least one seeder to keep the file small, and will be periodically purged of non-seeded torrents, and sorted by seeders descending.<br></br><br></br>
|
||||
To request more torrents, or add your own to the file, go <a href={site}>here</a>.<br></br><br></br>
|
||||
Made with <a href="https://www.rust-lang.org">Rust</a>, <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>, <a href="https://actix.rs/">Actix</a>, <a href="https://www.infernojs.org">Inferno</a>, and <a href="https://www.typescriptlang.org/">Typescript</a>.
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import { Component, linkEvent } from 'inferno';
|
||||
import { SearchParams } from '../interfaces';
|
||||
|
||||
|
||||
interface State {
|
||||
searchParams: SearchParams;
|
||||
}
|
||||
|
||||
export class Navbar extends Component<any, State> {
|
||||
|
||||
state: State = {
|
||||
searchParams: {
|
||||
page: 1,
|
||||
q: ""
|
||||
}
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>{this.navbar()}</div>
|
||||
)
|
||||
}
|
||||
|
||||
navbar() {
|
||||
return (
|
||||
<nav class="navbar navbar-dark" style="background-color: #673ab7">
|
||||
<a class="navbar-brand" href="#">
|
||||
<i class="fas fa-fw fa-database mr-1"></i>Torrents.csv
|
||||
</a>
|
||||
<div class="col-12 col-sm-6">
|
||||
{this.searchForm()}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// https://www.codeply.com/go/xBVaM3q5X4/bootstrap-4-navbar-search-full-width
|
||||
searchForm() {
|
||||
return (
|
||||
<form class="my-2 my-lg-0 d-inline w-100" onSubmit={linkEvent(this, this.search)}>
|
||||
<div class="input-group">
|
||||
<input value={this.state.searchParams.q} onInput={linkEvent(this, this.searchChange)} type="text" class="form-control" placeholder="Search..." />
|
||||
<button type="submit" class="btn btn-light border border-left-0 border-radius-left-0">
|
||||
<i className="fas fa-fw fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
search(i: Navbar, event) {
|
||||
event.preventDefault();
|
||||
i.context.router.history.push(`/search/${i.state.searchParams.q}/${i.state.searchParams.page}`);
|
||||
}
|
||||
|
||||
searchChange(i: Navbar, event) {
|
||||
let searchParams: SearchParams = {
|
||||
q: event.target.value,
|
||||
page: 1
|
||||
}
|
||||
i.setState({ searchParams: searchParams });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
import { Component, linkEvent } from 'inferno';
|
||||
import * as moment from 'moment';
|
||||
|
||||
import { endpoint } from '../env';
|
||||
import { SearchParams, Results } from '../interfaces';
|
||||
import { convertCsvToJson, humanFileSize, magnetLink } from '../utils';
|
||||
|
||||
interface State {
|
||||
results: Results;
|
||||
searchParams: SearchParams;
|
||||
searching: Boolean;
|
||||
}
|
||||
|
||||
export class Search extends Component<any, State> {
|
||||
|
||||
state: State = {
|
||||
results: {
|
||||
torrents: []
|
||||
},
|
||||
searchParams: {
|
||||
q: "",
|
||||
page: 1
|
||||
},
|
||||
searching: false
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.state.searchParams = {
|
||||
page: Number(this.props.match.params.page),
|
||||
q: this.props.match.params.q
|
||||
}
|
||||
this.search();
|
||||
}
|
||||
|
||||
// Re-do search if the props have changed
|
||||
componentDidUpdate(lastProps, lastState, snapshot) {
|
||||
if (lastProps.match && lastProps.match.params !== this.props.match.params) {
|
||||
this.state.searchParams = {
|
||||
page: Number(this.props.match.params.page),
|
||||
q: this.props.match.params.q
|
||||
}
|
||||
this.search();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
search() {
|
||||
if (!!this.state.searchParams.q) {
|
||||
this.setState({ searching: true, results: { torrents: [] } });
|
||||
this.fetchData(this.state.searchParams)
|
||||
.then(results => {
|
||||
if (!!results) {
|
||||
this.setState({
|
||||
results: results
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('request failed', error);
|
||||
}).then(() => this.setState({ searching: false }));
|
||||
} else {
|
||||
this.setState({ results: { torrents: [] } });
|
||||
}
|
||||
}
|
||||
|
||||
fetchData(searchParams: SearchParams): Promise<Results> {
|
||||
let q = encodeURI(searchParams.q);
|
||||
return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}`)
|
||||
.then(data => data.text())
|
||||
.then(csv => convertCsvToJson(csv));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div className={this.state.results.torrents[0] ? "container-fluid" : "container"}>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12">
|
||||
{
|
||||
this.state.searching ?
|
||||
this.spinner()
|
||||
:
|
||||
this.state.results.torrents[0] ? this.table() : this.noResults()
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
noResults() {
|
||||
return (
|
||||
<h1>No Results</h1>
|
||||
)
|
||||
}
|
||||
|
||||
table() {
|
||||
return (
|
||||
<div>
|
||||
<table style="table-layout: fixed;" class="table table-fixed table-hover table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Name</th>
|
||||
<th class="text-right">Size</th>
|
||||
<th class="text-right">Seeds</th>
|
||||
<th class="text-right d-none d-md-table-cell">Leeches</th>
|
||||
<th class="text-right d-none d-md-table-cell">Created</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.state.results.torrents.map(torrent => (
|
||||
<tr>
|
||||
<td style="word-wrap: break-word;">{torrent.name}</td>
|
||||
<td class="text-right text-muted">{humanFileSize(torrent.size_bytes, true)}</td>
|
||||
<td class="text-right text-success"><div><i class="fas fa-fw fa-arrow-up"></i>{torrent.seeders}</div></td>
|
||||
<td class="text-right text-danger d-none d-md-table-cell"><i class="fas fa-fw fa-arrow-down"></i>{torrent.leechers}</td>
|
||||
<td class="text-right text-muted d-none d-md-table-cell" title={`Scraped ${moment(torrent.scraped_date * 1000).fromNow()}`}>
|
||||
{moment(torrent.created_unix * 1000).fromNow()}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-sm" href={magnetLink(torrent.infohash)}>
|
||||
<i title="magnet link" class="fas fa-fw fa-magnet"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm d-none d-md-inline" href={`https://gitlab.com/dessalines/torrents.csv/issues/new?issue[assignee_id]=&issue[milestone_id]=&issue[title]=Report%20Torrent%20infohash%20${torrent.infohash}`}>
|
||||
<i title="Report torrent" class="fas fa-fw fa-flag"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{this.paginator()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
spinner() {
|
||||
return (
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin fa-5x"></i>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
paginator() {
|
||||
return (
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
<li className={(this.state.searchParams.page == 1) ? "page-item disabled" : "page-item"}>
|
||||
<button class="page-link"
|
||||
onClick={linkEvent({ i: this, nextPage: false }, this.switchPage)}
|
||||
>
|
||||
Previous</button>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<button class="page-link"
|
||||
onClick={linkEvent({ i: this, nextPage: true }, this.switchPage)}>
|
||||
Next
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
switchPage(a: { i: Search, nextPage: boolean }, event) {
|
||||
|
||||
let newSearch = a.i.state.searchParams;
|
||||
newSearch.page += (a.nextPage) ? 1 : -1;
|
||||
a.i.props.history.push(`/search/${newSearch.q}/${newSearch.page}`);
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,209 +1,27 @@
|
|||
import { render, Component, linkEvent } from 'inferno';
|
||||
import * as moment from 'moment';
|
||||
import { render, Component } from 'inferno';
|
||||
import { HashRouter, Route, Switch, Link } from 'inferno-router';
|
||||
|
||||
import { endpoint } from './env';
|
||||
import { SearchParams, Results, State } from './interfaces';
|
||||
import { convertCsvToJson, humanFileSize, magnetLink } from './utils';
|
||||
import { Navbar } from './components/navbar';
|
||||
import { Home } from './components/home';
|
||||
import { Search } from './components/search';
|
||||
|
||||
import './Main.css';
|
||||
|
||||
const container = document.getElementById('app');
|
||||
|
||||
class TorrentSearchComponent extends Component<any, State> {
|
||||
|
||||
state: State = {
|
||||
results: {
|
||||
torrents: []
|
||||
},
|
||||
searchParams: {
|
||||
q: "",
|
||||
page: 1
|
||||
},
|
||||
searching: false
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
}
|
||||
|
||||
search(i: TorrentSearchComponent, event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!!i.state.searchParams.q) {
|
||||
i.setState({ searching: true, results: { torrents: [] } });
|
||||
i.fetchData(i.state.searchParams)
|
||||
.then(results => {
|
||||
if (!!results) {
|
||||
i.setState({
|
||||
results: results
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('request failed', error);
|
||||
}).then(() => i.setState({ searching: false }));
|
||||
} else {
|
||||
i.setState({ results: { torrents: [] } });
|
||||
}
|
||||
}
|
||||
|
||||
fetchData(searchParams: SearchParams): Promise<Results> {
|
||||
let q = encodeURI(searchParams.q);
|
||||
return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}`)
|
||||
.then(data => data.text())
|
||||
.then(csv => convertCsvToJson(csv));
|
||||
}
|
||||
|
||||
class Index extends Component<any, any> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.navbar()}
|
||||
<div className={this.state.results.torrents[0] ? "container-fluid" : "container"}>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12">
|
||||
{
|
||||
this.state.searching ?
|
||||
this.spinner()
|
||||
:
|
||||
this.state.results.torrents[0] ? this.table() : this.onboard()
|
||||
}
|
||||
</div>
|
||||
<HashRouter>
|
||||
<div>
|
||||
<Navbar />
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route path={`/search/:q/:page`} component={Search} />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</HashRouter>
|
||||
)
|
||||
}
|
||||
|
||||
table() {
|
||||
return (
|
||||
<div>
|
||||
<table style="table-layout: fixed;" class="table table-fixed table-hover table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Name</th>
|
||||
<th class="text-right">Size</th>
|
||||
<th class="text-right">Seeds</th>
|
||||
<th class="text-right d-none d-md-table-cell">Leeches</th>
|
||||
<th class="text-right d-none d-md-table-cell">Created</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.state.results.torrents.map(torrent => (
|
||||
<tr>
|
||||
<td style="word-wrap: break-word;">{torrent.name}</td>
|
||||
<td class="text-right text-muted">{humanFileSize(torrent.size_bytes, true)}</td>
|
||||
<td class="text-right text-success"><div><i class="fas fa-fw fa-arrow-up"></i>{torrent.seeders}</div></td>
|
||||
<td class="text-right text-danger d-none d-md-table-cell"><i class="fas fa-fw fa-arrow-down"></i>{torrent.leechers}</td>
|
||||
<td class="text-right text-muted d-none d-md-table-cell" title={`Scraped ${moment(torrent.scraped_date * 1000).fromNow()}`}>
|
||||
{moment(torrent.created_unix * 1000).fromNow()}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-sm" href={magnetLink(torrent.infohash)}>
|
||||
<i title="magnet link" class="fas fa-fw fa-magnet"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm d-none d-md-inline" href={`https://gitlab.com/dessalines/torrents.csv/issues/new?issue[assignee_id]=&issue[milestone_id]=&issue[title]=Report%20Torrent%20infohash%20${torrent.infohash}`}>
|
||||
<i title="Report torrent" class="fas fa-fw fa-flag"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{this.paginator()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
navbar() {
|
||||
return (
|
||||
<nav class="navbar navbar-dark" style="background-color: #673ab7">
|
||||
<a class="navbar-brand" href="#">
|
||||
<i class="fas fa-fw fa-database mr-1"></i>Torrents.csv
|
||||
</a>
|
||||
<div class="col-12 col-sm-6">
|
||||
{this.searchForm()}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// https://www.codeply.com/go/xBVaM3q5X4/bootstrap-4-navbar-search-full-width
|
||||
searchForm() {
|
||||
return (
|
||||
<form class="my-2 my-lg-0 d-inline w-100" onSubmit={linkEvent(this, this.search)}>
|
||||
<div class="input-group">
|
||||
<input value={this.state.searchParams.q} onInput={linkEvent(this, this.searchChange)} type="text" class="form-control" placeholder="Search..." />
|
||||
<button type="submit" class="btn btn-light border border-left-0 border-radius-left-0">
|
||||
<i className={(this.state.searching) ? "fas fa-spinner fa-spin" : "fas fa-fw fa-search"}></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
spinner() {
|
||||
return (
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin fa-5x"></i>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onboard() {
|
||||
let site: string = "https://gitlab.com/dessalines/torrents.csv";
|
||||
return (
|
||||
<div class="mt-2">
|
||||
<a href={site}>Torrents.csv</a> is a collaborative, <b>vetted</b> git repository of torrents, consisting of a single, searchable <code>torrents.csv</code> file. Its initially populated with a January 2017 backup of the pirate bay, and new torrents are periodically added from various torrents sites via a rust script.<br></br><br></br>
|
||||
<a href={site}>Torrents.csv</a> will only store torrents with at least one seeder to keep the file small, and will be periodically purged of non-seeded torrents, and sorted by seeders descending.<br></br><br></br>
|
||||
To request more torrents, or add your own to the file, go <a href={site}>here</a>.<br></br><br></br>
|
||||
Made with <a href="https://www.rust-lang.org">Rust</a>, <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>, <a href="https://actix.rs/">Actix</a>, <a href="https://www.infernojs.org">Inferno</a>, and <a href="https://www.typescriptlang.org/">Typescript</a>.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
paginator() {
|
||||
return (
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
<li className={(this.state.searchParams.page == 1) ? "page-item disabled" : "page-item"}>
|
||||
<button class="page-link"
|
||||
onClick={linkEvent({ i: this, nextPage: false }, this.switchPage)}
|
||||
>
|
||||
Previous</button>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<button class="page-link"
|
||||
onClick={linkEvent({ i: this, nextPage: true }, this.switchPage)}>
|
||||
Next
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
searchChange(i, event) {
|
||||
let searchParams: SearchParams = {
|
||||
q: event.target.value,
|
||||
page: 1
|
||||
}
|
||||
i.setState({ searchParams: searchParams });
|
||||
}
|
||||
|
||||
|
||||
switchPage(a: { i: TorrentSearchComponent, nextPage: boolean }, event) {
|
||||
|
||||
let newSearch = a.i.state.searchParams;
|
||||
newSearch.page += (a.nextPage) ? 1 : -1;
|
||||
a.i.setState({
|
||||
searchParams: newSearch
|
||||
});
|
||||
|
||||
a.i.search(a.i, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render(<TorrentSearchComponent />, container);
|
||||
render(<Index />, container);
|
||||
|
|
|
@ -17,10 +17,4 @@ export interface Torrent {
|
|||
leechers: number;
|
||||
completed: number;
|
||||
scraped_date: number;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
results: Results;
|
||||
searchParams: SearchParams;
|
||||
searching: Boolean;
|
||||
}
|
|
@ -1104,6 +1104,22 @@ has-values@^1.0.0:
|
|||
is-number "^3.0.0"
|
||||
kind-of "^4.0.0"
|
||||
|
||||
history@^4.7.2:
|
||||
version "4.7.2"
|
||||
resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b"
|
||||
integrity sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==
|
||||
dependencies:
|
||||
invariant "^2.2.1"
|
||||
loose-envify "^1.2.0"
|
||||
resolve-pathname "^2.2.0"
|
||||
value-equal "^0.4.0"
|
||||
warning "^3.0.0"
|
||||
|
||||
hoist-non-inferno-statics@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-inferno-statics/-/hoist-non-inferno-statics-1.1.3.tgz#7d870f4160bfb6a59269b45c343c027f0e30ab35"
|
||||
integrity sha1-fYcPQWC/tqWSabRcNDwCfw4wqzU=
|
||||
|
||||
http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
|
||||
|
@ -1149,6 +1165,16 @@ ignore-walk@^3.0.1:
|
|||
dependencies:
|
||||
minimatch "^3.0.4"
|
||||
|
||||
inferno-router@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/inferno-router/-/inferno-router-6.3.1.tgz#dc31d979e1ebeb313fc2c1d94f21f06969fd366b"
|
||||
integrity sha512-Ze7SkL28aydxffONhjE0Dcl0uWdXPNVcNBQ1Jxfa2HQjT/09DPcDO5nwdWEyYkU3zJcdyPsThh2YESbfxk0eJg==
|
||||
dependencies:
|
||||
history "^4.7.2"
|
||||
hoist-non-inferno-statics "^1.1.3"
|
||||
inferno "6.3.1"
|
||||
path-to-regexp-es6 "1.7.0"
|
||||
|
||||
inferno-shared@6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/inferno-shared/-/inferno-shared-6.3.1.tgz#0ebb51aa397cabaaced4ef48b4e794d6f41f0455"
|
||||
|
@ -1159,7 +1185,7 @@ inferno-vnode-flags@6.3.1:
|
|||
resolved "https://registry.yarnpkg.com/inferno-vnode-flags/-/inferno-vnode-flags-6.3.1.tgz#8ef65130f1966c6ad38a0582a19d64c331094077"
|
||||
integrity sha512-Wr6xrWVxjjk4QRwDc6chbSkCko7birNLV60LnWoczILwFezPZHYg5U4fz/CMOc6u+0/cnDdfdV433SynwzW7Qw==
|
||||
|
||||
inferno@^6.0.3:
|
||||
inferno@6.3.1, inferno@^6.0.3:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/inferno/-/inferno-6.3.1.tgz#51c11787c9dbd311e8df82dacb0efef0d2ff51c8"
|
||||
integrity sha512-FejSGGzymfFqvWG3wrOfRrv+cP1TOeq7aiBN7hZsCd0mcKnqMjEgdSXm1YltFekk+lpTFWEvCzD7SBETmYWwhg==
|
||||
|
@ -1225,6 +1251,13 @@ inquirer@^3.0.6:
|
|||
strip-ansi "^4.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
invariant@^2.2.1:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
ipaddr.js@1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
|
||||
|
@ -1392,6 +1425,11 @@ is-windows@^1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
|
||||
|
||||
isarray@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||
|
||||
isarray@1.0.0, isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
|
@ -1414,6 +1452,11 @@ isstream@~0.1.2:
|
|||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
|
@ -1500,6 +1543,13 @@ lodash@^4.3.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
map-cache@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
||||
|
@ -1914,11 +1964,25 @@ path-is-absolute@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-to-regexp-es6@1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp-es6/-/path-to-regexp-es6-1.7.0.tgz#965657c9f8ea8f660e103ccb839abf038cba4caf"
|
||||
integrity sha512-QdT7okCAMGv7FR7w6KWFH9OSMivOgtXAGKodD6MDZBNR/XNL16W+hHoj6qBmV6cy/7eR1fr0Qujrg9OhBf5QPw==
|
||||
dependencies:
|
||||
path-to-regexp "1.7.0"
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
path-to-regexp@1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
|
||||
integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=
|
||||
dependencies:
|
||||
isarray "0.0.1"
|
||||
|
||||
performance-now@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
|
@ -2172,6 +2236,11 @@ request@^2.79.0:
|
|||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
resolve-pathname@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879"
|
||||
integrity sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==
|
||||
|
||||
resolve-url@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||
|
@ -2681,6 +2750,11 @@ uuid@^3.3.2:
|
|||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
||||
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
|
||||
|
||||
value-equal@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7"
|
||||
integrity sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==
|
||||
|
||||
vary@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
|
@ -2695,6 +2769,13 @@ verror@1.10.0:
|
|||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
warning@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
|
||||
integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
watch@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/watch/-/watch-1.0.2.tgz#340a717bde765726fa0aa07d721e0147a551df0c"
|
||||
|
|
Loading…
Reference in New Issue