Compare commits

..

64 commits

Author SHA1 Message Date
Ben van Hartingsveldt
89f5237d85
Improve note text 2025-07-23 21:32:31 +02:00
Ben van Hartingsveldt
95078acc24
Fix note color 2025-07-23 21:29:59 +02:00
Ben van Hartingsveldt
b2e160f3d3
Fix importing non-cached CSS file 2025-07-23 21:25:19 +02:00
Ben van Hartingsveldt
28f8607fc7
Fill all tutorial pages 2025-07-23 21:18:52 +02:00
Ben van Hartingsveldt
d897d08d83
Fix navigation 2025-07-23 20:39:47 +02:00
Ben van Hartingsveldt
cedccbcc00
Fix tutorials page 2025-07-23 20:39:13 +02:00
Ben van Hartingsveldt
76e36deefa
Add chart image 2025-07-22 13:52:15 +02:00
Ben van Hartingsveldt
65b775e312
Add other protocols 2025-07-21 15:56:04 +02:00
Ben van Hartingsveldt
0b302314a5
Fill protocol pages 2025-07-18 20:17:58 +02:00
Ben van Hartingsveldt
35d0ef20b3
Add protocol pages 2025-07-18 20:12:01 +02:00
b75e114c79
Merge pull request #4 from LBRYFoundation/migrate-framework
migrate to hono and bun
2025-04-23 02:34:01 +02:00
22a9114f7d migrate to hono and bun 2025-04-23 02:31:18 +02:00
9479ebde47 use lbry foundation component repo and update sass 2025-04-22 16:21:49 +02:00
ac51344f5b removed bloat and updated packages 2025-04-21 04:10:38 +02:00
15ff3bd393 fix 2025-04-20 20:52:57 +02:00
e8bb671803 Merge branch 'api-docs-fix'
merge
2025-04-20 20:29:10 +02:00
420d0c6635 bump node to 22 and fixing broken stuff 2025-04-20 20:25:10 +02:00
2541a74344
Merge pull request #3 from LBRYFoundation/api-docs-fix
replaced github endpoints with jsdelivr
2024-01-26 20:10:07 +01:00
a3ec616fd4 replaced github endpoints with jsdelivr 2024-01-24 17:24:37 +01:00
81633dd711
Merge pull request #2 from LBRYFoundation/sync-lock-file
Sync lock file
2024-01-23 17:46:05 +01:00
b9823dd39e synced lock file 2024-01-23 17:31:06 +01:00
e780ad27cd remove enforce https on production 2023-12-07 20:18:32 +01:00
45e91657c8 fix module-alias issue 2023-12-07 20:18:13 +01:00
ad230d2271 setup docker 2023-12-07 20:17:43 +01:00
Alex Grin
b5eb6fe390
Merge pull request #381 from keikari/patch-2 2022-06-10 14:21:26 -04:00
miko
d8bbdffbf8
Update TCP port default value 2022-06-08 18:18:51 +03:00
Alex Grin
6fd2acdfc2
Merge pull request #380 from lbryio/p2p
LBRY P2P: Settings and troubleshooting
2022-05-24 12:55:25 -04:00
Victor Shyba
3d21394728 refactor confusing paragraph 2022-05-19 18:49:34 -03:00
Victor Shyba
dbb050baa0 improve reachability text 2022-05-19 18:46:56 -03:00
Victor Shyba
9b5c92a722 add wiki reference on hole punching 2022-05-19 18:19:30 -03:00
Victor Shyba
aebef79820 add p2p-seeding.md 2022-05-19 18:15:21 -03:00
Lex Berezhny
a8f9d90f22 added time locked transaction instructions 2022-04-25 09:07:16 -04:00
Alex Grintsvayg
2098f6bbba
improve lbrycrdd service file
inspired by #370  and https://github.com/lbryio/lbrycrd/blob/master/contrib/init/bitcoind.service
2022-02-08 10:32:27 -05:00
Alex Grin
158c49b58f
Merge pull request #370 from derlaft/patch-1 2022-02-08 10:21:14 -05:00
Aleksei Kharlamov
08759225a6
wallet-server: update suggested systemd unit
Use `Type=Forking`. By default, `Type=Simple` is used and it expects that the daemon stays forking in the foreground. However, lbrycrdd does the opposite and forks/goes to background. This confuses systemd a little bit:

```
Dec 09 10:30:28 lbry.devass.club systemd[1]: lbrycrdd.service: Unit process 12237 (lbrycrdd) remains running after unit stopped.
```

So basically it thinks that the unit stops working instantly. When the correct type is used, systemd should be able to handle this situations, along with restarts, properly.

See also: [AUR systemd service](https://aur.archlinux.org/cgit/aur.git/tree/lbrycrd.service?h=lbrycrd)
2021-12-10 01:50:40 +01:00
Alex Grin
2fe727d9c2
Update resources.md 2021-11-08 11:39:35 -05:00
Alex Grin
935ca0694a
Delete regtest-setup.md 2021-11-08 11:39:17 -05:00
Alex Grin
ff9c0dbc8b
Merge pull request #366 from lbryio/update-wallet-server-instructions 2021-11-08 11:23:48 -05:00
Alex Grin
cf6850c89d
Merge pull request #358 from lbryio/odysee-api 2021-11-08 11:22:26 -05:00
Jack Robison
f97bb66eda
update instructions 2021-11-08 09:33:15 -05:00
Alex Grin
04a8ef7d96
Merge pull request #364 from lbryio/dht_bootstrap 2021-10-04 15:02:20 -04:00
Victor Shyba
b46edd2ba0 add section to check the node 2021-09-28 18:55:34 -03:00
Victor Shyba
2eef37c05d DHT bootstrap guide 2021-09-28 18:42:32 -03:00
Alex Grintsvayg
62cbc4edad
fix pgp link 2021-09-28 10:23:23 -04:00
Alex Grin
9be3af9cb5
Update README.md 2021-09-28 10:16:15 -04:00
Thomas Zarebczan
679561b334
Merge pull request #363 from bradley-ray/github-feed-fix-undefined
added case PullRequestReviewEvent to generateEvent function
2021-09-24 09:49:40 -04:00
Bradley Ray
7da3b6dacf
added case PullRequestReviewEvent to generateEvent 2021-09-23 00:10:50 -05:00
Thomas Zarebczan
88bc857a31
Update lbrytv-sdk.js 2021-08-07 01:47:50 -04:00
Thomas Zarebczan
abbebe07d3
update to odysee api 2021-08-07 01:44:10 -04:00
Andrey Beletsky
cd3faa3270 documents/contribute.md 2021-07-20 15:21:05 +07:00
Alex Grin
786be56ed4
Update resources.md 2021-06-28 09:45:17 -04:00
Bryan Lunduke
a9cdfd94ba
Add Hello Satoshi Tutorial to list 2021-06-20 12:49:07 -05:00
Bryan Lunduke
aa79b0ae21
Creating a dedicated page for Hello Satoshi 2021-06-20 12:44:34 -05:00
Bryan Lunduke
53262f300d
Adding a Tutorials option to the navigation bar 2021-06-20 12:28:05 -05:00
Bryan Lunduke
ae51c52240
Create tutorials.md
Setting up the tutorials document which will contain all of the tutorials (and related resources) in categories.
2021-06-17 10:42:31 -05:00
Alex Grin
706b9752e2
Merge pull request #354 from lbryio/web-instance 2021-06-15 15:21:48 -04:00
Alex Grin
34f7842505
Update web-instance.md 2021-06-15 15:21:13 -04:00
Victor Shyba
0e95e6dc72 move prebuilt image section up, add step to clone sdk repo 2021-06-15 16:04:49 -03:00
Victor Shyba
55d0a6df63 format title/description 2021-06-15 14:49:06 -03:00
Victor Shyba
ad1395a5b9 describe how to install a web instance 2021-06-15 14:47:47 -03:00
Bryan Lunduke
0a7955bcdc
Removing Developer program link 2021-06-10 16:22:55 -05:00
Thomas Zarebczan
d7f7612f6d
Merge pull request #344 from e4drcf/patch-1
Update contribute.md
2021-06-10 11:53:08 -04:00
Thomas Zarebczan
d0069e7a51
Merge pull request #352 from lbryio/BryanLunduke-patch-1
Update overview.md
2021-06-09 20:58:09 -04:00
Ivan Hranat
c207123556
Update contribute.md 2021-04-21 20:27:21 +03:00
87 changed files with 6762 additions and 11139 deletions

View file

@ -3,14 +3,14 @@
"@babel/env"
],
"plugins": [
"@babel/proposal-class-properties",
"@babel/proposal-export-namespace-from",
"@babel/proposal-function-sent",
"@babel/proposal-json-strings",
"@babel/proposal-numeric-separator",
"@babel/proposal-throw-expressions",
"@babel/syntax-dynamic-import",
"@babel/syntax-import-meta"
// "@babel/proposal-class-properties",
// "@babel/proposal-export-namespace-from",
// "@babel/proposal-function-sent",
// "@babel/proposal-json-strings",
// "@babel/proposal-numeric-separator",
// "@babel/proposal-throw-expressions",
// "@babel/syntax-dynamic-import",
// "@babel/syntax-import-meta"
],
"ignore": [
"app/dist/*.js"

11
.dockerignore Normal file
View file

@ -0,0 +1,11 @@
# Files
.DS_Store
.env
*.log
.sass-cache
app/dist/bundle.css
app/dist/bundle.css.map
# Directories
cache
node_modules

View file

@ -2,15 +2,9 @@
# HTTPS is assumed for security reasons
DAEMON_URL=
# These are for powering the LBRY Developer Program
# /developer-program
GITHUB_APP_ID=
GITHUB_APP_SECRET=
REWARD_URL=api.lbry.com
# https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app
# We use this to show the GitHub feed on the homepage
GITHUB_OAUTH_TOKEN=
GITHUB_TOKEN=
# You can use openssl to generate impossible to guess tokens
# Ideally you would have a reference to this on your daemon so make sure that server is secure!
@ -22,11 +16,4 @@ LBRY_DAEMON_IMAGES_PATH=
# Usually 443
# This is commented-out for local development
PORT=
# https://devcenter.heroku.com/articles/rediscloud
REDISCLOUD_URL=
# https://api.slack.com/incoming-webhooks
# We get notified when issues with this app happens, directly in Slack
SLACK_WEBHOOK_URL=
PORT=

View file

@ -1,5 +1,5 @@
language: node_js
node_js:
- "10.2"
- "22"
before_install:
- npm i -g npm@latest

14
Dockerfile Normal file
View file

@ -0,0 +1,14 @@
FROM oven/bun:1.2.10-alpine
WORKDIR /app
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN bun install
COPY . .
EXPOSE 8080
CMD [ "bun", "run", "start" ]

View file

@ -57,7 +57,7 @@ This project is MIT licensed. For the full license, see [LICENSE](LICENSE).
## Security
We take security seriously. Please contact [security@lbry.com](mailto:security@lbry.com) regarding any security issues.
[Our PGP key is here](https://keybase.io/lbry/key.asc) if you need it.
[Our PGP key is here](https://lbry.com/faq/pgp-key) if you need it.
## Contact
The primary contact for this project is [Thomas Zarebczan](https://github.com/tzarebczan) (tom+github@lbry.com).

100
app/client.js Executable file → Normal file
View file

@ -1,71 +1,33 @@
"use strict";
import { Hono } from "hono";
import head from "./components/head.js";
import wrapper from "./components/wrapper.js";
import home from "./views/home.js";
import api from "./views/api.js";
import spec from "./views/spec.js";
import redirect from "./views/redirect.js";
const app = new Hono();
app.get("/", page(home));
app.get("/api/:wildcard", page(api));
app.get("/spec", page(spec));
app.get("*", page(redirect));
function page(view) {
return async (c, emit) => {
return c.html(`
<!DOCTYPE html>
<html lang="en">
${head(c)}
${await wrapper(view)(c)}
</html>
`);
};
}
// I M P O R T S
import async from "choo-async";
import asyncHtml from "choo-async/html";
import choo from "choo";
import ssr from "choo-ssr";
// U T I L S
import head from "~component/head";
import wrapper from "~component/wrapper";
// P R O G R A M
function main() {
const app = async(choo());
const page = view => (
shell(
ssr.head(
head,
ssr.state()
),
ssr.body(wrapper(view))
)
);
app.use(ssr());
app.route("/", page(require("./views/home")));
app.route("/api/*", page(require("./views/api")));
app.route("/spec", page(require("./views/spec")));
app.route("/*", page(require("./views/redirect")));
app.mount("html");
return app;
}
if (typeof window !== "undefined") main();
// E X P O R T
module.exports = exports = main;
// H E L P E R
function shell(head, body) {
return (state, emit) => {
const bodyPromise = Promise.resolve(body(state, emit));
const headPromise = bodyPromise.then(() => head(state, emit)); // resolve `head` once `body` is resolved
return asyncHtml`
<!DOCTYPE html>
<html lang="en">
${headPromise}
${bodyPromise}
</html>
`;
};
}
export default app;

View file

@ -4,7 +4,8 @@
// I M P O R T
import html from "choo/html";
import { html } from 'hono/html'

View file

@ -4,7 +4,8 @@
// I M P O R T
import html from "choo/html";
import { html } from 'hono/html'

View file

@ -1,97 +0,0 @@
"use strict"; /* global document, fetch, history, send, window */
document.getElementById("get-started").onclick = event => {
event.preventDefault();
send({
message: "auth me with github"
});
};
if (window.location.search.includes("?code=")) {
document.querySelector("developer-program").innerHTML = `
<form onsubmit="return false;">
<input-submit>
<input id="walletAddress" placeholder="Your LBRY wallet address" type="text"/>
<input id="oauthCode" type="hidden" value="${window.location.search.split("?code=").pop()}"/>
<button id="creditsAcquire" title="Get LBRY credits" type="button">Get credits</button>
</input-submit>
</form>
<h4>Need An Address?</h4>
<p>To receive your LBC, you'll need a wallet address. While graphical wallets are available, the recommended path for engineers is to:</p>
<ol>
<li>Download <a href="https://github.com/lbryio/lbry-sdk/releases">the LBRY SDK</a>.</li>
<li>Launch the command-line utility (<code>./lbrynet start</code>).</li>
<li>Run <code>./lbrynet address unused</code> and copy the <code>id</code> field.</li>
</ol>
`;
history.replaceState({}, "", window.location.pathname); // clean up URL bar
}
if (document.getElementById("creditsAcquire")) {
document.getElementById("walletAddress").addEventListener("keyup", event => {
const key = event.keyCode ? event.keyCode : event.which;
if (key === 13)
document.getElementById("creditsAcquire").click();
});
document.getElementById("creditsAcquire").onclick = () => {
send({
address: document.getElementById("walletAddress").value,
code: document.getElementById("oauthCode").value,
message: "verify github token"
});
document.querySelector("developer-program").innerHTML = "<p><em>Awaiting response from LBRY server...</em></p>";
};
}
function syncWithApi(data) { // eslint-disable-line no-unused-vars
const address = data.address;
const code = data.code;
if (code === null) {
document.querySelector("developer-program").innerHTML =
"<p><strong>There was an issue with accessing GitHub's API. Please try again later.</strong></p>";
}
fetch(`https://api.lbry.com/reward/new?github_token=${code}&reward_type=github_developer&wallet_address=${address}`)
.then(response => response.json())
.then(result => {
switch(true) {
case result.error === "This reward is limited to 1 per person":
document.querySelector("developer-program").innerHTML =
"<p>You have already claimed this reward. This reward is limited to <strong>ONE</strong> per person. Your enthusiasm is appreciated.</p>";
return;
case Boolean(result.error):
document.querySelector("developer-program").innerHTML =
`<p>${result.error}</p>`;
return;
case result.success:
result = result.data;
document.querySelector("developer-program").innerHTML =
`<p><strong>Success!</strong> Your wallet has been credited with ${result.reward_amount} LBC.</p><p>We have a great reference for the <a href="/api/sdk">LBRY SDK here</a> to help you get started.</p><p>You can see proof of this transaction on <a href="https://explorer.lbry.com/tx/${result.transaction_id}">our Blockchain Explorer</a>.</p>`;
return;
default:
console.info(data); // eslint-disable-line no-console
document.querySelector("developer-program").innerHTML =
"<p><strong>No success or error was received so the LBRY API might be down.<br/>Please try again later.</strong></p>";
}
})
.catch(error => {
console.error(error);
// Idk what the error would be (probably a 500) so let's just have this message
document.querySelector("developer-program").innerHTML =
"<p><strong>LBRY API is down. Please try again later.</strong></p>";
});
}

View file

@ -1,32 +0,0 @@
"use strict";
// I M P O R T
import html from "choo/html";
// E X P O R T
export default () => {
if (
!process.env.GITHUB_APP_ID ||
!process.env.GITHUB_APP_SECRET ||
!process.env.REWARD_URL
) {
return html`
<developer-program>
<p><strong>Environment variables required to enable functionality are missing.</strong></p>
</developer-program>
`;
}
return html`
<developer-program>
<button class="button" id="get-started">Claim Developer LBC</button>
<small class="meta">This will authenticate you with GitHub to prove eligibility as well as mark you as a follower of LBRY.</small>
</developer-program>
`;
};

View file

@ -4,13 +4,13 @@
// U T I L S
import applications from "./module-applications";
import chainquery from "./submodule-chainquery";
import lbry from "./module-lbry";
import lbrycrd from "./module-lbrycrd";
import lighthouse from "./submodule-lighthouse";
import reflector from "./submodule-reflector";
import wallet from "./submodule-wallet";
import applications from "./module-applications.js";
import chainquery from "./submodule-chainquery.js";
import lbry from "./module-lbry.js";
import lbrycrd from "./module-lbrycrd.js";
import lighthouse from "./submodule-lighthouse.js";
import reflector from "./submodule-reflector.js";
import wallet from "./submodule-wallet.js";

View file

@ -4,13 +4,13 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown.js";
// E X P O R T
export default () => `
export default async () => `
<div class="ecosystem__module applications">
<span class="__close" data-action="close">&times;</span>
@ -28,7 +28,7 @@ export default () => `
</h2>
<div class="ecosystem__module__details">
${markdown("./documents/partials/overview/applications.md")}
${await markdown("./documents/partials/overview/applications.md")}
</div>
</div>
`;

View file

@ -4,13 +4,13 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown.js";
// E X P O R T
export default () => `
export default async () => `
<div class="ecosystem__module lbry">
<span class="__close" data-action="close">&times;</span>
@ -27,7 +27,7 @@ export default () => `
</h2>
<div class="ecosystem__module__details">
${markdown("./documents/partials/overview/lbrysdk.md")}
${await markdown("./documents/partials/overview/lbrysdk.md")}
</div>
</div>
`;

View file

@ -4,13 +4,13 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown.js";
// E X P O R T
export default () => `
export default async () => `
<div class="ecosystem__module lbrycrd">
<span class="__close" data-action="close">&times;</span>
@ -27,7 +27,7 @@ export default () => `
</h2>
<div class="ecosystem__module__details">
${markdown("./documents/partials/overview/lbrycrd.md")}
${await markdown("./documents/partials/overview/lbrycrd.md")}
</div>
</div>
`;

View file

@ -4,19 +4,19 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown.js";
// E X P O R T
export default () => `
export default async () => `
<div class="ecosystem__submodule chainquery">
<h3 class="ecosystem__submodule__title" data-action="openSubmodule" data-target="chainquery">chainquery</h3>
<div class="ecosystem__submodule__description">
<div class="ecosystem__submodule__markdown">
${markdown("./documents/partials/overview/chainquery.md")}
${await markdown("./documents/partials/overview/chainquery.md")}
</div>
<ul class="__parents">

View file

@ -4,19 +4,19 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown.js";
// E X P O R T
export default () => `
export default async () => `
<div class="ecosystem__submodule lighthouse">
<h3 class="ecosystem__submodule__title" data-action="openSubmodule" data-target="lighthouse">lighthouse</h3>
<div class="ecosystem__submodule__description">
<div class="ecosystem__submodule__markdown">
${markdown("./documents/partials/overview/lighthouse.md")}
${await markdown("./documents/partials/overview/lighthouse.md")}
</div>
<ul class="__parents">

View file

@ -4,19 +4,19 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown.js";
// E X P O R T
export default () => `
export default async () => `
<div class="ecosystem__submodule reflector">
<h3 class="ecosystem__submodule__title" data-action="openSubmodule" data-target="reflector">reflector</h3>
<div class="ecosystem__submodule__description">
<div class="ecosystem__submodule__markdown">
${markdown("./documents/partials/overview/reflector.md")}
${await markdown("./documents/partials/overview/reflector.md")}
</div>
<ul class="__parents">

View file

@ -4,19 +4,19 @@
// U T I L
import markdown from "~component/markdown";
import markdown from "../../components/markdown.js";
// E X P O R T
export default () => `
export default async () => `
<div class="ecosystem__submodule wallet">
<h3 class="ecosystem__submodule__title" data-action="openSubmodule" data-target="wallet">wallet server</h3>
<div class="ecosystem__submodule__description">
<div class="ecosystem__submodule__markdown">
${markdown("./documents/partials/overview/wallet-server.md")}
${await markdown("./documents/partials/overview/wallet-server.md")}
</div>
<ul class="__parents">

View file

@ -4,11 +4,13 @@
// I M P O R T
import html from "choo/html";
// import html from "choo/html";
import { html } from "hono/html"
// U T I L
import config from "~root/config";
import config from "../../config.js";

View file

@ -4,32 +4,27 @@
// I M P O R T
import html from "choo/html";
import { html } from "hono/html"
// U T I L S
import editLink from "./edit-link";
import emailSubscribe from "./email-subscribe";
import editLink from "./edit-link.js";
// E X P O R T
export default state => {
if (state.hideFooter)
export default context => {
if (context.hideFooter)
return "";
return html`
<section class="email-subscribe-container">
${emailSubscribe()}
</section>
<footer class="footer">
<div class="inner-wrap">
<ul>
<li>
<a href="//${process.env.NODE_ENV === "development" ? "localhost:8000" : "lbry.com"}" title="Rediscover content freedom"> LBRY.com</a> |
${editLink(state.href)}
<a href="//lbry.org" title="Rediscover content freedom"> LBRY.org</a> |
${editLink(context.req.url)}
</li>
<li><a href="/overview" title="LBRY overview">Overview</a></li>
@ -40,6 +35,5 @@ export default state => {
</div>
</footer>
<script src="/assets/scripts/app.js"></script>
`;
};

View file

@ -13,7 +13,7 @@ const titleRegex = /(>.*<)/g;
// E X P O R T
export default (state, emit, markdown) => {
export default (context, emit, markdown) => {
const collectionOfTocElements = [];
const tocElements = markdown.match(renderedHeaderRegex);

View file

@ -1,21 +1,19 @@
"use strict";
// I M P O R T
import html from "choo/html";
// import html from "choo/html";
import { html } from 'hono/html'
// U T I L
import config from "~root/config";
import config from "../../config.js";
// E X P O R T
export default (state, emit) => {
const newMetadata = state.lbry;
export default (context) => {
const newMetadata = context.var.lbry;
const description = newMetadata && newMetadata.description ?
newMetadata.description :
config.meta.description;
@ -24,11 +22,6 @@ export default (state, emit) => {
newMetadata.title + " - lbry.tech" :
"lbry.tech - " + config.meta.tagline;
if (state.title !== title)
emit(state.events.DOMTITLECHANGE, title);
state.page = state.page || { };
return html`
<meta charset="utf-8"/>
<title>${title}</title>
@ -48,7 +41,7 @@ export default (state, emit) => {
<meta property="og:locale" content="en_US"/>
<meta property="og:site_name" content="LBRY.tech"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://lbry.tech${state.href}"/>
<meta property="og:url" content="https://lbry.tech${context.href}"/>
<!--/ Social/App Stuff /-->
<meta name="apple-mobile-web-app-title" content="${config.meta.title}"/>
@ -61,9 +54,9 @@ export default (state, emit) => {
<link rel="icon" href="/assets/favicon.svg" type="image/svg+xml"/>
<link rel="mask-icon" href="/assets/favicon.svg" color="${config.meta.color}"/>
<link rel="shortcut icon" href="/assets/favicon.ico"/>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tonsky/FiraCode@master/distr/fira_code.css"/>
<link rel="stylesheet" href="/assets/bundle.css"/>
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=inter:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet" />
<link rel="stylesheet" href="/assets/bundle.css?v=${Math.random()}"/>
<script src="/assets/scripts/sockets.js"></script>
`;

View file

@ -4,8 +4,8 @@
// I M P O R T
import html from "choo/html";
// import html from "choo/html";
import { html, raw } from 'hono/html'
// E X P O R T
@ -26,7 +26,7 @@ export default links => {
// H E L P E R
function returnLinkTemplate(title, description, destination, label) {
return `
return html`
<li class="link-grid__link">
<p class="link-grid__title"><strong>${title}</strong></p>
<p class="link-grid__description">${description}</p>

View file

@ -4,16 +4,14 @@
// I M P O R T S
import decamelize from "decamelize";
import exists from "fs-exists-sync";
// import decamelize from "decamelize";
import fs from "fs";
import fm from "front-matter";
import fs from "graceful-fs";
import html from "choo/html";
import { html, raw } from 'hono/html'
import m from "markdown-it";
import markdownAnchor from "markdown-it-anchor";
import markdownSup from "~module/markdown-it-sup";
import markdownSup from "../modules/markdown-it-sup.js";
import path from "path";
import raw from "choo/html/raw";
// U T I L S
@ -44,11 +42,11 @@ const md = m({
// E X P O R T
export default path => {
export default async path => {
const markdownFile = fs.readFileSync(path, "utf-8");
const markdownFileDetails = fm(markdownFile);
const renderedMarkdown = md.render(markdownFileDetails.body);
const updatedMarkdown = wikiFinder(partialFinder(renderedMarkdown));
const updatedMarkdown = wikiFinder(await partialFinder(renderedMarkdown));
return html`
${raw(updatedMarkdown)}
@ -59,25 +57,25 @@ export default path => {
// H E L P E R S
function partialFinder(markdownBody) {
async function partialFinder(markdownBody) {
const regexToFindPartials = /<\w+ ?\/>/g;
const partials = markdownBody.match(regexToFindPartials);
if (partials) {
for (const partial of partials) {
const filename = decamelize(partial, "-").replace("<", "")
const filename = decamelize(partial).replace("<", "")
.replace("/>", "")
.trim();
const fileExistsTest = exists(`./app/components/${filename}.js`);
const fileExistsTest = fs.existsSync(`./app/components/${filename}.js`);
if (!fileExistsTest)
markdownBody = markdownBody.replace(partial, "");
else {
const partialFunction = require(path.join(__dirname, "..", `./components/${filename}.js`));
const { default: partialFunction } = await import(import.meta.resolve(`../components/${filename}.js`));
if (filename === "glossary-toc") markdownBody = markdownBody.replace(partial, partialFunction.default);
else markdownBody = markdownBody.replace(partial, `</div>${partialFunction.default()}<div class="page__markup">`);
else markdownBody = markdownBody.replace(partial, `</div>${await partialFunction()}<div class="page__markup">`);
}
}
}
@ -95,3 +93,13 @@ function wikiFinder(markdownBody) {
match.input;
});
}
function decamelize(str) {
if (typeof str !== 'string') {
throw new TypeError('Expected a string');
}
return str
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
.toLowerCase();
}

View file

@ -4,7 +4,8 @@
// I M P O R T
import html from "choo/html";
import { html } from 'hono/html'

View file

@ -4,7 +4,8 @@
// I M P O R T
import html from "choo/html";
import { html } from "hono/html"
@ -13,15 +14,25 @@ import html from "choo/html";
export default currentUrl => {
const links = [
{
name: "LBRY.com",
name: "LBRY.org",
title: "Escape the techno scene",
url: process.env.NODE_ENV === "development" ? "http://localhost:8000" : "https://lbry.com"
url: "https://lbry.org"
},
{
name: "Overview",
title: "LBRY overview",
url: "/overview"
},
{
name: "Tutorials",
title: "LBRY Tutorials",
url: "/tutorials"
},
{
name: "Protocols",
title: "LBRY Protocols",
url: "/protocols"
},
{
name: "Playground",
title: "Experience LBRY",

19
app/components/note.js Normal file
View file

@ -0,0 +1,19 @@
"use strict";
// I M P O R T
import { html } from 'hono/html'
// E X P O R T
export default () => html`
<div class="component--note">
<strong class="component--note__title">Note</strong>
<span>LBRYCRD is deprecated and not officially supported anymore. It is recommended to use LBCD.</span>
</div>
`;

View file

@ -12,28 +12,28 @@ import {
lighthouse,
reflector,
wallet
} from "./ecosystem";
} from "./ecosystem/index.js";
// E X P O R T
export default () => `
export default async () => `
<section class="ecosystem">
<aside class="ecosystem__submodules">
${chainquery()}
${wallet()}
${await chainquery()}
${await wallet()}
</aside>
<section class="ecosystem__modules">
${lbrycrd()}
${lbry()}
${applications()}
${await lbrycrd()}
${await lbry()}
${await applications()}
</section>
<aside class="ecosystem__submodules">
${lighthouse()}
${reflector()}
${await lighthouse()}
${await reflector()}
</aside>
</section>
`;

View file

@ -1,18 +1,10 @@
"use strict";
// I M P O R T S
import dedent from "dedent";
import html from "choo/html";
import raw from "choo/html/raw";
import { html, raw } from 'hono/html'
// E X P O R T
export default () => dedent`
export default () => html`
<section class="playground">
<ul class="playground-navigation">
${raw(navigation())}
@ -43,7 +35,7 @@ function example1() {
}
function navigation() {
return dedent`
return html`
<li
class="playground-navigation__example"
data-action="playground, example 1"

View file

@ -1,12 +1,6 @@
"use strict";
// U T I L
import linkGrid from "./link-grid";
import linkGrid from "./link-grid.js";
// E X P O R T
@ -35,9 +29,4 @@ export default () => linkGrid([{
destination: "/build",
label: "Build An App",
title: "Builder's Guide"
}, {
description: "Jumpstart your LBRY development with 100 LBC, on us",
destination: "/developer-program",
label: "Get Credits",
title: "Developer Program"
}]);

View file

@ -1,43 +1,30 @@
"use strict";
// I M P O R T
import asyncHtml from "choo-async/html";
import { html } from "hono/html"
// U T I L S
import config from "~root/config";
import footer from "./footer";
import navigation from "./navigation";
import config from "../../config.js";
import footer from "./footer.js";
import navigation from "./navigation.js";
// E X P O R T
export default children => (state, emit) => {
return asyncHtml`
export default children => async (context) => {
return html`
<main>
<noscript>
<p>LBRY is quite fancy and relies on a bit of JavaScript to do these fancy things.</p>
<p>Please enable it, if you can.</p>
</noscript>
${navigation(state.href)}
${navigation(context.req.url)}
<aside class="flashes" id="flash-container"></aside>
${children.default(state, emit)}
${footer(state, emit)}
${await children(context)}
${footer(context)}
</main>
<script>
(function(i,s,o,g,r,a,m){i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,"script","https://www.google-analytics.com/analytics.js","ga");
ga("create", "${config.ga}", "auto");
ga("send", "pageview");
</script>
`;
};

BIN
app/dist/LBRYNetworkProtocolChart.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

View file

@ -25,11 +25,6 @@ function initializeWebSocketConnection() {
let data = JSON.parse(socket.data);
switch(true) {
case data.message === "github token status":
data = data.data;
syncWithApi(data); // eslint-disable-line no-undef
break;
case data.message === "notification": // TODO: Make work with appending so multiple notifications can be sent
document.getElementById("flash-container").innerHTML =
`<div class="flash active${data.type ? " " + data.type : ""}">${data.details}</div>`;
@ -93,11 +88,6 @@ function initializeWebSocketConnection() {
if (data.class)
document.querySelector(data.selector).classList.add(data.class);
if (data.selector !== "#emailMessage") {
document.getElementById("emailAddress").value = "";
document.getElementById("emailMessage").innerHTML = "";
}
if (data.example === 2) {
detectLanguageAndUpdate(); // eslint-disable-line no-undef
initCanvas(); // eslint-disable-line no-undef

View file

@ -1,23 +1,16 @@
"use strict";
// I M P O R T S
import got from "got";
import prism from "prismjs";
import raw from "choo/html/raw";
import stringifyObject from "stringify-object";
import { raw } from 'hono/html'
// U T I L S
import messageSlack from "./slack";
import publishMeme from "./publish-meme";
import lbrytvAPI from "~helper/lbrytv-sdk";
import publishMeme from "./publish-meme.js";
import lbrytvAPI from "../helpers/lbrytv-sdk.js";
import randomString from "./random-string";
import { send } from "~socket";
import uploadImage from "./upload-image";
import randomString from "./random-string.js";
import { send } from "../sockets.js";
import uploadImage from "./upload-image.js";
const allowedQueryMethods = [
"support_create",
@ -142,7 +135,7 @@ export default async(data, socket) => {
delete memePublishResponse.result.lbrytech_claim_name;
const renderedCode = prism.highlight(
stringifyObject(memePublishResponse, { indent: " ", singleQuotes: false }),
JSON.stringify(memePublishResponse, null, 2),
prism.languages.json,
"json"
);
@ -164,14 +157,6 @@ export default async(data, socket) => {
type: "error"
});
if (process.env.NODE_ENV !== "development") {
messageSlack({
message: "```" + JSON.parse(JSON.stringify(memePublishError.error)) + "```",
pretext: "_Someone is going through the Playground after a response has been parsed_",
title: `DAEMON ERROR | ${environment}`
});
}
return;
}
} catch(imageUploadError) {
@ -181,14 +166,6 @@ export default async(data, socket) => {
type: "error"
});
if (process.env.NODE_ENV !== "development") {
messageSlack({
message: "```" + imageUploadError.status + "```",
pretext: "_Someone attempted to upload a meme to the web daemon and it failed_",
title: `DAEMON ERROR | ${environment}`
});
}
return;
}
@ -204,7 +181,7 @@ export default async(data, socket) => {
if (socket) {
const renderedCode = prism.highlight(
stringifyObject(resolveResponse, { indent: " ", singleQuotes: false }),
JSON.stringify(resolveResponse, null, 2),
prism.languages.json,
"json"
);
@ -221,10 +198,8 @@ export default async(data, socket) => {
});
}
} catch(error) {
messageSlack({
message: "```" + error + "```",
title: "DAEMON ERROR: resolve"
});
console.log(error);
}
break;
@ -250,7 +225,7 @@ export default async(data, socket) => {
`https://${process.env.DAEMON_URL}/${resolveMethod}`;
try {
const response = await got(queryUrl, queryOptions);
const response = await fetch(queryUrl, queryOptions);
switch(true) {
case data.example === 3:
@ -265,7 +240,7 @@ export default async(data, socket) => {
if (socket) {
const renderedCode = prism.highlight(
stringifyObject(response.body, { indent: " ", singleQuotes: false }),
JSON.stringify(response.body, null, 2),
prism.languages.json,
"json"
);
@ -289,11 +264,6 @@ export default async(data, socket) => {
return response.body.result[Object.keys(response.body.result)[0]];
} catch(error) {
console.error(error);
messageSlack({
message: "```" + error + "```",
pretext: "_Someone is going through the Playground and the daemon is not running_",
title: `DAEMON ERROR | ${environment}`
});
}
};

View file

@ -4,44 +4,18 @@
// P A C K A G E S
import async from "async";
import color from "colorette";
import Octokit from "@octokit/rest";
import redis from "redis";
// U T I L S
import messageSlack from "~helper/slack";
import relativeDate from "~module/relative-date";
let octokit;
// R E D I S
let client;
if (process.env.GITHUB_OAUTH_TOKEN) {
octokit = new Octokit({
auth: `token ${process.env.GITHUB_OAUTH_TOKEN}`
});
} else process.stdout.write(`${color.red("[missing]")} GitHub token\n`);
if (process.env.REDISCLOUD_URL) {
client = redis.createClient(process.env.REDISCLOUD_URL);
client.on("error", redisError => {
process.env.NODE_ENV === "development" ?
process.stdout.write(`\n${color.yellow("Unable to connect to Redis client.")}\nYou may be missing an .env file or your connection was reset.`) :
messageSlack(
"\n" +
"> *REDIS ERROR:* ```" + JSON.parse(JSON.stringify(redisError)) + "```" + "\n" +
"> _Cause: Someone is trying to run LBRY.tech locally without environment variables OR Heroku is busted_\n"
)
;
});
} else process.stdout.write(`${color.red("[missing]")} Redis client URL\n`);
import relativeDate from "../modules/relative-date.js";
let githubFeed;
let lastGithubFeedUpdate;
updateGithubFeed();
// Update the feed every 5 minutes
setInterval(() => { updateGithubFeed(); }, 5 * 60 * 1000);
// P R O G R A M
@ -180,6 +154,23 @@ function generateEvent(event) {
title="View this pull request on GitHub"
>${escapeSpecialCharacters(event.payload.pull_request.title)}</a></em> in
`;
case "PullRequestReviewEvent":
return `
<strong><a
href="${generateUrl("actor", event)}"
rel="noopener noreferrer"
target="_blank"
title="Visit ${event.actor.login}'s profile on GitHub"
>${event.actor.display_login}</a></strong> reviewed pull request
<em><a
href="${generateUrl("pull_request", event)}"
rel="noopener noreferrer"
target="_blank"
title="View this review on GitHub"
>${escapeSpecialCharacters(event.payload.pull_request.title)}</a></em> in
`;
case "PullRequestReviewCommentEvent":
return `
@ -247,49 +238,38 @@ function generateEvent(event) {
}
}
function generateGitHubFeed(displayGitHubFeed) {
if (process.env.REDISCLOUD_URL) {
client.zrevrange("events", 0, 9, (err, reply) => {
if (err) return; // TODO: Render a div with nice error message
async function generateGitHubFeed(displayGitHubFeed) {
await githubFeed;
if (!githubFeed) return;
const events = [];
const renderedEvents = [];
const renderedEvents = [];
reply.forEach(item => events.push(JSON.parse(item)));
for (const event of githubFeed) {
const repoName = `
<a href="${generateUrl("repo", event)}" title="View this repo on GitHub" target="_blank" rel="noopener noreferrer"><strong>${event.repo.name}</strong></a>
`;
for (const event of events) {
const repoName = `
<a href="${generateUrl("repo", event)}" title="View this repo on GitHub" target="_blank" rel="noopener noreferrer"><strong>${event.repo.name}</strong></a>
`;
renderedEvents.push(`
<div class='github-feed__event'>
<a href="${generateUrl("actor", event)}" target="_blank" rel="noopener noreferrer">
<img src="${event.actor.avatar_url}" class="github-feed__event__avatar" alt="${event.actor.login}'s avatar"/>
</a>
renderedEvents.push(`
<div class='github-feed__event'>
<a href="${generateUrl("actor", event)}" target="_blank" rel="noopener noreferrer">
<img src="${event.actor.avatar_url}" class="github-feed__event__avatar" alt="${event.actor.login}'s avatar"/>
</a>
<p>
${generateEvent(event)}
${event.type !== "ForkEvent" ? repoName : ""}
<em class="github-feed__event__time">${relativeDate(new Date(event.created_at))}</em>
</p>
</div>
`);
}
updateGithubFeed(); // TODO: Update `.last-updated` every minute
displayGitHubFeed(`
<h3>GitHub</h3>
<h5 class="last-updated">Last updated: ${new Date().format("YYYY-MM-DD")
.replace(/-/g, "&middot;")} at ${new Date().add(-4, "hours")
.format("UTC:H:mm:ss A")
.toLowerCase()} EST</h5>
${renderedEvents.join("")}
`);
});
<p>
${generateEvent(event)}
${event.type !== "ForkEvent" ? repoName : ""}
<em class="github-feed__event__time">${relativeDate(new Date(event.created_at))}</em>
</p>
</div>
`);
}
displayGitHubFeed(`
<h3>GitHub</h3>
<h5 class="last-updated">Last updated: ${lastGithubFeedUpdate.date} at ${lastGithubFeedUpdate.time} UTC</h5>
${renderedEvents.join("")}
`);
}
function generateUrl(type, event) {
@ -326,33 +306,28 @@ function generateUrl(type, event) {
}
}
function updateGithubFeed() {
octokit.activity.listPublicEventsForOrg({
org: "lbryio",
per_page: 20,
page: 1
}).then(({ data }) => {
async.eachSeries(data, (item, callback) => {
const eventString = JSON.stringify(item);
async function updateGithubFeed() {
let response;
client.zrank("events", eventString, (err, reply) => {
if (err)
return;
if (reply === null)
client.zadd("events", item.id, eventString, callback);
else
callback();
});
}, () => client.zremrangebyrank("events", 0, -51)); // Keep the latest 50 events
})
.catch(err => {
messageSlack(
"\n" +
"> *GITHUB FEED ERROR:* ```" + JSON.parse(JSON.stringify(err)) + "```" + "\n" +
"> _Cause: GitHub feed refresh_\n"
);
try {
response = await fetch(`https://api.github.com/orgs/lbryfoundation/events`, process.env.GITHUB_TOKEN && {
headers: {
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`
}
});
} catch (err) {
console.log(err);
return;
}
githubFeed = await response.json();
const now = new Date();
lastGithubFeedUpdate = {
date: now.toISOString().split("T")[0],
time: now.toLocaleTimeString('en-US', {
timeZone: 'UTC'
})
};
}

View file

@ -1,107 +1,81 @@
"use strict";
import messageSlack from "./slack";
const request = require("request");
const addSupport = function () { };
const publish = function () { };
const addSupport = function() {};
const publish = function() {};
const resolve = function(urls) {
return new Promise(function(resolve, reject) {
const resolve = function (urls) {
return new Promise(async (resolve, reject) => {
const options = {
method: "POST",
url: "https://api.lbry.tv/api/v1/proxy",
headers:
{
"Content-Type": "application/json"
},
body: {
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
method: "resolve",
params: { urls: urls }
},
json: true
})
};
request(options, function(error, response, daemonResponse) {
if (error) {
messageSlack({
message: "```" + error + "```",
title: "DAEMON ERROR: resolve"
});
return reject(new Error("DAEMON ERROR: resolve"));
}
const response = await fetch("https://api.na-backend.odysee.com/api/v1/proxy", options);
let json;
if (!response.ok) return reject(new Error("DAEMON ERROR: resolve"));
if (Object.prototype.hasOwnProperty.call(daemonResponse, "error")) {
messageSlack({
message: "```" + daemonResponse.error + "```",
title: "DAEMON ERROR: resolve"
});
return reject(new Error("DAEMON ERROR: resolve"));
} else
return resolve(daemonResponse.result);
});
try {
json = await response.json();
} catch (err) {
return reject(new Error("DAEMON ERROR: resolve"));
}
return resolve(json.result);
});
};
const getTrending = function() {
return new Promise(function(resolve, reject) {
const getTrending = function () {
return new Promise(async (resolve, reject) => {
const options = {
method: "POST",
url: "https://api.lbry.tv/api/v1/proxy",
headers:
{
"Content-Type": "application/json"
},
body:
{
method: "claim_search",
params:
{
page_size: 20,
page: 1,
no_totals: true,
any_tags:
["art",
"automotive",
"blockchain",
"comedy",
"economics",
"education",
"gaming",
"music",
"news",
"science",
"sports",
"technology"],
channel_ids: [],
not_channel_ids: [],
not_tags: ["porn", "nsfw", "mature", "xxx"],
order_by: ["trending_group", "trending_mixed"]
}
},
json: true
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
method: "claim_search",
params: {
page_size: 20,
page: 1,
no_totals: true,
any_tags:
["art",
"automotive",
"blockchain",
"comedy",
"economics",
"education",
"gaming",
"music",
"news",
"science",
"sports",
"technology"],
channel_ids: [],
not_channel_ids: [],
not_tags: ["porn", "porno", "nsfw", "mature", "xxx", "sex", "creampie", "blowjob", "handjob", "vagina", "boobs", "big boobs", "big dick", "pussy", "cumshot", "anal", "hard fucking", "ass", "fuck", "hentai"],
order_by: ["trending_group", "trending_mixed"]
}
})
};
request(options, function(error, response, daemonResponse) {
if (error) {
messageSlack({
message: "```" + error + "```",
title: "DAEMON ERROR: trending"
});
return reject(new Error("DAEMON ERROR: trending"));
}
const response = await fetch("https://api.na-backend.odysee.com/api/v1/proxy", options);
let json;
if (!response.ok) return reject(new Error("DAEMON ERROR: resolve"));
if (Object.prototype.hasOwnProperty.call(daemonResponse, "error")) {
messageSlack({
message: "```" + daemonResponse.error + "```",
title: "DAEMON ERROR: trending"
});
return reject(JSON.stringify(daemonResponse));
} else
return resolve(daemonResponse.result.items);
});
try {
json = await response.json();
} catch (err) {
return reject(new Error("DAEMON ERROR: resolve"));
}
return resolve(json.result.items);
});
};

View file

@ -4,7 +4,6 @@
// I M P O R T
import got from "got";
// U T I L
@ -18,6 +17,7 @@ const queryUrl = process.env.NODE_ENV === "development" ?
export default async(publishMetadata) => {
const options = {
method: 'PUT',
body: {
authorization: process.env.LBRY_DAEMON_ACCESS_TOKEN,
metadata: publishMetadata
@ -26,7 +26,7 @@ export default async(publishMetadata) => {
};
try {
const response = await got.put(queryUrl, options);
const response = await fetch(queryUrl, options);
return response.body; // eslint-disable-line padding-line-between-statements
} catch(error) {
return error;

View file

@ -1,41 +0,0 @@
"use strict";
// I M P O R T
import { IncomingWebhook } from "@slack/client";
// U T I L S
require("dotenv").config();
const environmentMessage = process.env.NODE_ENV === "development" ?
"\n_— in DEVELOPMENT_" :
"\n_— in PRODUCTION_";
const slackUrl = process.env.SLACK_WEBHOOK_URL || "";
const slackWebhook = new IncomingWebhook(slackUrl);
// P R O G R A M
export default ({ message, pretext, title }) => {
if (!slackUrl) return;
pretext = pretext || "" + environmentMessage;
slackWebhook.send({
attachments: [{
mrkdwn_in: [
"text",
"pretext"
],
pretext: pretext || "",
text: message || "",
title: title || ""
}]
}, (error, response) => { // eslint-disable-line no-unused-vars
if (error) console.log(error); // eslint-disable-line no-console
});
};

View file

@ -4,7 +4,6 @@
// I M P O R T
import got from "got";
// U T I L
@ -18,6 +17,7 @@ const queryUrl = process.env.NODE_ENV === "development" ?
export default async(imageSource) => {
const options = {
method: 'POST',
body: {
authorization: process.env.LBRY_DAEMON_ACCESS_TOKEN,
image: imageSource
@ -26,7 +26,7 @@ export default async(imageSource) => {
};
try {
const response = await got.post(queryUrl, options);
const response = await fetch(queryUrl, options);
return response.body; // eslint-disable-line padding-line-between-statements
} catch(error) {
return error;

166
app/index.js Executable file → Normal file
View file

@ -1,101 +1,89 @@
"use strict";
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { createBunWebSocket } from 'hono/bun';
import { serveStatic } from "@hono/node-server/serve-static";
import { secureHeaders } from 'hono/secure-headers';
import { readFileSync } from 'fs';
import client from "./client.js";
import handleSocketMessages from "./sockets.js";
import dotenv from "dotenv";
if (!process.versions.bun) dotenv.config();
const { upgradeWebSocket, websocket } =
createBunWebSocket()
// P A C K A G E S
const redirects = JSON.parse(readFileSync('./app/data/redirects.json', 'utf8'));
import * as color from "colorette";
import compress from "fastify-compress";
import cors from "cors";
import fastify from "fastify";
import helmet from "fastify-helmet";
import ssr from "choo-ssr/fastify";
import statik from "fastify-static";
import websockets from "@inc/fastify-ws";
const app = new Hono({ strict: true });
// U T I L S
import handleSocketMessages from "./sockets";
import messageSlack from "~helper/slack";
import redirects from "~data/redirects.json";
const server = fastify({
logger: {
level: "warn",
prettyPrint: process.env.NODE_ENV === "development",
redact: ["req.headers.authorization"],
serializers: {
req(req) {
return {
headers: req.headers,
hostname: req.hostname,
method: req.method,
remoteAddress: req.ip,
remotePort: req.connection.remotePort,
url: req.url
};
}
}
// Own trimTrailingSlash function because hono's middleware doesn't work?
app.use(async (c, next)=>{
if ((c.req.method === "GET" || c.req.method === "HEAD") && c.req.path !== "/" && c.req.path.at(-1) === "/") {
const url = new URL(c.req.url);
url.pathname = url.pathname.substring(0, url.pathname.length - 1);
c.res = c.redirect(url.toString(), 301);
}
});
await next();
})
app.use(secureHeaders())
// P R O G R A M
server
.use(cors())
.register(compress)
.register(websockets)
.register(helmet, {
hidePoweredBy: {
setTo: "LBRY"
// Mount websocket
app.get(
'/',
upgradeWebSocket((c) => {
return {
onMessage(event, ws) {
return handleSocketMessages(ws, JSON.parse(event.data));
},
onClose: () => {
// console.log('Connection closed')
},
}
})
.register(statik, {
prefix: "/assets/",
root: `${__dirname}/dist/`
);
// Mount static files
app.get(
"/assets/*",
serveStatic({
root: "./app/dist",
rewriteRequestPath: (path) => {
// return path
return path.replace(/^\/assets/, "/");
}
})
)
// Mount redirects
app.use('*', async (c, next)=>{
if (Object.keys(redirects).includes(c.req.path)) return c.redirect(redirects[c.req.path])
await next();
})
app.route("/", client);
if (!process.versions.bun) {
serve({
fetch: app.fetch,
port: process.env.PORT || 8080
})
.register(ssr, {
app: require("./client")
})
.addHook("preHandler", (request, reply, next) => {
if (redirects[request.raw.originalUrl])
reply.redirect(301, redirects[request.raw.originalUrl]);
process.stdout.write(`\n— ⚡ ${process.env.PORT || 8080}\n`);
}
if (process.env.NODE_ENV !== "development") {
if (request.headers["x-forwarded-proto"] !== "https")
reply.redirect(302, `https://${request.raw.hostname}${request.raw.originalUrl}`);
else
next();
}
next();
})
.ready(err => {
if (err)
throw err;
server.ws.on("connection", socket => {
socket.on("message", data => {
data = JSON.parse(data);
return handleSocketMessages(socket, data);
});
socket.on("close", () => socket.terminate());
});
});
// B E G I N
server.listen(process.env.PORT || 8080, process.env.IP || "0.0.0.0", async() => {
process.env.NODE_ENV === "development" ?
process.stdout.write(`\n${color.green("⚡")} ${server.server.address().port}\n`) :
messageSlack({
message: `Server started at port \`${server.server.address().port}\``,
title: "APP BOOT"
});
});
export default {
fetch: app.fetch,
websocket,
port: process.env.PORT || 8080
}

View file

@ -86,6 +86,6 @@ function superscript(state, silent) {
// E X P O R T
module.exports = exports = function sup_plugin(md) { // eslint-disable-line camelcase
export default function sup_plugin(md) { // eslint-disable-line camelcase
md.inline.ruler.after("emphasis", "sup", superscript);
};

View file

@ -52,5 +52,4 @@ const relativeDate = (() => {
// E X P O R T
if (typeof module !== "undefined" && module.exports)
module.exports = exports = relativeDate;
export default relativeDate;

View file

@ -1,12 +1,11 @@
@charset "utf-8";
@import "@lbry/components/sass/";
@import "@lbry/components/sass/index";
@import "init/markdown";
@import "init/extends";
@import "partials/animation";
@import "partials/ecosystem";
@import "partials/email-subscribe";
@import "partials/feature-links";
@import "partials/flash";
@import "partials/footer";
@ -16,13 +15,13 @@
@import "partials/navigation";
@import "partials/mission-statement";
@import "partials/modal";
@import "partials/note";
@import "partials/pre";
@import "layout";
@import "pages/api";
@import "pages/contributing";
@import "pages/developer";
@import "pages/documentation";
@import "pages/home";
@import "pages/page";

View file

@ -1,33 +0,0 @@
developer-program {
@extend %markdown;
input-submit {
padding-top: 0.5rem;
button {
color: $lbry-white;
padding-right: 1.5rem;
padding-left: 1.5rem;
transition: background-color 0.2s;
&:not(:hover) {
background-color: $lbry-black;
}
&:hover {
background-color: $lbry-teal-4;
}
}
}
.button {
margin: 1rem auto;
display: block;
}
small {
display: block;
font-size: 0.8rem;
text-align: center;
}
}

View file

@ -1,137 +0,0 @@
.newsletter-cta {
background-color: $lbry-gray-1;
padding-top: 1rem;
padding-bottom: 1rem;
text-align: center;
> div:first-of-type {
margin-right: auto;
margin-left: auto;
@media (min-width: 551px) {
width: 500px;
}
@media (max-width: 550px) {
width: 90%;
}
}
&::after {
@include clearfix;
}
}
.newsletter-cta__title {
font-size: 0.8rem;
letter-spacing: 0.05rem;
margin-bottom: 0.75rem;
text-transform: uppercase;
}
.newsletter-cta__input,
.newsletter-cta__submit {
@extend .__button-padding-horizontal;
border-style: solid;
border-width: 1px;
@media (max-width: 550px) {
width: 100%;
}
}
.newsletter-cta__input {
height: 38px;
background-color: $lbry-white;
font-size: 1rem;
transition: border 0.2s;
@media (min-width: 551px) {
width: calc(100% - 112px);
float: left;
}
@media (max-width: 550px) {
text-align: center;
}
&:not(:focus) {
border-top-color: $lbry-black;
border-left-color: $lbry-black;
@media (min-width: 551px) {
border-right-color: transparent;
border-bottom-color: $lbry-black;
}
@media (max-width: 550px) {
border-right-color: $lbry-black;
border-bottom-color: transparent;
}
}
&:focus {
border-top-color: $lbry-teal-5;
border-left-color: $lbry-teal-5;
@media (min-width: 551px) {
border-right-color: transparent;
border-bottom-color: $lbry-teal-5;
}
@media (max-width: 550px) {
border-right-color: $lbry-teal-5;
border-bottom-color: transparent;
}
}
}
.newsletter-cta__submit {
@extend .__button-basic;
@extend .__button-padding-vertical;
color: $lbry-white;
@media (min-width: 551px) {
width: 112px; height: 38px;
left: -1px;
float: right;
}
@media (max-width: 550px) {
top: -1px;
display: block;
}
&:not(:hover) {
background-color: $lbry-black;
border-color: $lbry-black;
}
&:hover {
background-color: $lbry-teal-3;
border-color: $lbry-teal-5;
}
}
.newsletter-cta__message {
@include clearfix;
color: $lbry-white;
cursor: default;
display: inline-block;
font-size: 0.8rem;
text-align: center;
&:not(:empty) {
margin: 0.5rem auto 0; padding: 0.25rem 1rem;
}
&:not(.error) {
background-color: $lbry-teal-3;
}
&.error {
background-color: $lbry-red-3;
}
}

View file

@ -65,7 +65,7 @@
flex: 1;
}
&:not(:first-of-type):not([href="http://localhost:8000"]):not([href="https://lbry.com"]) {
&:not(:first-of-type):not([href="http://localhost:8000"]):not([href="https://lbry.org"]) {
&::after {
width: 100%; height: 3px;
left: 0;
@ -96,13 +96,13 @@
line-height: 4rem;
}
&:not([href="http://localhost:8000"]):not([href="https://lbry.com"]) {
&:not([href="http://localhost:8000"]):not([href="https://lbry.org"]) {
padding-right: 0.5rem;
padding-left: 0.5rem;
}
&[href="http://localhost:8000"],
&[href="https://lbry.com"] {
&[href="https://lbry.org"] {
color: $lbry-white;
margin-right: 0.5rem;
padding-right: 1rem;

View file

@ -0,0 +1,29 @@
.component--note {
padding: 1rem 1rem 1rem 1.25rem;
background-color: #ffa50050;
border-left: 0.5rem solid #ffa500;
line-height: 1.55;
margin-bottom: 1.5rem;
position: relative;
user-select: text;
-ms-user-select: text;
-moz-user-select: text;
-webkit-user-select: text;
@media (min-width: 901px) {
font-size: 1.25rem;
}
@media (max-width: 900px) {
font-size: 1.05rem;
}
}
.component--note__title {
color: #ffa500;
display: block;
letter-spacing: 0.025rem;
text-transform: uppercase;
}

View file

@ -1,28 +1,15 @@
"use strict";
// I M P O R T S
import got from "got";
import html from "choo/html";
import { html } from 'hono/html'
// U T I L S
import apiPage from "~view/api";
import fetchMetadata from "~helper/fetch-metadata";
import lbrytvAPI from "~helper/lbrytv-sdk";
import { generateGitHubFeed } from "~helper/github";
import messageSlack from "~helper/slack";
import apiPage from "./views/api.js";
import fetchMetadata from "./helpers/fetch-metadata.js";
import lbrytvAPI from "./helpers/lbrytv-sdk.js";
import { generateGitHubFeed } from "./helpers/github.js";
import { URL } from "url";
const githubAppId = process.env.GITHUB_APP_ID;
const githubAppSecret = process.env.GITHUB_APP_SECRET;
// const githubAppId = process.env.GITHUB_APP_ID_TEST;
// const githubAppSecret = process.env.GITHUB_APP_SECRET_TEST;
// P R O G R A M
@ -31,14 +18,6 @@ export default async(socket, action) => {
return;
switch(true) {
case action.message === "auth me with github":
getGitHubUserToken(socket);
break;
case action.message === "verify github token":
verifyGitHubToken(action, socket);
break;
case action.message === "fetch metadata":
fetchMetadata(action, socket);
break;
@ -87,11 +66,6 @@ export default async(socket, action) => {
});
break;
case action.message === "subscribe":
newsletterSubscribe(action, socket);
break;
case action.message === "view different documentation version":
send(socket, {
element: "div",
@ -134,7 +108,7 @@ function generateContent(exampleNumber, displayTrendingContent) {
for (const r in responses) {
const part = responses[r];
if (part.value && part.value.thumbnail.url) {
if (part.value && part.value.thumbnail && part.value.thumbnail.url) {
renderedContentCollection.push(`
<section class="playground-content__trend">
<figure
@ -359,13 +333,6 @@ function generateMemeCreator(socket) {
});
}
function getGitHubUserToken(socket) {
send(socket, {
message: "redirect",
url: `https://github.com/login/oauth/authorize?client_id=${githubAppId}&scope=public_repo,user:email`
});
}
function makeImageSourceSecure(url) {
if (!url || !url.length)
return url;
@ -378,88 +345,6 @@ function makeImageSourceSecure(url) {
return originalUrl.href;
}
async function newsletterSubscribe(data, socket) {
const email = data.email;
if (!validateEmail(email)) {
send(socket, {
class: "error",
html: "Your email address is invalid",
message: "updated html",
selector: "#emailMessage"
});
}
try {
await got.post(`https://api.lbry.com/list/subscribe?email=${encodeURIComponent(email)}&tag=developer`);
return send(socket, {
html: "Thank you! Please confirm subscription in your inbox.",
message: "updated html",
selector: "#emailMessage"
});
} catch(error) {
const response = JSON.parse(error.body);
if (!response.success) {
messageSlack({
message: `via ${email}: ${response.error}`,
title: "NEWSLETTER ERROR"
});
return send(socket, {
class: "error",
html: response.error,
message: "updated html",
selector: "#emailMessage"
});
}
messageSlack({
message: `via ${email} (strange error): ${response.error}`,
title: "NEWSLETTER ERROR"
});
return send(socket, {
class: "error",
html: "Something is terribly wrong",
message: "updated html",
selector: "#emailMessage"
});
}
}
export function send(transport, data) {
return transport.send(JSON.stringify(data));
}
function validateEmail(email) {
const emailRegex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\\.,;:\s@"]{2,})$/i;
return emailRegex.test(String(email)); // eslint-disable-line padding-line-between-statements
}
async function verifyGitHubToken(data, socket) {
const code = data.code;
try {
const result = await got.post(`https://github.com/login/oauth/access_token?client_id=${githubAppId}&client_secret=${githubAppSecret}&code=${code}`, { json: true });
const response = {
address: data.address,
code: result.body.access_token
};
return send(socket, {
data: response,
message: "github token status"
});
} catch(verificationError) {
console.log(verificationError.body); // eslint-disable-line no-console
return send(socket, {
details: verificationError.body,
message: "notification",
type: "error"
});
}
}

View file

@ -1,10 +1,6 @@
"use strict";
// I M P O R T
import html from "choo/html";
import { html } from 'hono/html'

View file

@ -1,42 +1,34 @@
"use strict";
// I M P O R T S
import asyncHtml from "choo-async/html";
import dedent from "dedent";
import got from "got";
import Octokit from "@octokit/rest";
import { html } from 'hono/html'
// U T I L S
import headerBlockchain from "~component/api/header-blockchain";
import headerSdk from "~component/api/header-sdk";
import redirects from "~data/redirects.json";
import headerBlockchain from "../components/api/header-blockchain.js";
import headerSdk from "../components/api/header-sdk.js";
import { readFileSync } from 'fs';
// import redirects from './app/data/redirects.json' assert { type: 'json' };
const redirects = JSON.parse(readFileSync('./app/data/redirects.json', 'utf8'));
const cache = new Map();
const filePathBlockchain = "/contrib/devtools/generated/api_v1.json";
const filePathSdk = "/docs/api.json";
const rawGitHubBase = "https://raw.githubusercontent.com/lbryio/";
const octokit = new Octokit({
auth: `token ${process.env.GITHUB_OAUTH_TOKEN}`
});
const filePathSdk = "docs/api.json";
const rawGitHubBase = "https://cdn.jsdelivr.net/gh/lbryfoundation/";
// E X P O R T
export default async(state) => {
const { tag } = state;
const { wildcard } = state.params;
export default async(context) => {
const { tag } = context;
const wildcard = context.req.param('wildcard');
const repository = wildcard === "sdk" ?
"lbry-sdk" :
"lbrycrd";
state.lbry = {
context.var.lbry = {
title: tag ? tag + " API Documentation" : "API Documentation",
description: "See API documentation, signatures, and sample calls for the LBRY APIs."
};
@ -47,11 +39,11 @@ export default async(state) => {
try {
const apiResponse = await parseApiFile({ repo: repository, tag: currentTag });
return asyncHtml`
return html`
<div class="__slate">
<aside class="api-toc">
<select class="api-toc__select" onchange="changeDocumentationVersion(value);">
${renderVersionSelector(wildcard, tags, tag)}
${html(await renderVersionSelector(wildcard, tags, tag))}
</select>
<div class="api-toc__search">
@ -61,7 +53,7 @@ export default async(state) => {
</div>
<ul class="api-toc__commands" id="toc" role="navigation">
${wildcard === "sdk" ? createSdkSidebar(apiResponse) : createApiSidebar(apiResponse)}
${html(await wildcard === "sdk" ? createSdkSidebar(apiResponse) : createApiSidebar(apiResponse))}
</ul>
</aside>
@ -70,11 +62,11 @@ export default async(state) => {
<div></div>
<nav class="api-content__items">
${renderCodeLanguageToggles(wildcard)}
${html(await renderCodeLanguageToggles(wildcard))}
</nav>
${createApiHeader(wildcard, currentTag)}
${wildcard === "sdk" ? createSdkContent(apiResponse) : createApiContent(apiResponse)}
${html(await createApiHeader(wildcard, currentTag))}
${html(await wildcard === "sdk" ? createSdkContent(apiResponse) : createApiContent(apiResponse))}
</div>
</section>
@ -92,9 +84,10 @@ export default async(state) => {
</div>
`;
} catch(error) {
const redirectUrl = redirects[state.href];
console.log(error);
const redirectUrl = redirects[context.req.url];
return asyncHtml`
return html(await `
<article class="page" itemtype="http://schema.org/BlogPosting">
<header class="page__header">
<div class="page__header-wrap">
@ -116,7 +109,7 @@ export default async(state) => {
window.location.href = "${redirectUrl}";
}, 2000);
</script>
`;
`);
}
};
@ -238,10 +231,12 @@ function createSdkSidebar(apiDetails) {
}
async function getTags(repositoryName) {
const { data } = await octokit.repos.listTags({
owner: "lbryio",
repo: repositoryName
});
const {versions: data} = await (await fetch(`https://data.jsdelivr.com/v1/packages/gh/lbryfoundation/${repositoryName}`)).json();
// const { data } = await octokit.repos.listTags({
// owner: "lbryio",
// repo: repositoryName
// });
const tags = ["master"];
@ -254,19 +249,20 @@ async function getTags(repositoryName) {
switch(true) {
case repositoryName === "lbry-sdk":
data.forEach(tag => {
if (tag.name >= "v0.52.0") tags.push(tag.name);
if (tag.version >= "v0.52.0") tags.push(tag.version);
});
break;
case repositoryName === "lbrycrd":
data.forEach(tag => {
if (
tag.name >= "v0.17.1.0" &&
tag.name !== "v0.3.16" &&
tag.name !== "v0.3.15" &&
tag.name !== "v0.3-osx" &&
tag.name !== "v0.2-alpha"
) tags.push(tag.name);
tag.version >= "v0.17.1.0" &&
tag.version !== "v0.3.16" &&
tag.version !== "v0.3.15" &&
tag.version !== "v0.3-osx" &&
tag.version !== "v0.2-alpha"
)
tags.push(tag.version);
});
break;
@ -278,7 +274,7 @@ async function getTags(repositoryName) {
}
async function parseApiFile({ repo, tag }) {
let apiFileLink = `${rawGitHubBase}${repo}/${tag}`;
let apiFileLink = `${rawGitHubBase}${repo}@${tag}/`;
switch(true) {
case (repo === "lbrycrd"):
@ -293,10 +289,17 @@ async function parseApiFile({ repo, tag }) {
return Promise.reject(new Error("Failed to fetch API docs"));
}
const response = await got(apiFileLink, { cache, json: true });
// if (cache.has(apiFileLink)) {
// console.log("Using cache for " + apiFileLink);
// return cache.get(apiFileLink);
// }
const response = await fetch(apiFileLink);
try {
return response.body;
const json = response.json();
// cache.set(apiFileLink, json);
return json;
} catch(error) {
return "Issue loading API documentation";
}
@ -396,3 +399,24 @@ function renderCodeLanguageToggles(pageSlug) {
onSdkPage ? "<button class='api-content__item' id='toggle-python' type='button'>python</button>" : ""
];
}
function dedent(string) {
// Split into lines
const lines = string.split('\n');
if (lines[0].trim() === '') lines.shift();
if (lines.length > 0 && lines[lines.length - 1].trim() === '') lines.pop();
const indents = lines
.slice(1)
.filter(line => line.trim() !== '')
.map(line => line.match(/^\s*/)[0].length);
const minIndent = indents.length > 0 ? Math.min(...indents) : 0;
return lines.map(line => {
const leadingWhitespace = line.match(/^\s*/)[0];
if (leadingWhitespace.length >= minIndent) {
return line.slice(minIndent);
}
return line;
}).join('\n');
}

View file

@ -1,14 +1,11 @@
"use strict";
// I M P O R T
import html from "choo/html";
// import html from "choo/html";
import { html } from 'hono/html'
// U T I L
import linkGrid from "~component/link-grid";
import linkGrid from "../components/link-grid.js";

View file

@ -1,27 +1,23 @@
"use strict";
// I M P O R T S
import fm from "front-matter";
import fs from "graceful-fs";
import html from "choo/html";
import raw from "choo/html/raw";
import fs from "fs";
import { html, raw } from 'hono/html'
// U T I L S
import markdown from "~component/markdown";
import page404 from "./404";
import markdown from "../components/markdown.js";
import page404 from "./404.js";
// E X P O R T
export default (state, emit) => { // eslint-disable-line
const partialPath = state.route === "resources/*" ?
`resources/${state.params.wildcard}` :
state.params.wildcard;
export default (context) => { // eslint-disable-line
const partialPath = /^\/resources\/.*/.test(context.req.path) ?
context.req.path :
context.req.path.slice(1);
const path = `./documents/${partialPath}.md`;
@ -43,11 +39,11 @@ export default (state, emit) => { // eslint-disable-line
}
}
state.lbry = customMetadata;
context.var.lbry = customMetadata;
}
// below is evil, I just inherited it -- Jeremy
state.lbry = {
context.var.lbry = {
title: title,
description: description
};
@ -56,10 +52,6 @@ export default (state, emit) => { // eslint-disable-line
let pageScript = "";
switch(true) {
case partialPath === "developer-program":
pageScript = renderClientScript("devprogram-scripts");
break;
case partialPath === "glossary":
pageScript = renderClientScript("glossary-scripts");
break;

View file

@ -1,18 +1,12 @@
"use strict";
// I M P O R T
import html from "choo/html";
import { html } from 'hono/html'
// E X P O R T
export default state => {
state.hideFooter = true;
state.lbry = {
export default context => {
context.var.hideFooter = true;
context.var.lbry = {
title: "LBRY Specification",
description: "A detailed specification of the LBRY protocol. Learn exactly what LBRY is and how it works!"
};
@ -34,7 +28,7 @@ export default state => {
return;
const url = window.location.href.substr(0, window.location.href.lastIndexOf("#"));
history.replaceState(null, null, url + "#" + event.data);
history.replaceconst(null, null, url + "#" + event.data);
});
</script>
`;

949
bun.lock Normal file
View file

@ -0,0 +1,949 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "lbry.tech",
"dependencies": {
"@elysiajs/node": "^1.2.6",
"@elysiajs/static": "^1.2.0",
"@hono/node-server": "^1.14.1",
"date-format-lite": "^17.7.0",
"dotenv": "^8.6.0",
"elysia": "^1.2.25",
"front-matter": "^4.0.2",
"hono": "^4.7.7",
"markdown-it": "^14.1.0",
"markdown-it-anchor": "^9.2.0",
"pino-pretty": "^3.2.0",
"prismjs": "^1.30.0",
"socket.io": "^4.8.1",
},
"devDependencies": {
"@lbry/components": "^2019.6.22",
"@springernature/sasslint-config": "^1.2.1",
"eslint": "^6.1.0",
"eslint-config": "^0.2.1",
"husky": "^3.0.2",
"sass": "^1.87.0",
"sass-lint": "^1.13.1",
},
},
},
"packages": {
"@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="],
"@elysiajs/node": ["@elysiajs/node@1.2.6", "", { "dependencies": { "formidable": "^3.5.2", "ws": "^8.18.0" }, "peerDependencies": { "bufferutil": ">= 4.0.1", "elysia": ">= 1.2.7", "formidable": ">= 3.5.2", "ws": ">= 8.18.0" } }, "sha512-qauTs0YNLvfSyqW8k8pDCazd3nPQtFOeDH/cSz1wBhAGn1HL1PQlywnK6V0+bOGemkKlxLIjhmvbHxGrNzZMSg=="],
"@elysiajs/static": ["@elysiajs/static@1.2.0", "", { "dependencies": { "node-cache": "^5.1.2" }, "peerDependencies": { "elysia": ">= 1.2.0" } }, "sha512-oLpAi8c+maPpA0XhhK3BELaIjIG+nXg/K9p8cFfW4q5ayRD59a3MOMOOGgpiXZkHJzLPWcouhhyyLAYtaANW4g=="],
"@hapi/bourne": ["@hapi/bourne@1.3.2", "", {}, "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA=="],
"@hono/node-server": ["@hono/node-server@1.14.1", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vmbuM+HPinjWzPe7FFPWMMQMsbKE9gDPhaH0FFdqbGpkT5lp++tcWDTxwBl5EgS5y6JVgIaCdjeHRfQ4XRBRjQ=="],
"@lbry/components": ["@lbry/components@2019.6.22", "", {}, "sha512-hO2G3nObQEa5LkR2pr28NFRVSGDnBur9bQP1HbVzG2CgZmRIvMO1k+TDnQyLnXyirKu5frPsiq+CLBJzH4ZW9g=="],
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
"@paralleldrive/cuid2": ["@paralleldrive/cuid2@2.2.2", "", { "dependencies": { "@noble/hashes": "^1.1.5" } }, "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA=="],
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="],
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw=="],
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg=="],
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ=="],
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA=="],
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.1", "", { "os": "linux", "cpu": "arm" }, "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q=="],
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w=="],
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg=="],
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A=="],
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.1", "", { "os": "linux", "cpu": "x64" }, "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg=="],
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw=="],
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ=="],
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
"@sinclair/typebox": ["@sinclair/typebox@0.34.33", "", {}, "sha512-5HAV9exOMcXRUxo+9iYB5n09XxzCXnfy4VTNW4xnDv+FgjzAGY989C28BIdljKqmF+ZltUwujE3aossvcVtq6g=="],
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
"@springernature/sasslint-config": ["@springernature/sasslint-config@1.2.1", "", {}, "sha512-q89ubstXP0WQRqJaQLkAGW0znp2oClKYju9ssrZmpZrVUeSH5CIGCGMPKQSx+LkUJCls4tUCNIrRWBZIlsXfgw=="],
"@types/cors": ["@types/cors@2.8.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA=="],
"@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
"@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="],
"@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
"@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="],
"@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="],
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
"acorn": ["acorn@7.4.1", "", { "bin": "bin/acorn" }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"ajv-keywords": ["ajv-keywords@1.5.1", "", { "peerDependencies": { "ajv": ">=4.10.0" } }, "sha512-vuBv+fm2s6cqUyey2A7qYcvsik+GMDJsw8BARP2sDE76cqmaZVarsvHf7Vx6VJ0Xk8gLl+u3MoAPf6gKzJefeA=="],
"ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
"ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="],
"ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"args": ["args@5.0.3", "", { "dependencies": { "camelcase": "5.0.0", "chalk": "2.4.2", "leven": "2.1.0", "mri": "1.1.4" } }, "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA=="],
"asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="],
"astral-regex": ["astral-regex@1.0.0", "", {}, "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"bufferutil": ["bufferutil@4.0.9", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw=="],
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
"caller-callsite": ["caller-callsite@2.0.0", "", { "dependencies": { "callsites": "^2.0.0" } }, "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ=="],
"caller-path": ["caller-path@0.1.0", "", { "dependencies": { "callsites": "^0.2.0" } }, "sha512-UJiE1otjXPF5/x+T3zTnSFiTOEmJoGTD9HmBoxnCUwho61a2eSNn/VwtwuIBDAo2SEOv1AJ7ARI5gCmohFLu/g=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"camelcase": ["camelcase@5.0.0", "", {}, "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA=="],
"chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
"chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="],
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
"ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="],
"circular-json": ["circular-json@0.3.3", "", {}, "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A=="],
"cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
"cli-width": ["cli-width@3.0.0", "", {}, "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw=="],
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
"co": ["co@4.6.0", "", {}, "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ=="],
"code-point-at": ["code-point-at@1.1.0", "", {}, "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA=="],
"color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
"commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"concat-stream": ["concat-stream@1.6.2", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw=="],
"cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
"core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
"cosmiconfig": ["cosmiconfig@5.2.1", "", { "dependencies": { "import-fresh": "^2.0.0", "is-directory": "^0.3.1", "js-yaml": "^3.13.1", "parse-json": "^4.0.0" } }, "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA=="],
"cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="],
"d": ["d@1.0.2", "", { "dependencies": { "es5-ext": "^0.10.64", "type": "^2.7.2" } }, "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw=="],
"date-format-lite": ["date-format-lite@17.7.0", "", {}, "sha512-bNPOv5pYkhPDNpMTnm8awiVcJy+7cqIkSXcAZsZ9iSCt02fBc8i83aM4pvMoDsi0zBfrDmUJZMgaGoi2p/sz4w=="],
"dateformat": ["dateformat@3.0.3", "", {}, "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="],
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
"detect-libc": ["detect-libc@1.0.3", "", { "bin": "bin/detect-libc.js" }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
"dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="],
"doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="],
"dotenv": ["dotenv@8.6.0", "", {}, "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"elysia": ["elysia@1.2.25", "", { "dependencies": { "@sinclair/typebox": "^0.34.27", "cookie": "^1.0.2", "memoirist": "^0.3.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-WsdQpORJvb4uszzeqYT0lg97knw1iBW1NTzJ1Jm57tiHg+DfAotlWXYbjmvQ039ssV0fYELDHinLLoUazZkEHg=="],
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="],
"engine.io": ["engine.io@6.6.4", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1" } }, "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g=="],
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es5-ext": ["es5-ext@0.10.64", "", { "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", "esniff": "^2.0.1", "next-tick": "^1.1.0" } }, "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg=="],
"es6-iterator": ["es6-iterator@2.0.3", "", { "dependencies": { "d": "1", "es5-ext": "^0.10.35", "es6-symbol": "^3.1.1" } }, "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g=="],
"es6-map": ["es6-map@0.1.5", "", { "dependencies": { "d": "1", "es5-ext": "~0.10.14", "es6-iterator": "~2.0.1", "es6-set": "~0.1.5", "es6-symbol": "~3.1.1", "event-emitter": "~0.3.5" } }, "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A=="],
"es6-set": ["es6-set@0.1.6", "", { "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.62", "es6-iterator": "~2.0.3", "es6-symbol": "^3.1.3", "event-emitter": "^0.3.5", "type": "^2.7.2" } }, "sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw=="],
"es6-symbol": ["es6-symbol@3.1.4", "", { "dependencies": { "d": "^1.0.2", "ext": "^1.7.0" } }, "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg=="],
"es6-weak-map": ["es6-weak-map@2.0.3", "", { "dependencies": { "d": "1", "es5-ext": "^0.10.46", "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA=="],
"escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
"escope": ["escope@3.6.0", "", { "dependencies": { "es6-map": "^0.1.3", "es6-weak-map": "^2.0.1", "esrecurse": "^4.1.0", "estraverse": "^4.1.1" } }, "sha512-75IUQsusDdalQEW/G/2esa87J7raqdJF+Ca0/Xm5C3Q58Nr4yVYjZGp/P1+2xiEVgXRrA39dpRb8LcshajbqDQ=="],
"eslint": ["eslint@6.8.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "ajv": "^6.10.0", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", "debug": "^4.0.1", "doctrine": "^3.0.0", "eslint-scope": "^5.0.0", "eslint-utils": "^1.4.3", "eslint-visitor-keys": "^1.1.0", "espree": "^6.1.2", "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.14", "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.3", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^6.1.2", "strip-ansi": "^5.2.0", "strip-json-comments": "^3.0.1", "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "bin": "bin/eslint.js" }, "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig=="],
"eslint-config": ["eslint-config@0.2.1", "", { "dependencies": { "eslint": "^2.1.0", "object-assign": "^4.0.1" } }, "sha512-xbiJVhqG+4tm39ftVRq++SXz3VqatB29ErHCYv5S7tEGrNd2zlicZc+Zs5ZEjxKXmPqYL3t93B7ps8EjzCUPug=="],
"eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
"eslint-utils": ["eslint-utils@1.4.3", "", { "dependencies": { "eslint-visitor-keys": "^1.1.0" } }, "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q=="],
"eslint-visitor-keys": ["eslint-visitor-keys@1.3.0", "", {}, "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="],
"esniff": ["esniff@2.0.1", "", { "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.62", "event-emitter": "^0.3.5", "type": "^2.7.2" } }, "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg=="],
"espree": ["espree@6.2.1", "", { "dependencies": { "acorn": "^7.1.1", "acorn-jsx": "^5.2.0", "eslint-visitor-keys": "^1.1.0" } }, "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw=="],
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"event-emitter": ["event-emitter@0.3.5", "", { "dependencies": { "d": "1", "es5-ext": "~0.10.14" } }, "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA=="],
"execa": ["execa@1.0.0", "", { "dependencies": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" } }, "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA=="],
"exit-hook": ["exit-hook@1.1.1", "", {}, "sha512-MsG3prOVw1WtLXAZbM3KiYtooKR1LvxHh3VHsVtIy0uiUu8usxgB/94DP2HxtD/661lLdB6yzQ09lGJSQr6nkg=="],
"ext": ["ext@1.7.0", "", { "dependencies": { "type": "^2.7.2" } }, "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw=="],
"external-editor": ["external-editor@3.1.0", "", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
"fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="],
"figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="],
"file-entry-cache": ["file-entry-cache@5.0.1", "", { "dependencies": { "flat-cache": "^2.0.1" } }, "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
"flat-cache": ["flat-cache@2.0.1", "", { "dependencies": { "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" } }, "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA=="],
"flatted": ["flatted@2.0.2", "", {}, "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA=="],
"formidable": ["formidable@3.5.3", "", { "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0" } }, "sha512-pQEHGLZjLRyfLCe6r6n8IQGqHEceKfYR5tIf/iUDn5SabaitfVR/pIskxnyvSSl122J63rFY17i68hrfK0BVOA=="],
"front-matter": ["front-matter@4.0.2", "", { "dependencies": { "js-yaml": "^3.13.1" } }, "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg=="],
"fs-extra": ["fs-extra@3.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^3.0.0", "universalify": "^0.1.0" } }, "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg=="],
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"functional-red-black-tree": ["functional-red-black-tree@1.0.1", "", {}, "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g=="],
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
"generate-object-property": ["generate-object-property@1.2.0", "", { "dependencies": { "is-property": "^1.0.0" } }, "sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"get-stdin": ["get-stdin@7.0.0", "", {}, "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ=="],
"get-stream": ["get-stream@4.1.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="],
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"globals": ["globals@12.4.0", "", { "dependencies": { "type-fest": "^0.8.1" } }, "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg=="],
"globule": ["globule@1.3.4", "", { "dependencies": { "glob": "~7.1.1", "lodash": "^4.17.21", "minimatch": "~3.0.2" } }, "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg=="],
"gonzales-pe-sl": ["gonzales-pe-sl@4.2.3", "", { "dependencies": { "minimist": "1.1.x" }, "bin": { "gonzales": "bin/gonzales.js" } }, "sha512-EdOTnR11W0edkA1xisx4UYtobMSTYj+UNyffW3/b9LziI7RpmHiBIqMs+VL43LrCbiPcLQllCxyzqOB+l5RTdQ=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"has-ansi": ["has-ansi@2.0.0", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg=="],
"has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hono": ["hono@4.7.7", "", {}, "sha512-2PCpQRbN87Crty8/L/7akZN3UyZIAopSoRxCwRbJgUuV1+MHNFHzYFxZTg4v/03cXUm+jce/qa2VSBZpKBm3Qw=="],
"hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="],
"husky": ["husky@3.1.0", "", { "dependencies": { "chalk": "^2.4.2", "ci-info": "^2.0.0", "cosmiconfig": "^5.2.1", "execa": "^1.0.0", "get-stdin": "^7.0.0", "opencollective-postinstall": "^2.0.2", "pkg-dir": "^4.2.0", "please-upgrade-node": "^3.2.0", "read-pkg": "^5.2.0", "run-node": "^1.0.0", "slash": "^3.0.0" }, "bin": { "husky-run": "run.js", "husky-upgrade": "lib/upgrader/bin.js" } }, "sha512-FJkPoHHB+6s4a+jwPqBudBDvYZsoQW5/HBuMSehC8qDiCe50kpcxeqFoDSlow+9I6wg47YxBoT3WxaURlrDIIQ=="],
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"ignore": ["ignore@4.0.6", "", {}, "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="],
"immutable": ["immutable@5.1.1", "", {}, "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"inquirer": ["inquirer@7.3.3", "", { "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.19", "mute-stream": "0.0.8", "run-async": "^2.4.0", "rxjs": "^6.6.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" } }, "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA=="],
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
"is-directory": ["is-directory@0.3.1", "", {}, "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-my-ip-valid": ["is-my-ip-valid@1.0.1", "", {}, "sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg=="],
"is-my-json-valid": ["is-my-json-valid@2.20.6", "", { "dependencies": { "generate-function": "^2.0.0", "generate-object-property": "^1.1.0", "is-my-ip-valid": "^1.0.0", "jsonpointer": "^5.0.0", "xtend": "^4.0.0" } }, "sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
"is-resolvable": ["is-resolvable@1.1.0", "", {}, "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg=="],
"is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"jmespath": ["jmespath@0.15.0", "", {}, "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w=="],
"joycon": ["joycon@2.2.5", "", {}, "sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": "bin/js-yaml.js" }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
"json-parse-better-errors": ["json-parse-better-errors@1.0.2", "", {}, "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="],
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stable-stringify": ["json-stable-stringify@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"jsonfile": ["jsonfile@3.0.1", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w=="],
"jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="],
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
"known-css-properties": ["known-css-properties@0.3.0", "", {}, "sha512-QMQcnKAiQccfQTqtBh/qwquGZ2XK/DXND1jrcN9M8gMMy99Gwla7GQjndVUsEqIaRyP6bsFRuhwRj5poafBGJQ=="],
"leven": ["leven@2.1.0", "", {}, "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA=="],
"levn": ["levn@0.3.0", "", { "dependencies": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" } }, "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA=="],
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
"linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="],
"locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
"lodash.capitalize": ["lodash.capitalize@4.2.1", "", {}, "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw=="],
"lodash.kebabcase": ["lodash.kebabcase@4.1.1", "", {}, "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g=="],
"markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": "bin/markdown-it.mjs" }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="],
"markdown-it-anchor": ["markdown-it-anchor@9.2.0", "", { "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" } }, "sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="],
"memoirist": ["memoirist@0.3.0", "", {}, "sha512-wR+4chMgVPq+T6OOsk40u9Wlpw1Pjx66NMNiYxCQQ4EUJ7jDs3D9kTCeKdBOkvAiqXlHLVJlvYL01PvIJ1MPNg=="],
"merge": ["merge@1.2.1", "", {}, "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": "bin/cmd.js" }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
"mri": ["mri@1.1.4", "", {}, "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mute-stream": ["mute-stream@0.0.8", "", {}, "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
"next-tick": ["next-tick@1.1.0", "", {}, "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="],
"nice-try": ["nice-try@1.0.5", "", {}, "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="],
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
"node-cache": ["node-cache@5.1.2", "", { "dependencies": { "clone": "2.x" } }, "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg=="],
"node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
"normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="],
"npm-run-path": ["npm-run-path@2.0.2", "", { "dependencies": { "path-key": "^2.0.0" } }, "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw=="],
"number-is-nan": ["number-is-nan@1.0.1", "", {}, "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
"opencollective-postinstall": ["opencollective-postinstall@2.0.3", "", { "bin": "index.js" }, "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q=="],
"optionator": ["optionator@0.8.3", "", { "dependencies": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", "word-wrap": "~1.2.3" } }, "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA=="],
"os-homedir": ["os-homedir@1.0.2", "", {}, "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ=="],
"os-tmpdir": ["os-tmpdir@1.0.2", "", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="],
"p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="],
"p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
"path-is-inside": ["path-is-inside@1.0.2", "", {}, "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="],
"path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="],
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"pino-pretty": ["pino-pretty@3.6.1", "", { "dependencies": { "@hapi/bourne": "^1.3.2", "args": "^5.0.1", "chalk": "^2.4.2", "dateformat": "^3.0.3", "fast-safe-stringify": "^2.0.7", "jmespath": "^0.15.0", "joycon": "^2.2.5", "pump": "^3.0.0", "readable-stream": "^3.4.0", "split2": "^3.1.1", "strip-json-comments": "^3.0.1" }, "bin": "bin.js" }, "sha512-S3bal+Yd313OEaPijbf7V+jPxVaTaRO5RQX8S/Mwdtb/8+JOgo1KolDeJTfMDTU2/k6+MHvEbxv+T1ZRfGlnjA=="],
"pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="],
"please-upgrade-node": ["please-upgrade-node@3.2.0", "", { "dependencies": { "semver-compare": "^1.0.0" } }, "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg=="],
"pluralize": ["pluralize@1.2.1", "", {}, "sha512-TH+BeeL6Ct98C7as35JbZLf8lgsRzlNJb5gklRIGHKaPkGl1esOKBc5ALUMd+q08Sr6tiEKM+Icbsxg5vuhMKQ=="],
"prelude-ls": ["prelude-ls@1.1.2", "", {}, "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w=="],
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
"progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
"pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
"read-pkg": ["read-pkg@5.2.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", "parse-json": "^5.0.0", "type-fest": "^0.6.0" } }, "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg=="],
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
"readline2": ["readline2@1.0.1", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "mute-stream": "0.0.5" } }, "sha512-8/td4MmwUB6PkZUbV25uKz7dfrmjYWxsW8DVfibWdlHRk/l/DfHKn4pU+dfcoGLFgWOdyGCzINRQD7jn+Bv+/g=="],
"regexpp": ["regexpp@2.0.1", "", {}, "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw=="],
"require-uncached": ["require-uncached@1.0.3", "", { "dependencies": { "caller-path": "^0.1.0", "resolve-from": "^1.0.0" } }, "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w=="],
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
"rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": "bin.js" }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="],
"run-async": ["run-async@2.4.1", "", {}, "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="],
"run-node": ["run-node@1.0.0", "", { "bin": "run-node" }, "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A=="],
"rx-lite": ["rx-lite@3.1.2", "", {}, "sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ=="],
"rxjs": ["rxjs@6.6.7", "", { "dependencies": { "tslib": "^1.9.0" } }, "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"sass": ["sass@1.87.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": "sass.js" }, "sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw=="],
"sass-lint": ["sass-lint@1.13.1", "", { "dependencies": { "commander": "^2.8.1", "eslint": "^2.7.0", "front-matter": "2.1.2", "fs-extra": "^3.0.1", "glob": "^7.0.0", "globule": "^1.0.0", "gonzales-pe-sl": "^4.2.3", "js-yaml": "^3.5.4", "known-css-properties": "^0.3.0", "lodash.capitalize": "^4.1.0", "lodash.kebabcase": "^4.0.0", "merge": "^1.2.0", "path-is-absolute": "^1.0.0", "util": "^0.10.3" }, "bin": "bin/sass-lint.js" }, "sha512-DSyah8/MyjzW2BWYmQWekYEKir44BpLqrCFsgs9iaWiVTcwZfwXHF586hh3D1n+/9ihUNMfd8iHAyb9KkGgs7Q=="],
"semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
"shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="],
"shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="],
"shelljs": ["shelljs@0.6.1", "", { "bin": { "shjs": "bin/shjs" } }, "sha512-B1vvzXQlJ77SURr3SIUQ/afh+LwecDKAVKE1wqkBlr2PCHoZDaF6MFD+YX1u9ddQjR4z2CKx1tdqvS2Xfs5h1A=="],
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"slice-ansi": ["slice-ansi@2.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ=="],
"socket.io": ["socket.io@4.8.1", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg=="],
"socket.io-adapter": ["socket.io-adapter@2.5.5", "", { "dependencies": { "debug": "~4.3.4", "ws": "~8.17.1" } }, "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg=="],
"socket.io-parser": ["socket.io-parser@4.2.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="],
"spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
"spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="],
"spdx-license-ids": ["spdx-license-ids@3.0.21", "", {}, "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg=="],
"split2": ["split2@3.2.2", "", { "dependencies": { "readable-stream": "^3.0.0" } }, "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="],
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
"strip-eof": ["strip-eof@1.0.0", "", {}, "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"table": ["table@5.4.6", "", { "dependencies": { "ajv": "^6.10.2", "lodash": "^4.17.14", "slice-ansi": "^2.1.0", "string-width": "^3.0.0" } }, "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug=="],
"text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="],
"through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
"tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="],
"type-check": ["type-check@0.3.2", "", { "dependencies": { "prelude-ls": "~1.1.2" } }, "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg=="],
"type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="],
"typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="],
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"user-home": ["user-home@2.0.0", "", { "dependencies": { "os-homedir": "^1.0.0" } }, "sha512-KMWqdlOcjCYdtIJpicDSFBQ8nFwS2i9sslAd6f4+CBGcU4gist2REnr2fxj2YocvJFxSF3ZOHLYLVZnUxv4BZQ=="],
"utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="],
"util": ["util@0.10.4", "", { "dependencies": { "inherits": "2.0.3" } }, "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"v8-compile-cache": ["v8-compile-cache@2.4.0", "", {}, "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw=="],
"validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
"which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": "bin/which" }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"write": ["write@1.0.3", "", { "dependencies": { "mkdirp": "^0.5.1" } }, "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig=="],
"ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" } }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
"ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
"caller-callsite/callsites": ["callsites@2.0.0", "", {}, "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="],
"caller-path/callsites": ["callsites@0.2.0", "", {}, "sha512-Zv4Dns9IbXXmPkgRRUjAaJQgfN4xX5p6+RQFhWUqscdvvK2xK/ZL8b3IXIJsj+4sD+f24NwnWy2BY8AJ82JB0A=="],
"concat-stream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"cosmiconfig/import-fresh": ["import-fresh@2.0.0", "", { "dependencies": { "caller-path": "^2.0.0", "resolve-from": "^3.0.0" } }, "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg=="],
"cross-spawn/semver": ["semver@5.7.2", "", { "bin": "bin/semver" }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
"engine.io/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"engine.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
"engine.io/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" } }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
"eslint-config/eslint": ["eslint@2.13.1", "", { "dependencies": { "chalk": "^1.1.3", "concat-stream": "^1.4.6", "debug": "^2.1.1", "doctrine": "^1.2.2", "es6-map": "^0.1.3", "escope": "^3.6.0", "espree": "^3.1.6", "estraverse": "^4.2.0", "esutils": "^2.0.2", "file-entry-cache": "^1.1.1", "glob": "^7.0.3", "globals": "^9.2.0", "ignore": "^3.1.2", "imurmurhash": "^0.1.4", "inquirer": "^0.12.0", "is-my-json-valid": "^2.10.0", "is-resolvable": "^1.0.0", "js-yaml": "^3.5.1", "json-stable-stringify": "^1.0.0", "levn": "^0.3.0", "lodash": "^4.0.0", "mkdirp": "^0.5.0", "optionator": "^0.8.1", "path-is-absolute": "^1.0.0", "path-is-inside": "^1.0.1", "pluralize": "^1.2.1", "progress": "^1.1.8", "require-uncached": "^1.0.2", "shelljs": "^0.6.0", "strip-json-comments": "~1.0.1", "table": "^3.7.8", "text-table": "~0.2.0", "user-home": "^2.0.0" }, "bin": "bin/eslint.js" }, "sha512-29PFGeV6lLQrPaPHeCkjfgLRQPFflDiicoNZOw+c/JkaQ0Am55yUICdYZbmCiM+DSef+q7oCercimHvjNI0GAw=="],
"esquery/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"globule/glob": ["glob@7.1.7", "", { "dependencies": { "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" } }, "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ=="],
"globule/minimatch": ["minimatch@3.0.8", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q=="],
"gonzales-pe-sl/minimist": ["minimist@1.1.3", "", {}, "sha512-2RbeLaM/Hbo9vJ1+iRrxzfDnX9108qb2m923U+s+Ot2eMey0IYGdSjzHmvtg2XsxoCuMnzOMw7qc573RvnLgwg=="],
"has-ansi/ansi-regex": ["ansi-regex@2.1.1", "", {}, "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="],
"inquirer/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"inquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
"json-stable-stringify/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"normalize-package-data/semver": ["semver@5.7.2", "", { "bin": "bin/semver" }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
"read-pkg/parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
"read-pkg/type-fest": ["type-fest@0.6.0", "", {}, "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="],
"readline2/is-fullwidth-code-point": ["is-fullwidth-code-point@1.0.0", "", { "dependencies": { "number-is-nan": "^1.0.0" } }, "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw=="],
"readline2/mute-stream": ["mute-stream@0.0.5", "", {}, "sha512-EbrziT4s8cWPmzr47eYVW3wimS4HsvlnV5ri1xw1aR6JQo/OrJX5rkl32K/QQHdxeabJETtfeaROGhd8W7uBgg=="],
"require-uncached/resolve-from": ["resolve-from@1.0.1", "", {}, "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg=="],
"sass-lint/eslint": ["eslint@2.13.1", "", { "dependencies": { "chalk": "^1.1.3", "concat-stream": "^1.4.6", "debug": "^2.1.1", "doctrine": "^1.2.2", "es6-map": "^0.1.3", "escope": "^3.6.0", "espree": "^3.1.6", "estraverse": "^4.2.0", "esutils": "^2.0.2", "file-entry-cache": "^1.1.1", "glob": "^7.0.3", "globals": "^9.2.0", "ignore": "^3.1.2", "imurmurhash": "^0.1.4", "inquirer": "^0.12.0", "is-my-json-valid": "^2.10.0", "is-resolvable": "^1.0.0", "js-yaml": "^3.5.1", "json-stable-stringify": "^1.0.0", "levn": "^0.3.0", "lodash": "^4.0.0", "mkdirp": "^0.5.0", "optionator": "^0.8.1", "path-is-absolute": "^1.0.0", "path-is-inside": "^1.0.1", "pluralize": "^1.2.1", "progress": "^1.1.8", "require-uncached": "^1.0.2", "shelljs": "^0.6.0", "strip-json-comments": "~1.0.1", "table": "^3.7.8", "text-table": "~0.2.0", "user-home": "^2.0.0" }, "bin": "bin/eslint.js" }, "sha512-29PFGeV6lLQrPaPHeCkjfgLRQPFflDiicoNZOw+c/JkaQ0Am55yUICdYZbmCiM+DSef+q7oCercimHvjNI0GAw=="],
"sass-lint/front-matter": ["front-matter@2.1.2", "", { "dependencies": { "js-yaml": "^3.4.6" } }, "sha512-wH9JJVUi/MUfRpSvYWltdC9FGFZdkcc2H7US7Sp3iYihXTpYWWEL7ZUHMBicA9MsFBR/EatSbYN5EtCaytfiNA=="],
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
"socket.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
"socket.io-adapter/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
"socket.io-adapter/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" } }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
"socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
"string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"table/string-width": ["string-width@3.1.0", "", { "dependencies": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } }, "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w=="],
"util/inherits": ["inherits@2.0.3", "", {}, "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="],
"concat-stream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"concat-stream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
"cosmiconfig/import-fresh/caller-path": ["caller-path@2.0.0", "", { "dependencies": { "caller-callsite": "^2.0.0" } }, "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A=="],
"cosmiconfig/import-fresh/resolve-from": ["resolve-from@3.0.0", "", {}, "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="],
"eslint-config/eslint/chalk": ["chalk@1.1.3", "", { "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } }, "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A=="],
"eslint-config/eslint/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"eslint-config/eslint/doctrine": ["doctrine@1.5.0", "", { "dependencies": { "esutils": "^2.0.2", "isarray": "^1.0.0" } }, "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg=="],
"eslint-config/eslint/espree": ["espree@3.5.4", "", { "dependencies": { "acorn": "^5.5.0", "acorn-jsx": "^3.0.0" } }, "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A=="],
"eslint-config/eslint/file-entry-cache": ["file-entry-cache@1.3.1", "", { "dependencies": { "flat-cache": "^1.2.1", "object-assign": "^4.0.1" } }, "sha512-JyVk7P0Hvw6uEAwH4Y0j+rZMvaMWvLBYRmRGAF2S6jKTycf0mMDcC7d21Y2KyrKJk3XI8YghSsk5KmRdbvg0VQ=="],
"eslint-config/eslint/globals": ["globals@9.18.0", "", {}, "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="],
"eslint-config/eslint/ignore": ["ignore@3.3.10", "", {}, "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug=="],
"eslint-config/eslint/inquirer": ["inquirer@0.12.0", "", { "dependencies": { "ansi-escapes": "^1.1.0", "ansi-regex": "^2.0.0", "chalk": "^1.0.0", "cli-cursor": "^1.0.1", "cli-width": "^2.0.0", "figures": "^1.3.5", "lodash": "^4.3.0", "readline2": "^1.0.1", "run-async": "^0.1.0", "rx-lite": "^3.1.2", "string-width": "^1.0.1", "strip-ansi": "^3.0.0", "through": "^2.3.6" } }, "sha512-bOetEz5+/WpgaW4D1NYOk1aD+JCqRjqu/FwRFgnIfiP7FC/zinsrfyO1vlS3nyH/R7S0IH3BIHBu4DBIDSqiGQ=="],
"eslint-config/eslint/progress": ["progress@1.1.8", "", {}, "sha512-UdA8mJ4weIkUBO224tIarHzuHs4HuYiJvsuGT7j/SPQiUJVjYvNDBIPa0hAorduOfjGohB/qHWRa/lrrWX/mXw=="],
"eslint-config/eslint/strip-json-comments": ["strip-json-comments@1.0.4", "", { "bin": "cli.js" }, "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg=="],
"eslint-config/eslint/table": ["table@3.8.3", "", { "dependencies": { "ajv": "^4.7.0", "ajv-keywords": "^1.0.0", "chalk": "^1.1.1", "lodash": "^4.0.0", "slice-ansi": "0.0.4", "string-width": "^2.0.0" } }, "sha512-RZuzIOtzFbprLCE0AXhkI0Xi42ZJLZhCC+qkwuMLf/Vjz3maWpA8gz1qMdbmNoI9cOROT2Am/DxeRyXenrL11g=="],
"inquirer/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"inquirer/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"inquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"sass-lint/eslint/chalk": ["chalk@1.1.3", "", { "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } }, "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A=="],
"sass-lint/eslint/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"sass-lint/eslint/doctrine": ["doctrine@1.5.0", "", { "dependencies": { "esutils": "^2.0.2", "isarray": "^1.0.0" } }, "sha512-lsGyRuYr4/PIB0txi+Fy2xOMI2dGaTguCaotzFGkVZuKR5usKfcRWIFKNM3QNrU7hh/+w2bwTW+ZeXPK5l8uVg=="],
"sass-lint/eslint/espree": ["espree@3.5.4", "", { "dependencies": { "acorn": "^5.5.0", "acorn-jsx": "^3.0.0" } }, "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A=="],
"sass-lint/eslint/file-entry-cache": ["file-entry-cache@1.3.1", "", { "dependencies": { "flat-cache": "^1.2.1", "object-assign": "^4.0.1" } }, "sha512-JyVk7P0Hvw6uEAwH4Y0j+rZMvaMWvLBYRmRGAF2S6jKTycf0mMDcC7d21Y2KyrKJk3XI8YghSsk5KmRdbvg0VQ=="],
"sass-lint/eslint/globals": ["globals@9.18.0", "", {}, "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="],
"sass-lint/eslint/ignore": ["ignore@3.3.10", "", {}, "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug=="],
"sass-lint/eslint/inquirer": ["inquirer@0.12.0", "", { "dependencies": { "ansi-escapes": "^1.1.0", "ansi-regex": "^2.0.0", "chalk": "^1.0.0", "cli-cursor": "^1.0.1", "cli-width": "^2.0.0", "figures": "^1.3.5", "lodash": "^4.3.0", "readline2": "^1.0.1", "run-async": "^0.1.0", "rx-lite": "^3.1.2", "string-width": "^1.0.1", "strip-ansi": "^3.0.0", "through": "^2.3.6" } }, "sha512-bOetEz5+/WpgaW4D1NYOk1aD+JCqRjqu/FwRFgnIfiP7FC/zinsrfyO1vlS3nyH/R7S0IH3BIHBu4DBIDSqiGQ=="],
"sass-lint/eslint/progress": ["progress@1.1.8", "", {}, "sha512-UdA8mJ4weIkUBO224tIarHzuHs4HuYiJvsuGT7j/SPQiUJVjYvNDBIPa0hAorduOfjGohB/qHWRa/lrrWX/mXw=="],
"sass-lint/eslint/strip-json-comments": ["strip-json-comments@1.0.4", "", { "bin": "cli.js" }, "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg=="],
"sass-lint/eslint/table": ["table@3.8.3", "", { "dependencies": { "ajv": "^4.7.0", "ajv-keywords": "^1.0.0", "chalk": "^1.1.1", "lodash": "^4.0.0", "slice-ansi": "0.0.4", "string-width": "^2.0.0" } }, "sha512-RZuzIOtzFbprLCE0AXhkI0Xi42ZJLZhCC+qkwuMLf/Vjz3maWpA8gz1qMdbmNoI9cOROT2Am/DxeRyXenrL11g=="],
"string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"table/string-width/emoji-regex": ["emoji-regex@7.0.3", "", {}, "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="],
"table/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
"eslint-config/eslint/chalk/ansi-styles": ["ansi-styles@2.2.1", "", {}, "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="],
"eslint-config/eslint/chalk/strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="],
"eslint-config/eslint/chalk/supports-color": ["supports-color@2.0.0", "", {}, "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="],
"eslint-config/eslint/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"eslint-config/eslint/espree/acorn": ["acorn@5.7.4", "", { "bin": "bin/acorn" }, "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="],
"eslint-config/eslint/espree/acorn-jsx": ["acorn-jsx@3.0.1", "", { "dependencies": { "acorn": "^3.0.4" } }, "sha512-AU7pnZkguthwBjKgCg6998ByQNIMjbuDQZ8bb78QAFZwPfmKia8AIzgY/gWgqCjnht8JLdXmB4YxA0KaV60ncQ=="],
"eslint-config/eslint/file-entry-cache/flat-cache": ["flat-cache@1.3.4", "", { "dependencies": { "circular-json": "^0.3.1", "graceful-fs": "^4.1.2", "rimraf": "~2.6.2", "write": "^0.2.1" } }, "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg=="],
"eslint-config/eslint/inquirer/ansi-escapes": ["ansi-escapes@1.4.0", "", {}, "sha512-wiXutNjDUlNEDWHcYH3jtZUhd3c4/VojassD8zHdHCY13xbZy2XbW+NKQwA0tWGBVzDA9qEzYwfoSsWmviidhw=="],
"eslint-config/eslint/inquirer/ansi-regex": ["ansi-regex@2.1.1", "", {}, "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="],
"eslint-config/eslint/inquirer/cli-cursor": ["cli-cursor@1.0.2", "", { "dependencies": { "restore-cursor": "^1.0.1" } }, "sha512-25tABq090YNKkF6JH7lcwO0zFJTRke4Jcq9iX2nr/Sz0Cjjv4gckmwlW6Ty/aoyFd6z3ysR2hMGC2GFugmBo6A=="],
"eslint-config/eslint/inquirer/cli-width": ["cli-width@2.2.1", "", {}, "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="],
"eslint-config/eslint/inquirer/figures": ["figures@1.7.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5", "object-assign": "^4.1.0" } }, "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ=="],
"eslint-config/eslint/inquirer/run-async": ["run-async@0.1.0", "", { "dependencies": { "once": "^1.3.0" } }, "sha512-qOX+w+IxFgpUpJfkv2oGN0+ExPs68F4sZHfaRRx4dDexAQkG83atugKVEylyT5ARees3HBbfmuvnjbrd8j9Wjw=="],
"eslint-config/eslint/inquirer/string-width": ["string-width@1.0.2", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } }, "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw=="],
"eslint-config/eslint/inquirer/strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="],
"eslint-config/eslint/table/ajv": ["ajv@4.11.8", "", { "dependencies": { "co": "^4.6.0", "json-stable-stringify": "^1.0.1" } }, "sha512-I/bSHSNEcFFqXLf91nchoNB9D1Kie3QKcWdchYUaoIg1+1bdWDkdfdlvdIOJbi9U8xR0y+MWc5D+won9v95WlQ=="],
"eslint-config/eslint/table/slice-ansi": ["slice-ansi@0.0.4", "", {}, "sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw=="],
"eslint-config/eslint/table/string-width": ["string-width@2.1.1", "", { "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw=="],
"inquirer/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"inquirer/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"sass-lint/eslint/chalk/ansi-styles": ["ansi-styles@2.2.1", "", {}, "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="],
"sass-lint/eslint/chalk/strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="],
"sass-lint/eslint/chalk/supports-color": ["supports-color@2.0.0", "", {}, "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="],
"sass-lint/eslint/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"sass-lint/eslint/espree/acorn": ["acorn@5.7.4", "", { "bin": "bin/acorn" }, "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="],
"sass-lint/eslint/espree/acorn-jsx": ["acorn-jsx@3.0.1", "", { "dependencies": { "acorn": "^3.0.4" } }, "sha512-AU7pnZkguthwBjKgCg6998ByQNIMjbuDQZ8bb78QAFZwPfmKia8AIzgY/gWgqCjnht8JLdXmB4YxA0KaV60ncQ=="],
"sass-lint/eslint/file-entry-cache/flat-cache": ["flat-cache@1.3.4", "", { "dependencies": { "circular-json": "^0.3.1", "graceful-fs": "^4.1.2", "rimraf": "~2.6.2", "write": "^0.2.1" } }, "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg=="],
"sass-lint/eslint/inquirer/ansi-escapes": ["ansi-escapes@1.4.0", "", {}, "sha512-wiXutNjDUlNEDWHcYH3jtZUhd3c4/VojassD8zHdHCY13xbZy2XbW+NKQwA0tWGBVzDA9qEzYwfoSsWmviidhw=="],
"sass-lint/eslint/inquirer/ansi-regex": ["ansi-regex@2.1.1", "", {}, "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="],
"sass-lint/eslint/inquirer/cli-cursor": ["cli-cursor@1.0.2", "", { "dependencies": { "restore-cursor": "^1.0.1" } }, "sha512-25tABq090YNKkF6JH7lcwO0zFJTRke4Jcq9iX2nr/Sz0Cjjv4gckmwlW6Ty/aoyFd6z3ysR2hMGC2GFugmBo6A=="],
"sass-lint/eslint/inquirer/cli-width": ["cli-width@2.2.1", "", {}, "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="],
"sass-lint/eslint/inquirer/figures": ["figures@1.7.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5", "object-assign": "^4.1.0" } }, "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ=="],
"sass-lint/eslint/inquirer/run-async": ["run-async@0.1.0", "", { "dependencies": { "once": "^1.3.0" } }, "sha512-qOX+w+IxFgpUpJfkv2oGN0+ExPs68F4sZHfaRRx4dDexAQkG83atugKVEylyT5ARees3HBbfmuvnjbrd8j9Wjw=="],
"sass-lint/eslint/inquirer/string-width": ["string-width@1.0.2", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } }, "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw=="],
"sass-lint/eslint/inquirer/strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="],
"sass-lint/eslint/table/ajv": ["ajv@4.11.8", "", { "dependencies": { "co": "^4.6.0", "json-stable-stringify": "^1.0.1" } }, "sha512-I/bSHSNEcFFqXLf91nchoNB9D1Kie3QKcWdchYUaoIg1+1bdWDkdfdlvdIOJbi9U8xR0y+MWc5D+won9v95WlQ=="],
"sass-lint/eslint/table/slice-ansi": ["slice-ansi@0.0.4", "", {}, "sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw=="],
"sass-lint/eslint/table/string-width": ["string-width@2.1.1", "", { "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw=="],
"eslint-config/eslint/chalk/strip-ansi/ansi-regex": ["ansi-regex@2.1.1", "", {}, "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="],
"eslint-config/eslint/espree/acorn-jsx/acorn": ["acorn@3.3.0", "", { "bin": "bin/acorn" }, "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw=="],
"eslint-config/eslint/file-entry-cache/flat-cache/write": ["write@0.2.1", "", { "dependencies": { "mkdirp": "^0.5.1" } }, "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA=="],
"eslint-config/eslint/inquirer/cli-cursor/restore-cursor": ["restore-cursor@1.0.1", "", { "dependencies": { "exit-hook": "^1.0.0", "onetime": "^1.0.0" } }, "sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw=="],
"eslint-config/eslint/inquirer/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@1.0.0", "", { "dependencies": { "number-is-nan": "^1.0.0" } }, "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw=="],
"eslint-config/eslint/table/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
"eslint-config/eslint/table/string-width/strip-ansi": ["strip-ansi@4.0.0", "", { "dependencies": { "ansi-regex": "^3.0.0" } }, "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow=="],
"inquirer/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"sass-lint/eslint/chalk/strip-ansi/ansi-regex": ["ansi-regex@2.1.1", "", {}, "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="],
"sass-lint/eslint/espree/acorn-jsx/acorn": ["acorn@3.3.0", "", { "bin": "bin/acorn" }, "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw=="],
"sass-lint/eslint/file-entry-cache/flat-cache/write": ["write@0.2.1", "", { "dependencies": { "mkdirp": "^0.5.1" } }, "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA=="],
"sass-lint/eslint/inquirer/cli-cursor/restore-cursor": ["restore-cursor@1.0.1", "", { "dependencies": { "exit-hook": "^1.0.0", "onetime": "^1.0.0" } }, "sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw=="],
"sass-lint/eslint/inquirer/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@1.0.0", "", { "dependencies": { "number-is-nan": "^1.0.0" } }, "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw=="],
"sass-lint/eslint/table/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
"sass-lint/eslint/table/string-width/strip-ansi": ["strip-ansi@4.0.0", "", { "dependencies": { "ansi-regex": "^3.0.0" } }, "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow=="],
"eslint-config/eslint/inquirer/cli-cursor/restore-cursor/onetime": ["onetime@1.1.0", "", {}, "sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A=="],
"eslint-config/eslint/table/string-width/strip-ansi/ansi-regex": ["ansi-regex@3.0.1", "", {}, "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="],
"sass-lint/eslint/inquirer/cli-cursor/restore-cursor/onetime": ["onetime@1.1.0", "", {}, "sha512-GZ+g4jayMqzCRMgB2sol7GiCLjKfS1PINkjmx8spcKce1LiVqcbQreXwqs2YAFXC6R03VIG28ZS31t8M866v6A=="],
"sass-lint/eslint/table/string-width/strip-ansi/ansi-regex": ["ansi-regex@3.0.1", "", {}, "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="],
}
}

View file

@ -4,12 +4,12 @@
// E X P O R T
module.exports = exports = {
export default {
ga: "UA-60403362-8",
github: {
branch: "master",
linkText: "Edit this page on GitHub",
repo: "lbryio/lbry.tech"
repo: "LBRYFoundation/tech.lbry.org"
},
meta: {
color: "#222",

9
docker-compose.yml Normal file
View file

@ -0,0 +1,9 @@
services:
lbry-tech:
build:
context: ./
dockerfile: Dockerfile
ports:
- ${PORT:-8080}:8080
environment:
- GITHUB_TOKEN=${GITHUB_TOKEN}

View file

@ -30,9 +30,9 @@ If you want to contribute to LBRY, there's definitely something for you! The fir
--- | --- | --- | ---
| [lbry-desktop](https://github.com/lbryio/lbry-desktop) (and lbry.tv) | JavaScript (ReactJS, Electron) | A desktop browser for the LBRY network for Windows, macOS, and Linux as well as a web interface on lbry.tv. [[lbry-desktop]] is built with [[lbry-sdk]]. | [Video](/resources/video-lbrydesktop)
| [lbry-android](https://github.com/lbryio/lbry-android) | Java | A graphical browser for the LBRY network for Android. [[lbry-android]] uses [[lbry-sdk]] to interact with the network. | [Video](/resources/video-lbryandroid)
| [odysee-api](https://github.com/lbryio/odysee-api) | Go | An API server for https://odysee.com that reimplements some of the SDK APIs. | |
| [odysee-ios](https://github.com/lbryio/odysee-ios) | Swift | The Odysee IOS app. | |
| [lbry-redux](https://github.com/lbryio/lbry-redux) | JavaScript (Redux) | A common codebase for shared Redux logic between [[lbry-desktop]] and [[lbry-android]]. | |
| [lbrytv](https://github.com/lbryio/lbrytv) | Go | An API server for https://lbry.tv and https://odysee.com that reimplements some of the SDK APIs. | |
### Websites
| Domain | Language (Toolset) | What Is It
@ -44,7 +44,7 @@ If you want to contribute to LBRY, there's definitely something for you! The fir
| Domain | Language (Toolset) | What Is It
--- | --- | ---
| [chainquery](https://github.com/lbryio/chainquery) | Go | A utility for parsing, extracting, and updating the LBRY blockchain into structured SQL data. Used by several internal tools and useful for 3rd-party application development.
| [lighthouse](https://github.com/lbryio/lighthouse) | JavaScript, ElasticSearch | A search service for the LBRY blockchain.
| [lighthouse](https://github.com/lbryio/lighthouse) | Go, ElasticSearch | A search service for the LBRY blockchain.
| [wunderbot](https://github.com/lbryio/lbry-wunderbot) | JavaScript (Node) | A chatbot used by the LBRY discord.
| [block-explorer](https://github.com/lbryio/block-explorer) | PHP (vanilla) | A blockchain explorer for the LBRY blockchain.

28
documents/protocols.md Normal file
View file

@ -0,0 +1,28 @@
---
title: Protocols
description: Understand how LBRY works low level and build your own implementations.
---
- [Blockchain P2P Protocol](/protocols/blockchain-p2p)
- [Blockchain RPC Protocol](/protocols/blockchain-rpc)
- [Content Protocol](/protocols/content)
- [DHT Protocol](/protocols/dht)
- [Hub Protocol](/protocols/hub)
- [Hub Ping Protocol](/protocols/hub-ping)
- [Media Streaming Protocol](/protocols/media-streaming)
- [Media RPC Protocol](/protocols/media-rpc)
- [Reflector Protocol](/protocols/reflector)
![Protocol Chart](/assets/LBRYNetworkProtocolChart.png)

View file

@ -0,0 +1,10 @@
---
title: Blockchain P2P Protocol
description: The protocol used between blockchain nodes.
---
This protocol is used between blockchain nodes to communicate between each other.
- **Port:** 9246/TCP
See: [https://developer.bitcoin.org/reference/p2p_networking.html](Bitcoin.org P2P Network)

View file

@ -0,0 +1,10 @@
---
title: Blockchain RPC Protocol
description: The protocol used to query the blockchain.
---
This protocol is used to query information from the blockchain. For example, the LBRY hub uses this protocol to check for new blocks and to advance it into its own database.
- **Port:** 9245/TCP
See: [https://developer.bitcoin.org/reference/rpc/index.html](Bitcoin.org RPC API Reference) and [https://lbry.tech/api/blockchain](LBRY Blockchain API)

View file

@ -0,0 +1,60 @@
---
title: Content Protocol
description: The protocol used to download blobs.
---
This protocol provides a way to pull a blob from a specific node. The protocol uses JSON blocks. The end of a JSON block can be detected if the JSON is valid after receiving a `}`, thereby closing the root object. Another JSON block directly follows it, without any whitespace. This protocol supports composite requests, so multiple requests can be merged into one, as also for the response. For example, one request can both contain `requested_blobs` and `blob_data_payment_rate`, and the server will answer with one response containing both `available_blobs` and `blob_data_payment_rate`.
- **Port:** 3333/TCP (distributed peer, previously)
- **Port:** 4444/TCP (distributed peer)
- **Port:** 5567/TCP (fixed peer)
## Availability
This request will check what blobs are available on the peer. It takes the sent list of blob hashes from `requested_blobs`, checks everyone of them, and returns a list of available blobs in `available_blobs`. Also, the request contains `lbrycrd_address`, which is a boolean property. In the response the `lbrycrd_address` property is a string containing an address. However, the use of `lbrycrd_address` for paying for blob data is not implemented yet.
```json5
//REQUEST
{"lbrycrd_address":false,"requested_blobs":["aabbcc","ddeeff"]}
//or
{"lbrycrd_address":true,"requested_blobs":["aabbcc","ddeeff"]}
//RESPONSE
{"lbrycrd_address":"bJxKvpD96kaJLriqVajZ7SaQTsWWyrGQct","requested_blobs":["aabbcc","ddeeff"]}
//or
{"lbrycrd_address":"bJxKvpD96kaJLriqVajZ7SaQTsWWyrGQct","requested_blobs":["aabbcc"]}
//or
{"lbrycrd_address":"bJxKvpD96kaJLriqVajZ7SaQTsWWyrGQct","requested_blobs":["ddeeff"]}
//or
{"lbrycrd_address":"bJxKvpD96kaJLriqVajZ7SaQTsWWyrGQct","requested_blobs":[]}
```
## Payment Rate
The reason behind this request is unknown at this moment. The request contains the `blob_data_payment_rate` property with a float. The result is always `RATE_ACCEPTED`, unless the float is below zero. Then the server will respond with `RATE_TOO_LOW`. At this moment, `RATE_UNSET` is not used.
```json5
//REQUEST
{"blob_data_payment_rate":123.456}
//RESPONSE
{"blob_data_payment_rate":"RATE_ACCEPTED"}
//or
{"blob_data_payment_rate":"RATE_TOO_LOW"}
//or
{"blob_data_payment_rate":"RATE_UNSET"}
```
## Blob
This request will get the blob itself. The hash will be sent using `requested_blob`. The server then reacts with an `incoming_blob`. If everything is correct, the `incoming_blob` contains a `blob_hash` property and a `length` property. The blob data directly follows after the JSON block response. If an error occurs, the `incoming_blob` contains an `error` property (and `blob_hash` is empty and `length` is zero).
```json5
//REQUEST
{"requested_blob":"aabbcc"}
//RESPONSE
{"incoming_blob":{"blob_hash":"aabbcc","length":123}}/*[Raw Blob Data]*/
//or
{"incoming_blob":{"blob_hash":"","length":0,"error":"Blob not found"}}
```

View file

@ -0,0 +1,85 @@
---
title: DHT Protocol
description: The protocol used to find other nodes.
---
The DHT protocol is used to discover and connect with other nodes. It follows the Kademlia specification, with some slight modiciations. In the Kademlia spec there are 4 message types: `PING`, `FIND_NODE`, `FIND_VALUE` and `STORE`. The protocol uses Bencoding, but is different from the BitTorrent specification (BEP 5).
- **Port:** 4444/UDP
The root dictionary can hold 5 properties: `0`, `1`, `2`, `3` and `4`. Note: Bencoding requires those keys to be strings, but some old software could send those as integers. This is wrong. The request must have all those properties, the response only requires `0`, `1`, `2` and `3`. The error, like the request, also requires all properties.
- The `0` property is the message type, where the value is an integer with `0` for request, `1` for response and `2` for error.
- The `1` property is the message ID, which contains a string of 20 bytes and is unique for every request-response or request-error pair.
- The `2` property is the node ID, which contains a string of 48 bytes and is unique for every node.
- The `3` property is the method (string; one of the 4) in case of a request message type, the response (mixed) in case of a response message type, and the error type (string) in case of a error message type.
- The `4` property is the arguments (list) in case of a request message type, absent in case of a response message type, and the response (string) in case of a error message type.
When a node wants to send an error back (it doesn't need to in every case), then the error message looks like the response message. However, the `3` property now contains the error type and optionally, `4` contains more information.
## Ping
The request has a property `3` with value `ping`.
In protocol version `0`, there are no request arguments.
In protocol version `1`, there is 1 request argument:
- A dictionary containing `protocolVersion` with integer value `1`.
In both versions, the response has a property `3` with value `pong`.
## Find Node
The request has a property `3` with value `findNode`.
In protocol version `0`, there is 1 request argument:
- A 48-byte string containing a key.
In protocol version `1`, there are 2 request arguments:
- A 48-byte string containing a key.
- A dictionary containing `protocolVersion` with integer value `1`.
In both versions, the response has a property `3` with a list as value. Every item in the list is a tuple (another list), containing the node ID as 48-byte string, the IP as string and the port as integer.
## Find Value
The request has a property `3` with value `findValue`.
In protocol version `0`, there is 1 request argument:
- A 48-byte string containing a key.
In protocol version `1`, there are 2 request arguments:
- A 48-byte string containing a key.
- A dictionary containing `p` (optionally) with an integer value to indicate the page, and `protocolVersion` with integer value `1`.
In both versions, the response has a property `3` with a dictionary as value:
- The directory at least contains a property `token`, which is needed when storing values on the connected node. If the node supports protocol version `1`, it should have `protocolVersion` set to integer `1`. If it only supports version `0`, `protocolVersion` can be integer `0` or absent.
- Based on if the node has the key, it will return the `contacts` property or the property where the name is the same as the key argument in the request. If the key is not known by the node, the `contacts` property is present. Like the `findNode` function response, the `contacts` property will contain a list of tuples, containing the node ID, IP address and DHT port number.
- If the key is known by the node, `contacts` isn't required, but allowed to be present. The response now at least has a property with the same 48 byte long name as the key in the request. This property is a list, where every item in the list is a compact address. This compact address contains information on where the blob can be downloaded.
- The optional `p` property is an integer with the amount of pages of download locations. If there are no download locations, the `p` value is integer `0`. If the `p` property is set in the request, the `contacts` property is absent.
### Compact Address
The compact address is a value of 54 bytes. The first 4 bytes are the binary format of the IPv4 address. The next 2 bytes are the TCP port where the blob can be downloaded. The remanining 48 bytes are the node id of the associated DHT peer.
## Store
The request has a property `3` with value `store`.
In protocol version `0`, there are 4 request arguments:
- A 48-byte string containing a key (e.g. blob hash).
- A value.
- An original publisher ID.
- An age.
The value is a dictory with the properties `token`, `lbryid` and `port`. The `token` property holds the token value of the connected node, where the token value is received from an earlier `findValue` request. The `lbryid` property holds the node ID of the sending node. The `port` property holds the port number of where the blob can be downloaded.
In protocol version `1`, there are 6 request arguments:
- A blob hash.
- A token.
- A port.
- An original publisher ID.
- An age.
- A dictionary containing `protocolVersion` with integer value `1`.
In both versions, the response has a property `3` with value `OK`.

View file

@ -0,0 +1,8 @@
---
title: Hub Ping Protocol
description: The protocol used to ping a hub server to check if it is running.
---
This protocol is used to ping a hub server to check if it is running. If it is, the hub server also returns some additional information that can be used to determine if the hub server is a good fit for that client.
- **Port:** 50001/UDP

View file

@ -0,0 +1,17 @@
---
title: Hub Protocol
description: The protocol used to search and resolve claims, manage wallets, and quickly find blockchain information.
---
This protocol is used to search and resolve claims, manage wallets, and quickly find blockchain information. It is based on Bitcoin's BIP40, with some LBRY-specific additions, like Claimtrie methods.
- **Port:** 50001/TCP
- **Port:** 50002/TCP (SSL/TLS)
## Method: `blockchain.claimtrie.resolve`
This method resolves specific LBRY claims and returns more information.
## Method: `blockchain.claimtrie.search`
This method search for LBRY claims by a set of keywords and some filters.

View file

@ -0,0 +1,10 @@
---
title: Media RPC Protocol
description: The protocol used to query the media server.
---
This protocol is used to query information from the media server.
- **Port:** 5279/TCP
See: [https://lbry.tech/api/sdk](LBRY SDK API)

View file

@ -0,0 +1,8 @@
---
title: Media Streaming Protocol
description: The protocol used to stream content from the media server.
---
This protocol is used to stream content from the media server.
- **Port:** 5280/TCP

View file

@ -0,0 +1,72 @@
---
title: Reflector Protocol
description: The protocol used to upload blobs.
---
This protocol provides a way to actively push blobs to the network, instead of waiting for them to get pulled. The protocol uses JSON blocks. The end of a JSON block can be detected if the JSON is valid after receiving a `}`, thereby closing the root object. Another JSON block directly follows it, without any whitespace. At this moment, there will be no error block sent when an error occurs. Version `0` of the protocol supports blobs, version `1` of the protocol also supports SD blobs. Unknown properties are ignored and will not cause an error.
- **Port:** 5566/TCP
## Handshake
Before doing anything with blobs, the handshake should be sent. The `version` property is required and should be an integer valued `0` or `1`. If the server supports that version, it will send back an JSON block where the `version` property has the same value. If the `value` property contains an invalid protocol number or a protocol number that isn't supported, the server throws an error and closes the connection.
```json5
//REQUEST
{"version":0}
//or
{"version":1}
//RESPONSE
{"version":0}
//or
{"version":1}
```
## Blob Hash and Blob Size
After the handshake, blobs can be received. First, the server wants to know more about the blob itself, before it wants to receive the blob data itself. The clients needs to send a JSON block with the blob hash and the blob size. It is important to note that this request is different for normal blobs and SD (Stream Descriptor) blobs. The properties `blob_hash` and `blob_size` are required OR the properties `sd_blob_hash` and `sd_blob_size` are required. The hash cannot be empty and the size cannot be zero or exceed the maximum blob size.
Then, the server will check if it wants to receive the blob. It can do several checks, e.g. checking if it already has the blob or if the blob is blacklisted. If the server wants the blob, it will send a JSON block with the `send_blob` property for normal blobs and a JSON block with `send_sd_blob` for SD blobs. If the server notices that it has the SD blob, but misses some of the blobs defined in the SD blob, it will at those hashes to `needed_blobs`. If the server doesn't want the blob, the client can send information about another blob.
```json5
//REQUEST
{"blob_hash":"aabbcc","blob_size":123} // if version>=0
//or
{"sd_blob_hash":"ddeeff","sd_blob_size":456} // if version>=1
//RESPONSE
{"send_blob":false} // if version>=0
//or
{"send_blob":true} // if version>=0
//or
{"send_sd_blob":false} // if version>=1
//or
{"send_sd_blob":true} // if version>=1
//or
{"send_sd_blob":false,"needed_blobs":[]} // if version>=1
//or
{"send_sd_blob":true,"needed_blobs":[]} // if version>=1
//or
{"send_sd_blob":false,"needed_blobs":["aabbcc","ddeeff"]} // if version>=1
//or
{"send_sd_blob":true,"needed_blobs":["aabbcc","ddeeff"]} // if version>=1
```
## Blob Data
If the server wants the blob, it will read exactly the amount of bytes that were stated in the blob information. If there goes something wrong with sending the blob data (e.g. there was a socket timeout or the blob hash calculation didn't match the blob hash from the blob information), the server will send a message that it didn't receive the blob. This will be a JSON block with the `received_blob` property for normal blobs and a JSON block with `received_sd_blob` for SD blobs. After this message, the client can send information about another blob.
```json5
//REQUEST
/*[Raw Blob Data]*/
//RESPONSE
{"received_blob":false} // if version>=0
//or
{"received_blob":true} // if version>=0
//or
{"received_sd_blob":false} // if version>=1
//or
{"received_sd_blob":true} // if version>=1
```

View file

@ -15,7 +15,7 @@ description: Find the LBRY specification, API documentation, our Contributor's g
- [API Wrappers](/resources/api-wrappers)
- [LBRY SDK Configuration Settings](/resources/daemon-settings)
- [Claim Signing](/resources/claim-signing)
- [Regtest Setup](/resources/regtest-setup)
- [LBRY Android App Build Steps](/resources/android-build)
- [Lighthouse (search) API](https://github.com/lbryio/lighthouse)
- [Run Your Own Wallet Server](/resources/wallet-server)
- [Run Your Own lbry.tv](/resources/web-instance)

View file

@ -54,7 +54,7 @@ Configuration options are organized by their respective areas: Files, Wallet, Ne
| known_dht_nodes | list | ['lbrynet1.lbry.com:4444'] | ['myDHT.lbry.com:4444'] | Bootstrap nodes for network connectivity |
| max_connections_per_download | integer | 5 | 10 | Threads used to download blobs |
| seek_head_blob_first | boolean | true | false | Search for first data blob after downloading sd blob |
| tcp_port | integer | 3333 | 3334 | Port the SDK will listen on |
| tcp_port | integer | 4444 | 3334 | Port the SDK will listen on |
| concurrent_reflector_uploads| integer | 5 | 10 | Connections to use while uploading data to reflector |
| reflect_streams | boolean | true | false | Send published data to reflector servers |
| reflector_servers | list | ['reflector.lbry.com'] | ['myreflector.lbry.com'] | Server data will be reflected to |

View file

@ -0,0 +1,39 @@
---
title: Hosting a DHT bootstrap node
description: How to setup a bootstrap DHT node
---
This guide will help you setup and maintain a LBRY DHT [bootstrap node](https://en.wikipedia.org/wiki/Bootstrapping_node). Those nodes are important so people can join the network on first startup.
After finishing and checking that it works, if you want to add your node to the SDK bootstrap list just open a PR adding yourself to the [conf file](https://github.com/lbryio/lbry-sdk/blob/master/lbry/conf.py#L694) or an issue on the [SDK repo](https://github.com/lbryio/lbry-sdk/).
## Requirements
- Being reachable over UDP on the internet at some port
- 1GB of memory
- Docker or Python 3.7 (check [pyenv](https://github.com/pyenv/pyenv) if your Linux distribution doesn't offer that version)
## Running directly from Docker
This is the easiest way to run and maintain your node. Just run:
```bash
docker run -d -p 4444:4444 lbry/dht-bootstrap:latest
```
## Installing LBRY SDK from source
The most up to date guide for doing it will always be in the [INSTALL.md file](https://github.com/lbryio/lbry-sdk/blob/master/INSTALL.md). Please refer to it if you run into trouble. Otherwise, this should be enough most of the time (assuming requirements are all there):
```bash
git clone https://github.com/lbryio/lbry-sdk.git
make install
```
### Running a node from source
After installing, just:
```bash
python scripts/dht_node.py
```
### Checking if it is working
From another machine with the SDK installed, run:
```bash
python scripts/dht_node.py --bootstrap_node your-server-domain-here.com:4444
```
After 10-20 seconds, you should see more than 0 peers on the log messages. If that is not the case, check firewall on the bootstrap node and see if it is reachable.

View file

@ -0,0 +1,65 @@
---
title: LBRY P2P: Settings and troubleshooting
description: Guide on properly setting up P2P nodes and how to diagnose/fix common issues.
---
# LBRY P2P: Settings and troubleshooting
A very important step in supporting the network resilience is hosting content in a decentralized way. This already happens when you stream content using the desktop version of LBRY APP, but sometimes we want to make sure this is working or dedicate larger amounts of resources for this task.
This document aims to explain P2P configuration and troubleshooting from small to large nodes. If you don't know how to change SDK settings, check [this other document first](https://lbry.tech/resources/daemon-settings).
## Reachability
The first priority when seeding content is making sure there is a way for other nodes to reach you across the internet.
### Figuring out your ports
In order to troubleshoot reachability, we start by checking your configuration for the UDP and TCP ports. By default, they will both be set to 4444. Those can be found on the configuration under the keys `udp_port` and `tcp_port`. Please set them both to the same value as this helps connectivity trough [hole punching](https://en.wikipedia.org/wiki/Hole_punching_(networking)) and ease of management.
### Checking for reachability
There are some websites providing generic ways to check ports, like:
- https://www.portcheckers.com/
- https://portchecker.co/check
However, checking the port does not check if LBRY P2P protocol is working behind it. For a better check, we can use a tool hosted by Madiator, a community member.
- To test for UDP (DHT): http://test.madiator.com:60666/dht/<your `udp_port`>
- To test for TCP (P2P): http://test.madiator.com:60666/p2p/<your `tcp_port`>
As a last resource to test a remote machine DHT service, from a local SDK try:
```bash
lbrynet peer ping <DHT node id> <IP> <port>
```
To find out what the `DHT node id` is, on the target machine run `lbrynet status` and look for `node_id` .
### I am unreachable. What now?
VPN users: check with your provider if they feature port forwarding. There are guides specific to each one, like [this one from Mullvad](https://mullvad.net/en/help/port-forwarding-and-mullvad/).
Domestic routers: there are websites like [this one](https://portforward.com/how-to-port-forward/) providing information on popular router models. Unfortunately, this document would be huge if we added port forwarding instructions for every router/firewall.
Servers: check if your firewall is blocking the SDK ports. For ufw on Linux, this is `sudo ufw allow <port>`.
If you still have trouble figuring that out, don't be shy, [ask the LBRY community on Discord!](https://chat.lbry.com/)
## Content blobs storage settings
Files in LBRY are composed by `content blobs`, which can be seen as chunks of binary encrypted data belonging to some content. By default, the SDK enables saving blobs to disk, which then can be served over P2P. To check if that is enabled, look for the `save_blobs` setting.
**The following settings are isolated. The space limit set for one does not apply to the other.**
### Setting up storage space control
By default, content blobs are kept as long as the files are still in your file list. If you wish to allocate a space limit for content blobs and let the SDK decide what to delete, set `blob_storage_limit` to a value in megabytes.
This won't delete your downloads from the file list. Instead, it deletes content blobs associated with older files as space for newer blobs is requested.
### Setting up space for automatic contribution
This section is aimed at fully automatic contribution in background. Normal usage of the SDK with P2P enabled already helps the network, but requires interaction.
LBRY SDK can be configured to help the P2P network by automatically downloading and hosting content. This is ideal for contributing spare disk space and network bandwidth without further interaction.
Enabling: change `network_storage_limit` to the size (in megabytes) that will be used for automatic seeding.
Disabling: setting the space to 0 disables it. The space used will eventually be released automatically. For cleaning immediately, issue a `lbrynet blob clean` from command line.

View file

@ -1,208 +0,0 @@
---
title: Regtest Setup
description: Regtest is a parallel testing network for the LBRY blockchain. Learn how to use it in this resource article.
---
## Why Use Regtest
A regtest server provides for a way to instantly generate blocks so that transactions can be instantaneous, which ultimately means no waiting for confirmations from the blockchain. Also, its not a problem if you accidentally corrupt your wallet, since no real funds are lost! Delete the files and setup a new one.
## Setup
To begin setting up the network, there are a few things you need.
You'll need a Linux or a Mac distribution to run all this. A virtual machine is fine.
Note: These instructions specifically were tested on Ubuntu version 16.04.
### Virtual Environment
First up it's a good idea to create a Python virtual environment. This requires you to have a functional python2.7 setup, with the Python package manager `pip` installed. To create a new virtual environment in a folder `lbry-env`, run this:
`virtualenv -p /usr/bin/python2.7 lbry-env`
To enter the environment, run:
`source lbry-env/bin/activate`.
### lbrycrd
You need to download a build of `lbrycrd` from [here](https://github.com/lbryio/lbrycrd/releases/), no installation required. To configure `lbrycrd` you need to create a file at `~/.lbrycrd/lbrycrd.conf`,
containing the following:
```ini
rpcuser=test
rpcpassword=test
rpcport=18332
regtest=1
server=1
txindex=1
daemon=1
listen=0
discover=0
```
### lbryum-server
To install lbryum-server, you first need to install the package `leveldb`. After that, download the source from [here](https://github.com/lbryio/lbryum-server/releases), and run the following _not_ inside the environment:
```bash
cd lbryum-server
sudo pip2 install -r requirements.txt
```
If you're not running debian/\*buntu or a derivative of those, you need to edit the `configure` file a bit. In line 11, remove the `apt-get` line and manually install the required packages. In line 51, change `adduser` to `useradd` and on the same line, change `--disabled-password` to `-p !`.
```bash
sudo ./configure
sudo python2 setup.py install
```
The `sudo ./configure` command creates a new user in the system by the name "lbryum", which is the user through which we'll be running the server. lbryum-server also needs W/R access to `/var/lbryum-server`.
To do that run:
```bash
sudo chown -R lbryum /var/lbryum-server
```
When installed, append/use the following config options to the `/etc/lbryum.conf` file:
```ini
[lbrycrdd]
lbrycrdd_host = localhost
lbrycrdd_port = 18332
# user and password from lbrycrd.conf
lbrycrdd_user = test
lbrycrdd_password = test
[network]
type=lbrycrd_regtest
```
### lbryum
To install lbryum, first download the source from [here](https://github.com/lbryio/lbryum/releases). To install it, run the following inside the virtual environment:
```bash
cd lbryum
pip2 install -r requirements.txt
pip2 install -e .
```
After installation completes, you must set the config option for lbryum using:
```bash
lbryum setconfig default_servers '{ "localhost": { "t": "50001" }}'
lbryum setconfig chain 'lbrycrd_regtest'
```
Alternatively, you can create a file `touch ~/.lbryum/config` and paste the following config:
```json
{
"chain": "lbrycrd_regtest",
"default_servers": {
"localhost": {
"t": "50001"
}
}
}
```
### lbry
Download source from [here](https://github.com/lbryio/lbry-sdk/releases), and run the following inside the environment:
```bash
cd lbry
pip2 install -r requirements.txt
pip2 install -e .
mkdir ~/.lbrynet
touch ~/.lbrynet/daemon_settings.yml
```
Append the following in the newly created `~/.lbrynet/daemon_settings.yml` file:
```yml
blockchain_name: lbrycrd_regtest
lbryum_servers:
- localhost:50001
reflect_uploads: false
share_usage_data: false
use_upnp: false
```
### Last step
Go to the `lbryum` folder once again and run:
```bash
pip2 install -e .
```
This is to ensure that `lbrynet-daemon` uses the correct wallet.
## Firing up the regtest server
### Wallet backup
To start off, if you've already used LBRY on your machine, you need to backup the wallet by copying the folders `~/.lbrynet` and `~/.lbryum` and then deleting them to start from fresh. Run
`mkdir ~/.lbryum`
Now it should be all set-up. Just execute the commands in the following order, and the regtest server should be good to go.
### 1) lbrycrd
To run the `lbrycrd` daemon, run the following in the `lbrycrd` folder:
`./lbrycrdd`
To generate blocks, run `./lbrycrd-cli generate <num_of_blocks>`
You'll need to generate some blocks to get the network going. Start off by generating at least 100.
`./lbrycrd-cli generate 173`
If you'd prefer a more verbose output from lbrycrdd, run lbrycrd using:
`./lbrycrdd -printtoconsole`
### 2) lbryum-server
To run the server, run:
```bash
sudo runuser -l lbryum -c 'lbryum-server --conf=/etc/lbryum.conf'
```
Note: conf flag can be left out if the config is in the default directory(default: `/etc/lbryum.conf`)
### 3) lbryum
To run the lbryum, run:
```bash
lbryum daemon start
```
Generate some more blocks, get a wallet address by running:
`lbryum getunusedaddress`
and then send some credits to your wallet by doing
`./lbrycrd-cli sendtoaddress <address> <num_of_credits>`
### 4) lbry
You can now run `lbrynet-daemon`, and it should connect to the `lbryum`. Now you can use the regtest stack as you would normally use lbryum.
## Shutdown
To stop the network, run `lbrynet-cli daemon_stop`, `lbryum daemon stop`, and kill the `lbryum-server` process and stop lbrycrd by `lbrycrdd-cli stop`. If you want to use your wallet and the official servers again, backup the new regtest wallet, and replace it with your own.
## Note 1
You need to generate a few blocks every time you make a new transaction in the form of send, receive, claim, update, publish, support, tip, etc. for it to show up in the daemon and lbryum, etc.
## Note 2
If something goes wrong and you get a "Block not found" error, remember to delete `/var/lbryum-server` before trying again.
## Cheatsheet
#### Required processes in the correct order
```bash
lbrycrdd
sudo runuser -l lbryum -c 'lbryum-server --conf=/etc/lbryum.conf'
lbryum daemon start
lbrynet-daemon
```
#### Generate blocks
```bash
lbrycrd-cli generate 5
```
#### Get a wallet address
```bash
lbryum getunsusedaddress
```
#### Send credits from lbrycrd to your wallet
```bash
lbrycrd-cli sendtoaddress <address> <num_of_credits>
```

View file

@ -65,7 +65,7 @@ This document exists to introduce a project to a new visitor. It may also serve
### Security
* "We take security seriously. Please contact [security@lbry.com](mailto:security@lbry.com) regarding any security issues. Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it."
* "We take security seriously. Please contact [security@lbry.com](mailto:security@lbry.com) regarding any security issues. Our PGP key is [here](https://lbry.com/faq/pgp-key) if you need it."
### Contact

View file

@ -0,0 +1,59 @@
# How to spend your time locked transaction
This guide will walk you through the process of claiming a time locked transaction from a USB key. This involves accessing the transaction details on the key, making sure you have the latest version of `lbrynet` and finally using the transaction details to call `account_deposit` to claim your LBC.
## Check `lbrynet` version
If you already have `lbrynet` installed then you can check your version like this:
```
lbrynet version
```
If above command fails, you may need to start `lbrynet` first (and then try above again):
```
lbrynet start
```
If you do not have `lbrynet` installed or your version is less than `v0.108.0` then you can get latest version here:
[https://github.com/lbryio/lbry-sdk/releases](https://github.com/lbryio/lbry-sdk/releases)
## Gather Information
### Transaction ID and Transaction Output Number
1. On the USB key, find a file named `address.txt` and copy the address in this file.
1. Go to [LBRY Explorer](https://explorer.lbry.com/) and enter the address you copied.
1. You should see one transaction containing this address, click on this transaction.
1. You will need two pieces of information on this page, the `transaction id` and the `nout`.
1. The `transaction id` can be found at the top of the page and directly below the text `LBRY Transaction`.
1. The `nout` is the position of the output containing your address, starting with 0. Starting from the top of the list of outputs, count the outputs until you get to your address, then subtract 1 from this count, that is your `nout`.
### Private Key and Redeem Script
1. On the USB key, find a file named `key.zip` and unzip this file using the password emailed to you previously.
1. You should now have a file named `key.txt` which is base64 encoded and contains your `private key` and `redeem script`.
1. To decode the contents of the file you can use a website such as [base64decode.org](https://www.base64decode.org/) (not a secure option) or if you have Python installed you can do this on the command line:
```
python -m base64 -d /path/to/key.txt
```
1. After decoding you will see a key/value mapping of various items, including `privateKey` and `redeemScript`. Take note of these values.
## Redeem
Now that you have gathered the necessary information it is easy to redeem your LBC. Time locked transaction can be redeemed using the `lbrynet account deposit` command (fill in the values you gathered previously):
```
lbrynet account deposit <transaction id> <nout> <redeemScript> <privateKey>
```
If you get an error that says `AssertionError: Cannot find private key for signing output.`, try a different number for `<nout>` (for example, increase or decrease it by 1).
Enjoy your LBC!
## Get in touch
Whether you got to the end without a hiccup or you got stuck along the way, we want to hear from you. [Join our Discord](https://discord.gg/y3W9JuS) to get help, stay updated, and talk to other wallet server operators.

View file

@ -7,7 +7,7 @@ This guide will walk you through the process of setting up a LBRY wallet server.
## Start With A Fresh Server
We recommend a dual-core server with at least 16GB RAM, 100GB disk, and a fresh Ubuntu 18.04 install. Memory usage is flexible. 32 GB works best, but 16 GB is enough for a few clients.
We recommend a quad-core server with at least 16GB RAM, 200GB disk, and a fresh Ubuntu 18.04 install. Memory usage is flexible. 32 GB works best, but 16 GB is enough for a few clients.
Make sure your firewall has ports 9246 and 50001 open. 9246 is the port lbrycrd uses to communicate to other nodes. 50001 is the wallet server RPC port.
@ -24,7 +24,6 @@ daemon=1
rpcuser=lbry
rpcpassword=lbry
dustrelayfee=0.00000001
rpcworkqueue=128
```
Feel free to change the `rpcuser` or `rpcpassword`. If you do, you'll have to update the `DAEMON_URL` variable later on (in the docker-compose.yml file) to match the user/password you chose.
@ -41,11 +40,19 @@ Description="LBRYcrd daemon"
After=network.target
[Service]
ExecStart=/home/<your_user>/lbrycrdd -datadir="/home/<your_user>/.lbrycrd"
User=<your_user>
Group=<your_user_group>
ExecStart=/home/<your_user>/lbrycrdd -datadir="/home/<your_user>/.lbrycrd" -pid="/run/lbrycrdd/lbrycrdd.pid"
# Creates /run/lbrycrdd
RuntimeDirectory=lbrycrdd
Type=Forking
PIDFile=/run/lbrycrdd/lbrycrdd.pid
Restart=on-failure
KillMode=process
# hardening
PrivateTmp=true
ProtectSystem=full
NoNewPrivileges=true
PrivateDevices=true
MemoryDenyWriteExecute=true
[Install]
WantedBy=multi-user.target
@ -55,6 +62,8 @@ Then run `sudo systemctl daemon-reload`.
Now you can start and stop lbrycrd with `sudo service lbrycrdd start` and `sudo service lbrycrdd stop`.
You can watch the lbrycrd log with `tail -f ~/.lbrycrd/debug.log`
## Set Up Docker
### Install Docker & Docker Compose
@ -67,21 +76,58 @@ sudo systemctl enable docker && sudo systemctl start docker && \
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
sudo chmod +x /usr/local/bin/docker-compose
sudo usermod -aG docker $USER
```
### Download our docker-compose.yml
### Download our example docker-compose.yml
You can see it [here](https://github.com/lbryio/lbry-sdk/blob/master/docker/docker-compose-wallet-server.yml).
```
curl -L "https://raw.githubusercontent.com/lbryio/lbry-sdk/master/docker/docker-compose-wallet-server.yml" -o docker-compose.yml
```
Make sure the user and password in the `DAEMON_URL` variable (the `lbry@lbry` part) in this docker-compose.yml matches thes user/password in your `~/.lbrycrd/lbrycrd.conf` file.
Make sure the user and password in the `DAEMON_URL` variable (the `lbry@lbry` part) in this docker-compose.yml matches the user/password in your `~/.lbrycrd/lbrycrd.conf` file.
### Download snapshots for elasticsearch and the wallet server (optional)
You can skip the initial sync by starting from a snapshot. The following will download a snapshot of the elasticsearch volume and move it into the default location for docker volumes on ubuntu, on other systems you may need to adjust the path used here. Note: snapshot heights must be the same. The tars can be deleted after setting the volumes up.
```bash
SNAPSHOT_HEIGHT="1049658"
ES_VOLUME_PATH="/var/lib/docker/volumes/${USER}_es01"
ES_SNAPSHOT_TAR_NAME="es_snapshot_${SNAPSHOT_HEIGHT}.tar"
ES_SNAPSHOT_URL="https://snapshots.lbry.com/hub/${ES_SNAPSHOT_TAR_NAME}"
wget $ES_SNAPSHOT_URL
echo "decompressing elasticsearch snapshot"
tar -xf $ES_SNAPSHOT_TAR_NAME
sudo chown -R $USER:root "snapshot_es_${SNAPSHOT_HEIGHT}"
sudo chmod -R 775 "snapshot_es_${SNAPSHOT_HEIGHT}"
sudo mkdir -p $ES_VOLUME_PATH
sudo rm -rf "${ES_VOLUME_PATH}/_data"
sudo mv "snapshot_es_${SNAPSHOT_HEIGHT}" "${ES_VOLUME_PATH}/_data"
```
The following will download the wallet server docker volume and move it into place as well.
```bash
echo "fetching wallet server snapshot"
SNAPSHOT_HEIGHT="1049658"
HUB_VOLUME_PATH="/var/lib/docker/volumes/${USER}_wallet_server"
SNAPSHOT_TAR_NAME="wallet_server_snapshot_${SNAPSHOT_HEIGHT}.tar"
SNAPSHOT_URL="https://snapshots.lbry.com/hub/${SNAPSHOT_TAR_NAME}"
wget $SNAPSHOT_URL
tar -xf $SNAPSHOT_TAR_NAME
sudo mkdir -p $HUB_VOLUME_PATH
sudo rm -rf "${HUB_VOLUME_PATH}/_data"
sudo chown -R 999:999 "snapshot_${SNAPSHOT_HEIGHT}"
sudo mv "snapshot_${SNAPSHOT_HEIGHT}" "${HUB_VOLUME_PATH}/_data"
```
## Turn It On
### Start the servers
```
docker-compose up --detach
```
@ -111,7 +157,7 @@ echo '{"id":1,"method":"server.version"}' | timeout 1 curl telnet://localhost:50
You should see a response like `{"jsonrpc": "2.0", "result": ["0.46.1", "0.0"], "id": 1}`. If you do, congratulations! You've set up your own wallet server.
To check Elastic search, there are two commands you can use:
To check Elastic search, there are two commands you can use:
```
curl localhost:9200 # get Elastic status
@ -142,7 +188,7 @@ From time to time, we'll release an update that requires recreating one of the d
The process is similar to an update, but causes the server to be down for much longer.
#### Main database
Holds the raw blockchain data and takes many hours (can take a day or two) to resync from scratch, so be sure to have a snapshot or try that last.
Holds the raw blockchain data and takes several days to resync from scratch, so be sure to have a snapshot or try that last.
```
docker pull lbry/wallet-server:latest-release
@ -152,8 +198,7 @@ WALLET_SERVER_SNAPSHOT_URL= docker-compose up --detach
```
#### Elasticsearch
ES does the indexing of claims from the main database. It should take around 30 minutes to resync on a fast machine.
ES does the indexing of claims from the main database. It should take around 6 hours to resync on a fast machine.
```
docker pull lbry/wallet-server:latest-release

View file

@ -0,0 +1,79 @@
---
title: Hosting your own LBRY Web Instance
description: Setting up an app instance as a webpage.
---
Run your own instance of https://lbry.tv using Docker images.
## Run the SDK
The LBRY SDK provides RPC and streaming endpoints to interact with the LBRY network. Web users will connect to it directly, so it must be web-accessible. You may have to open ports on your firewall.
```
docker run -d -p 5279:5279 -p 5280:5280 vshyba/websdk
```
This image will not save files to disk. It has the `save_blobs` and `save_files` config options set to `false`. If you want to save files, see [Building your own SDK image](#building-your-own-sdk-image) below.
## Run the web app
Clone and install the app as described in the [lbry-desktop repo README](https://github.com/lbryio/lbry-desktop).
If you want to customize it further, follow the extra steps in `Customize the web app` section. Otherwise:
```
git clone https://github.com/lbryio/lbry-desktop.git
yarn
cp .env.defaults .env
```
Configure .env with the following settings. They must match the SDK ports in the previous section.
```
WEB_SERVER_PORT=8080
SDK_API_PATH=http://localhost:5279
LBRY_WEB_API=http://localhost:5279
LBRY_WEB_STREAMING_API=http://localhost:5280
LBRY_API_URL=http://disabled-api/
LBRY_WEB_BUFFER_API=https://disabled
```
Compile and run
```
NODE_ENV=production yarn compile:web
nodejs web/index.js
```
## Building your own SDK image
If you want to customize the SDK settings, you can
Clone the SDK repo:
```
git clone https://github.com/lbryio/lbry-sdk.git
```
Create a `docker/webconf.yaml` file and modify as you need. This is a good start:
```
allowed_origin: "*"
max_key_fee: "0.0 USD"
save_files: false
save_blobs: false
streaming_server: "0.0.0.0:5280"
api: "0.0.0.0:5279"
data_dir: /tmp
download_dir: /tmp
wallet_dir: /tmp
```
Note that it is required to have `streaming_server` and `api` set to user-accessible IPs. If you want this to be accessible on the open web, that means setting them to `0.0.0.0`.
To build the image, run:
```
docker build -f docker/Dockerfile.web -t <your dockerhub username>/<project name, like 'websdk'> .
docker push <dockerhub username/project name>
```

View file

@ -0,0 +1,73 @@
## Hello Satoshi - The LBRY "Hello World" Tutorial
Let's get started with a simple "Hellow World" tutorial... LBRY style!
This tutorial will guide you through creating a basic [Electron](https://electronjs.org) application that calls to the LBRY network and renders an image returned by the network.
Electron is nice because it allows you to easily create web apps that don't rely on any centralized web servers, but you can absolutely use any tooling or language you would like.
### Prerequisites
This tutoral only has a few simple requirements:
- [npm](https://www.npmjs.com). Learn how to install it [here](https://www.npmjs.com/get-npm).
- [git](https://git-scm.com/).
Once you have those installed (see the links above for downloads and How-To's), you are ready to begin!
#### Step 1. Download and build the starter project
Grab "[electron-starter](https://github.com/lbryio/electron-starter)". This project serves as a base upon which you can build LBRY applications. (Similar to "create-react-app" for React development.)
If you have git and npm installed, run the following lines one at a time:
```
git clone https://github.com/lbryio/electron-starter
cd electron-starter
npm install
npm run dev
```
#### Step 2. Make sure everything works
Before we make any changes, it's a good idea to verify that everything is working correctly.
Try typing a word into the text input and click the button to [resolve](https://lbry.tech/api/sdk#resolve) it.
This performs a [[claim]] lookup, which retrieves metadata the title, thumbnail, and file type from the LBRY blockchain.
Try resolving `lbry://doitlive`.
If you received no errors, move on to Step 3! Otherwise, head back to Step 1 to make sure you have all the requirements installed correctly.
#### Step 3. Make a small change to the code
Now that we have the metadata, let's [get](https://lbry.tech/api/sdk#get) the actual file!
The code to do this is already there, just un-comment these lines in the app's [renderer/index.js](https://github.com/lbryio/electron-starter/blob/master/src/renderer/index.js) file.
```js
claimData.innerText = "Loading...";
Lbry.get({ uri: `lbry://${value}` })
.then(result => {
const filePath = result.download_path;
const image = document.createElement("img");
image.src = filePath;
imageWrapper.appendChild(image);
claimData.innerText = JSON.stringify(result, null, 2);
})
.catch(error => {
claimData.innerText = JSON.stringify(error, null, 2);
});
```
This is the code that actually downloads a file.
There are more robust ways to handle the download progress, but this will work fine for images. After you added that code back, try `get`ing `lbry://doitlive`.
### Success! You Did It!
While our Hello Satoshi app isn't much to look at, it shows how simple it is to connect to the LBRY network and download files!

40
documents/tutorials.md Normal file
View file

@ -0,0 +1,40 @@
---
title: Tutorials
description: Learn how to setup, use, deploy, and develop with LBRY.
---
## Deployment tutorials
These tutorials will explain how to run different elements from the LBRY Network.
### Blockchain
- [Setup a LBRY blockchain with lbrycrd](/tutorials/setup-blockchain-lbrycrd)
- [Setup a LBRY blockchain with lbcd](/tutorials/setup-blockchain-lbcd)
### Hub
- [Setup a LBRY Hub](/tutorials/setup-hub)
### SDK
- [Setup the LBRY SDK with lbrynet](/tutorials/setup-sdk-lbrynet)
## Programming tutorials
### Tutorial #1 - "Hello Satoshi!"
Learn how to [create and modify a simple LBRY electron application](/tutorial-hellosatoshi) we'll call "[Hello Satoshi](/tutorial-hellosatoshi)".
---
## Old tutorials
### Setup your Development Environment
- **Desktop Application** - [Video tutorial](/resources/video-lbrydesktop) to setup the [Desktop app](https://github.com/lbryio/lbry-desktop) development environment.
- **Android Application** - [Video tutorial](/resources/video-lbryandroid) to setup the [Android app](https://github.com/lbryio/lbry-android) development environment.
- **LBRY SDK** - [Video tutorial](/resources/video-lbrysdk) to setup the [LBRY SDK](https://github.com/lbryio/lbry-sdk) development environment.
- **LBRY Blockchain** - [Video tutorial](/resources/video-lbrycrd) to setup the [LBRY Blockchain](https://github.com/lbryio/lbrycrd) development environment.

View file

@ -0,0 +1,53 @@
---
title: Setup LBRY blockchain with LBCD
description: Learn how to setup the LBRY blockchain with LBCD.
---
## Running
### With Docker
```shell
docker run -d lbryfoundation/lbcd
```
Or if you want to change some parameters:
```shell
docker run --entrypoint "lbcd --notls" -d lbryfoundation/lbcd
```
For all possible parameters, see [doc.go](https://github.com/lbryio/lbcd/blob/a0ff51b84acc553c9e9568e80c7873c03e24d679/doc.go). E.g., when changing the RPC credentials, use `--rpcuser` and `--rpcpass`.
### With Docker Compose
Create a `docker-compose.yml` file with this content:
```yml
version: "3"
volumes:
lbcd:
services:
lbcd:
image: lbry/lbcd:latest
restart: always
network_mode: host
command:
- "--notls"
- "--rpcuser=lbry"
- "--rpcpass=lbry"
- "--rpclisten=127.0.0.1"
volumes:
- "lbcd:/root/.lbcd"
ports:
- "127.0.0.1:9245:9245"
- "9246:9246" # p2p port
```
Then run:
```shell
docker-compose up -d
```

View file

@ -0,0 +1,33 @@
---
title: Setup LBRY blockchain with LBRYCRD
description: Learn how to setup the LBRY blockchain with LBRYCRD.
---
<Note/>
## Building
### With Docker
```shell
git clone https://github.com/lbryio/lbrycrd.git
cd lbrycrd
docker run -v "$(pwd):/lbrycrd" --rm -v "${HOME}/ccache:/ccache" -w /lbrycrd -e CCACHE_DIR=/ccache lbry/build_lbrycrd packaging/build_linux_64bit.sh
```
### Ubuntu
```shell
sudo apt install build-essential git libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates
git clone https://github.com/lbryio/lbrycrd.git
cd lbrycrd
./packaging/build_linux_64bit.sh
./src/test/test_lbrycrd
```
## Running
```shell
lbrycrdd -server -daemon
```

View file

@ -0,0 +1,42 @@
---
title: Setup Elasticsearch
description: Learn how to setup Elasticsearch.
---
## Running
### With Docker Compose
Create a `docker-compose.yml` file with this content:
```yml
version: "3"
volumes:
es01:
services:
es01:
image: docker.elastic.co/elasticsearch/elasticsearch:7.16.0
container_name: es01
environment:
- node.name=es01
- discovery.type=single-node
- indices.query.bool.max_clause_count=8192
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Dlog4j2.formatMsgNoLookups=true -Xms8g -Xmx8g" # no more than 32, remember to disable swap
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- "es01:/usr/share/elasticsearch/data"
ports:
- "127.0.0.1:9200:9200"
```
Then run:
```shell
docker-compose up -d
```

View file

@ -0,0 +1,84 @@
---
title: Setup LBRY Hub
description: Learn how to setup the LBRY Hub.
---
## Running
The Hub needs a running Elasticsearch instance. [Learn how to set up Elasticsearch.](/tutorials/setup-elasticsearch)
### With Docker Compose
Create a `docker-compose.yml` file with this content:
```shell
version: "3"
volumes:
lbry_rocksdb:
services:
scribe:
depends_on:
- scribe_elastic_sync
image: lbry/hub:${SCRIBE_TAG:-master}
restart: always
network_mode: host
volumes:
- "lbry_rocksdb:/database"
environment:
- HUB_COMMAND=scribe
- SNAPSHOT_URL=https://snapshots.lbry.com/hub/lbry-rocksdb.zip
command:
- "--daemon_url=http://lbry:lbry@127.0.0.1:9245"
- "--max_query_workers=2"
- "--cache_all_tx_hashes"
- "--index_address_statuses"
scribe_elastic_sync:
image: lbry/hub:${SCRIBE_TAG:-master}
restart: always
network_mode: host
ports:
- "127.0.0.1:19080:19080" # elastic notifier port
volumes:
- "lbry_rocksdb:/database"
environment:
- HUB_COMMAND=scribe-elastic-sync
- FILTERING_CHANNEL_IDS=770bd7ecba84fd2f7607fb15aedd2b172c2e153f 95e5db68a3101df19763f3a5182e4b12ba393ee8
- BLOCKING_CHANNEL_IDS=dd687b357950f6f271999971f43c785e8067c3a9 06871aa438032244202840ec59a469b303257cad b4a2528f436eca1bf3bf3e10ff3f98c57bd6c4c6
command:
- "--elastic_host=127.0.0.1"
- "--elastic_port=9200"
- "--max_query_workers=2"
herald:
depends_on:
- scribe_elastic_sync
- scribe
image: lbry/hub:${SCRIBE_TAG:-master}
restart: always
network_mode: host
ports:
- "50001:50001" # electrum rpc port and udp ping port
- "2112:2112" # comment out to disable prometheus metrics
volumes:
- "lbry_rocksdb:/database"
environment:
- HUB_COMMAND=herald
- FILTERING_CHANNEL_IDS=770bd7ecba84fd2f7607fb15aedd2b172c2e153f 95e5db68a3101df19763f3a5182e4b12ba393ee8
- BLOCKING_CHANNEL_IDS=dd687b357950f6f271999971f43c785e8067c3a9 06871aa438032244202840ec59a469b303257cad b4a2528f436eca1bf3bf3e10ff3f98c57bd6c4c6
command:
- "--index_address_statuses"
- "--daemon_url=http://lbry:lbry@127.0.0.1:9245"
- "--elastic_host=127.0.0.1"
- "--elastic_port=9200"
- "--max_query_workers=4"
- "--host=0.0.0.0"
- "--max_sessions=100000"
- "--prometheus_port=2112" # comment out to disable prometheus metrics
```
Then run:
```shell
docker-compose up -d
```

View file

@ -0,0 +1,19 @@
---
title: Setup LBRY SDK with lbrynet
description: Learn how to setup the LBRY SDK with lbrynet.
---
## Running
The media server RPC will run on `127.0.0.1:5279` by default, so it is only accessibly by the machine itself.
### With executable
- Download the latest version at [https://github.com/lbryio/lbry-sdk/releases](https://github.com/lbryio/lbry-sdk/releases) for the right target operating system.
- Extract the ZIP file.
- Run:
```shell
lbrynet start
```

View file

@ -1,14 +0,0 @@
"use strict";
// P A C K A G E S
require("@babel/register");
require("@babel/polyfill");
require("date-format-lite");
require("dotenv").config();
// U T I L
require("./app");

13961
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,115 +1,50 @@
{
"_moduleAliases": {
"~component": "app/components",
"~data": "app/data",
"~helper": "app/helpers",
"~module": "app/modules",
"~root": ".",
"~socket": "app/sockets.js",
"~view": "app/views"
},
"author": "LBRY Team",
"dependencies": {
"@babel/polyfill": "^7.4.4",
"@inc/fastify-ws": "^2019.7.23",
"@octokit/rest": "^16.28.7",
"@slack/client": "^5.0.2",
"async": "^3.1.0",
"async-es": "^3.1.0",
"choo": "6.13.3",
"choo-async": "^0.1.1",
"choo-devtools": "^3.0.0",
"choo-ssr": "^0.2.1",
"choo-websocket": "^2.0.0",
"colorette": "^1.1.0",
"cors": "^2.8.5",
"cron": "^1.7.1",
"@elysiajs/node": "^1.2.6",
"@elysiajs/static": "^1.2.0",
"@hono/node-server": "^1.14.1",
"date-format-lite": "^17.7.0",
"decamelize": "^3.2.0",
"dedent": "^0.7.0",
"dotenv": "^8.0.0",
"fastify": "~2.7.1",
"fastify-compress": "^0.10.0",
"fastify-helmet": "^3.0.1",
"fastify-static": "^2.5.0",
"front-matter": "^3.0.2",
"fs-exists-sync": "^0.1.0",
"got": "^9.6.0",
"graceful-fs": "^4.2.1",
"link-module-alias": "^1.2.0",
"make-promises-safe": "^5.0.0",
"markdown-it": "^9.0.1",
"markdown-it-anchor": "^5.2.4",
"prismjs": "^1.17.1",
"redis": "^2.8.0",
"slack-node": "^0.1.8",
"socket.io": "^2.2.0",
"stringify-object": "^3.3.0",
"request": "latest"
"dotenv": "^8.6.0",
"elysia": "^1.2.25",
"front-matter": "^4.0.2",
"hono": "^4.7.7",
"markdown-it": "^14.1.0",
"markdown-it-anchor": "^9.2.0",
"pino-pretty": "^3.2.0",
"prismjs": "^1.30.0",
"socket.io": "^4.8.1"
},
"description": "Documentation for the LBRY protocol and associated projects",
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/plugin-external-helpers": "7.2.0",
"@babel/plugin-proposal-class-properties": "7.5.5",
"@babel/plugin-proposal-decorators": "7.4.4",
"@babel/plugin-proposal-export-namespace-from": "7.5.2",
"@babel/plugin-proposal-function-sent": "7.5.0",
"@babel/plugin-proposal-json-strings": "7.2.0",
"@babel/plugin-proposal-numeric-separator": "7.2.0",
"@babel/plugin-proposal-throw-expressions": "7.2.0",
"@babel/plugin-syntax-dynamic-import": "7.2.0",
"@babel/plugin-syntax-import-meta": "7.2.0",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"@inc/eslint-config": "^2019.7.23",
"@inc/sasslint-config": "^2019.7.23",
"@lbry/color": "^1.1.1",
"@lbry/components": "^2019.6.22",
"@springernature/sasslint-config": "^1.2.1",
"eslint": "^6.1.0",
"eslint-config": "^0.2.1",
"husky": "^3.0.2",
"nodemon": "^1.19.1",
"npm-run-all": "^4.1.5",
"pino-pretty": "^3.2.0",
"sass": "^1.22.9",
"sass-lint": "^1.13.1",
"snazzy": "^8.0.0",
"standardx": "^4.0.0",
"updates": "^8.5.1"
},
"engines": {
"node": "10.2.x"
"sass": "^1.87.0",
"sass-lint": "^1.13.1"
},
"husky": {
"hooks": {
"pre-commit": "npm run format && npm run test:sass && git add -A :/"
}
},
"main": "server.js",
"main": "app/index.js",
"name": "lbry.tech",
"optionalDependencies": {
"win-node-env": "^0.4.0"
},
"type": "module",
"private": true,
"scripts": {
"css": "sass --load-path=node_modules --update app/sass:app/dist --style compressed",
"format": "eslint . --fix --ignore-pattern '/app/dist/'",
"postinstall": "link-module-alias",
"preinstall": "command -v link-module-alias;link-module-alias clean || true",
"start": "npm i && npm run css && NODE_ENV=production node index.js",
"test": "run-s test:*",
"start": "bun i && bun run css && NODE_ENV=production bun app/index.js",
"test": "npm run test:dependencies & npm run test:lint & npm run test:sass",
"test:dependencies": "updates --update ./ --exclude prismjs",
"test:lint": "standardx --verbose | snazzy",
"test:lint": "snazzy",
"test:sass": "sass-lint --config ./node_modules/@inc/sasslint-config/config.json --verbose --no-exit",
"watch": "npm run css && run-p watch:*",
"watch": "bun run css && bun run watch:server & bun run watch:sass",
"watch:sass": "sass --load-path=node_modules --watch app/sass:app/dist --style compressed",
"watch:server": "NODE_ENV=development nodemon --ignore 'app/dist'"
"watch:server": "NODE_ENV=development bun --watch app/index.js"
},
"standardx": {
"ignore": [
"app/dist"
]
},
"version": "5.3.2"
"version": "6.0.0"
}