setup subscriptions and relay pool

This commit is contained in:
hzrd149 2023-02-07 17:04:17 -06:00
parent c943250134
commit 452a2f66f7
12 changed files with 443 additions and 131 deletions

View File

@ -23,6 +23,7 @@
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-markdown": "^8.0.4",
"react-router-dom": "^6.5.0"
"react-router-dom": "^6.5.0",
"react-use": "^17.4.0"
}
}

View File

@ -1,38 +1,23 @@
import React, { useEffect } from "react";
import React from "react";
import { ChakraProvider } from "@chakra-ui/react";
import { HashRouter, Route, Routes } from "react-router-dom";
import { connectToRelays } from "./services/relays";
import { HomeView } from "./views/home";
import { UserView } from "./views/user";
import { ErrorBoundary } from "./components/error-boundary";
import { SettingsView } from "./views/settings";
import { WaitForRelays } from "./components/wait-for-relays";
export const App = () => {
useEffect(() => {
connectToRelays();
}, []);
return (
<React.StrictMode>
<ChakraProvider>
<ErrorBoundary>
<HashRouter>
<Routes>
<Route
path="/user/:pubkey"
element={
<WaitForRelays min={1}>
<UserView />
</WaitForRelays>
}
/>
<Route path="/settings" element={<SettingsView />} />
<Route path="/" element={<HomeView />} />
</Routes>
</HashRouter>
</ErrorBoundary>
</ChakraProvider>
</React.StrictMode>
<ChakraProvider>
<ErrorBoundary>
<HashRouter>
<Routes>
<Route path="/user/:pubkey" element={<UserView />} />
<Route path="/settings" element={<SettingsView />} />
<Route path="/" element={<HomeView />} />
</Routes>
</HashRouter>
</ErrorBoundary>
</ChakraProvider>
);
};

View File

@ -0,0 +1,20 @@
import { useRef } from "react";
import { useDeepCompareEffect, useMount, useUnmount } from "react-use";
import { Subscription } from "../services/subscriptions";
export function useSubscription(urls, query, watch = []) {
const sub = useRef(null);
sub.current = sub.current || new Subscription(urls, query);
useMount(() => {
if (sub.current) sub.current.open();
});
useDeepCompareEffect(() => {
if (sub.current) sub.current.setQuery(query);
}, [query]);
useUnmount(() => {
if (sub.current) sub.current.close();
});
return sub.current;
}

View File

@ -3,7 +3,8 @@ import { useCallback, useEffect } from "react";
export function useSignal(signal, fn, watch = []) {
const listener = useCallback(fn, watch);
useEffect(() => {
if (!signal) return;
signal.addListener(listener);
return () => signal.removeListener(listener);
}, [listener]);
}, [signal, listener]);
}

View File

@ -1,16 +1,17 @@
const MIGRATIONS = [
// 0 -> 1
function (db, transaction, event) {
db.createObjectStore("users", {
const userMetadata = db.createObjectStore("user-metadata", {
keyPath: "pubkey",
});
userMetadata.createIndex("id", "id", { unique: true });
db.createObjectStore("contacts", {
keyPath: "pubkey",
});
db.createObjectStore("settings");
// setup data
const settings = transaction.objectStore("settings");
const settings = db.createObjectStore("settings");
settings.put(["wss://nostr.rdfriedl.com"], "relays");
},
];

View File

@ -1,48 +1,53 @@
import { Signal } from "../../helpers/signal";
import { getRelays } from "../settings";
// import { Signal } from "../../helpers/signal";
// import { getRelays } from "../settings";
// import { Relay } from "./relay";
import relayPool, { RelayPool } from "./relay-pool";
import { Relay } from "./relay";
const connections = new Map();
export const onEvent = new Signal();
// const connections = new Map();
// export const onEvent = new Signal();
export function getAllActive() {
return Array.from(connections.values()).filter((relay) => relay.connected);
}
// export function getAllActive() {
// return Array.from(connections.values()).filter((relay) => relay.connected);
// }
export async function connectToRelay(url) {
if (!connections.has(url)) {
const relay = new Relay(url);
connections.set(url, relay);
// export async function connectToRelay(url) {
// if (!connections.has(url)) {
// const relay = new Relay(url);
// connections.set(url, relay);
// send all onEvent events to the main onEvent signal
relay.onEvent.addConnection(onEvent);
}
}
export async function connectToRelays() {
const relayUrls = await getRelays();
// // send all onEvent events to the main onEvent signal
// relay.onEvent.addConnection(onEvent);
// }
// }
// export async function connectToRelays() {
// const relayUrls = await getRelays();
for (const url of relayUrls) {
await connectToRelay(url);
}
}
// for (const url of relayUrls) {
// await connectToRelay(url);
// }
// }
const messageQueue = [];
export async function sendNextMessage() {
const next = messageQueue.shift();
if (!next) return;
let sent = false;
for (const [url, relay] of connections) {
if (relay.connected) {
relay.send(next);
sent = true;
}
}
if (!sent) {
messageQueue.unshift(next);
}
}
setInterval(sendNextMessage, 100);
// const messageQueue = [];
// export async function sendNextMessage() {
// const next = messageQueue.shift();
// if (!next) return;
// let sent = false;
// for (const [url, relay] of connections) {
// if (relay.connected) {
// relay.send(next);
// sent = true;
// }
// }
// if (!sent) {
// messageQueue.unshift(next);
// }
// }
// setInterval(sendNextMessage, 100);
export function subscribeToAuthor(pubkey) {
messageQueue.push(["REQ", "test", { authors: [pubkey] }]);
}
// export function subscribeToAuthor(pubkey) {
// messageQueue.push(["REQ", "test", { authors: [pubkey] }]);
// }
export { relayPool, Relay, RelayPool };

View File

@ -0,0 +1,66 @@
import { Relay } from "./relay";
export class RelayPool {
relays = new Map();
relayClaims = new Map();
getRelayClaims(url) {
if (!this.relayClaims.has(url)) {
this.relayClaims.set(url, new Set());
}
return this.relayClaims.get(url);
}
requestRelay(url, connect = true) {
if (!this.relays.has(url)) {
this.relays.set(url, new Relay(url));
}
const relay = this.relays.get(url);
if (connect && !relay.okay) {
relay.open();
}
return relay;
}
pruneRelays() {
for (const [url, relay] of this.relays.entries()) {
const claims = this.getRelayClaims(url).size;
if (claims === 0) {
relay.close();
}
}
}
reconnectRelays() {
for (const [url, relay] of this.relays.entries()) {
const claims = this.getRelayClaims(url).size;
if (!relay.okay && claims > 0) {
relay.open();
}
}
}
// id can be anything
addClaim(url, id) {
this.getRelayClaims(url).add(id);
}
removeClaim(url, id) {
this.getRelayClaims(url).delete(id);
}
get connectedCount() {
let count = 0;
for (const [url, relay] of this.relays.entries()) {
if (relay.connected) count++;
}
return count;
}
}
const relayPool = new RelayPool();
if (import.meta.env.DEV) {
window.relayPool = relayPool;
}
export default relayPool;

View File

@ -8,25 +8,28 @@ export class Relay {
this.onClose = new Signal();
this.onEvent = new Signal();
this.onNotice = new Signal();
this.connect();
}
connect() {
if (this.connected || this.connecting) return;
open() {
if (this.okay) return;
this.ws = new WebSocket(this.url);
this.ws.onopen = this.handleOpen.bind(this);
this.ws.onclose = this.handleClose.bind(this);
this.ws.onmessage = this.handleMessage.bind(this);
}
send(json) {
if (this.connected) {
this.ws.send(JSON.stringify(json));
}
}
close() {
this.ws?.close();
}
get okay() {
return this.connected || this.connecting;
}
get connected() {
return this.ws?.readyState === WebSocket.OPEN;
}
@ -38,29 +41,35 @@ export class Relay {
}
handleMessage(event) {
const data = JSON.parse(event.data);
const type = data[0];
try {
const data = JSON.parse(event.data);
const type = data[0];
switch (type) {
case "EVENT":
this.onEvent.emit({ subId: data[1], body: data[2] });
break;
case "NOTICE":
this.onEvent.emit({ message: data[1] });
break;
switch (type) {
case "EVENT":
this.onEvent.emit({ type, subId: data[1], body: data[2] }, this);
break;
case "NOTICE":
this.onNotice.emit({ type, message: data[1] }, this);
break;
}
} catch (e) {
console.log(`Failed to parse event from ${this.url}`);
console.log(event);
}
}
handleOpen() {
console.log(this.url, "connected");
this.onOpen.emit(this);
this.onOpen.emit();
if (import.meta.env.DEV) {
console.info(`Relay ${this.url} opened`);
}
}
handleClose() {
console.log(this.url, "reconnecting in 10s");
this.onClose.emit(this);
this.onClose.emit();
setTimeout(() => {
this.connect();
}, 1000 * 10);
if (import.meta.env.DEV) {
console.info(`Relay ${this.url} closed`);
}
}
}

View File

@ -7,9 +7,13 @@ export async function setRelays(relays = []) {
return await db.put("settings", relays, "relays");
}
const settingsService = {
getRelays,
setRelays,
};
if (import.meta.env.DEV) {
window.relayService = {
getRelays,
setRelays,
};
window.settingsService = settingsService;
}
export default settingsService;

View File

@ -1,22 +1,25 @@
import { Signal } from "../helpers/signal";
import { getAllActive } from "./relays";
import relayPool from "./relays/relay-pool";
import settingsService from "./settings";
export class Subscription {
static OPEN = "open";
static IDLE = "open";
static CLOSED = "closed";
constructor(relays, query) {
constructor(relayUrls, query) {
this.id = String(Math.floor(Math.random() * 1000000));
this.relays = relays;
this.status = Subscription.OPEN;
this.query = query;
this.relayUrls = relayUrls;
this.status = Subscription.IDLE;
this.relays = relayUrls.map((url) => relayPool.requestRelay(url));
this.onEvent = new Signal();
for (const relay of this.relays) {
relay.onEvent.addListener(this.handleEvent, this);
}
this.send(["REQ", this.id, query]);
}
handleOpen(relay) {
// when the relay connects send the req event
relay.send(["REQ", this.id, this.query]);
}
handleEvent(event) {
if (event.subId === this.id) {
@ -28,17 +31,52 @@ export class Subscription {
relay.send(message);
}
}
setQuery(query) {
this.query = query;
// if open, than update remote subscription
if (this.state === Subscription.OPEN) {
this.send(["REQ", this.id, this.query]);
}
}
open() {
this.state = Subscription.OPEN;
this.send(["REQ", this.id, this.query]);
for (const relay of this.relays) {
relay.onEvent.addListener(this.handleEvent, this);
relay.onOpen.addListener(this.handleOpen, this);
}
for (const url of this.relayUrls) {
relayPool.addClaim(url, this);
}
if (import.meta.env.DEV) {
console.info(`Subscription ${this.id} opened`);
}
}
close() {
this.status = Subscription.CLOSED;
this.send(["CLOSE", this.id]);
for (const relay of this.relays) {
relay.onEvent.removeListener(this.handleEvent, this);
relay.onOpen.removeListener(this.handleOpen, this);
}
for (const url of this.relayUrls) {
relayPool.removeClaim(url, this);
}
if (import.meta.env.DEV) {
console.info(`Subscription ${this.id} closed`);
}
}
}
export function createSubscription(query) {
const relays = getAllActive();
return new Subscription(relays, query);
export async function createSubscription(query) {
const urls = await settingsService.getRelays();
return new Subscription(urls, query);
}

View File

@ -1,26 +1,26 @@
import React, { useEffect, useMemo, useState } from "react";
import { Card, CardBody, SkeletonText, useMediaQuery } from "@chakra-ui/react";
import React, { useState } from "react";
import { Card, CardBody, SkeletonText } from "@chakra-ui/react";
import ReactMarkdown from "react-markdown";
import { onEvent, subscribeToAuthor } from "../../services/relays";
import settingsService from "../../services/settings";
import { useSignal } from "../../hooks/use-signal";
import { createSubscription } from "../../services/subscriptions";
import { useSubscription } from "../../helpers/use-subscription";
const relayUrls = await settingsService.getRelays();
export const UserPostsTab = ({ pubkey }) => {
const [events, setEvents] = useState({});
useEffect(() => {
if (pubkey) {
const sub = createSubscription({ authors: [pubkey] });
const sub = useSubscription(relayUrls, { authors: [pubkey] }, [pubkey]);
sub.onEvent.addListener((event) => {
if (event.kind === 1) {
setEvents((dir) => ({ [event.id]: event, ...dir }));
}
});
return () => sub.close();
}
}, [pubkey]);
useSignal(
sub?.onEvent,
(event) => {
if (event.kind === 1) {
setEvents((dir) => ({ [event.id]: event, ...dir }));
}
},
[setEvents]
);
const timeline = Object.values(events).sort(
(a, b) => a.created_at - b.created_at

192
yarn.lock
View File

@ -875,7 +875,7 @@
"@babel/types" "^7.4.4"
esutils "^2.0.2"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.8.4":
version "7.20.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3"
integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==
@ -2112,6 +2112,11 @@
dependencies:
"@types/unist" "*"
"@types/js-cookie@^2.2.6":
version "2.2.7"
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3"
integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==
"@types/lodash.mergewith@4.6.6":
version "4.6.6"
resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.6.tgz#c4698f5b214a433ff35cb2c75ee6ec7f99d79f10"
@ -2168,6 +2173,11 @@
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
"@xobotyi/scrollbar-width@^1.9.5":
version "1.9.5"
resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==
"@zag-js/element-size@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@zag-js/element-size/-/element-size-0.1.0.tgz#dfdb3f66a70328d0c3149aae29b8f99c10590c22"
@ -2415,6 +2425,13 @@ copy-to-clipboard@3.3.1:
dependencies:
toggle-selection "^1.0.6"
copy-to-clipboard@^3.3.1:
version "3.3.3"
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0"
integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==
dependencies:
toggle-selection "^1.0.6"
core-js-compat@^3.25.1:
version "3.26.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.1.tgz#0e710b09ebf689d719545ac36e49041850f943df"
@ -2445,7 +2462,22 @@ css-box-model@1.2.1:
dependencies:
tiny-invariant "^1.0.6"
csstype@^3.0.11, csstype@^3.0.2:
css-in-js-utils@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb"
integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==
dependencies:
hyphenate-style-name "^1.0.3"
css-tree@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
dependencies:
mdn-data "2.0.14"
source-map "^0.6.1"
csstype@^3.0.11, csstype@^3.0.2, csstype@^3.0.6:
version "3.1.1"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
@ -2511,6 +2543,13 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
error-stack-parser@^2.0.6:
version "2.1.4"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
dependencies:
stackframe "^1.3.4"
es-abstract@^1.19.0, es-abstract@^1.20.4:
version "1.20.5"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.5.tgz#e6dc99177be37cacda5988e692c3fa8b218e95d2"
@ -2614,7 +2653,7 @@ extend@^3.0.0:
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
fast-deep-equal@^3.1.1:
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@ -2635,6 +2674,21 @@ fast-json-stable-stringify@^2.1.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-loops@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75"
integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==
fast-shallow-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b"
integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==
fastest-stable-stringify@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76"
integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==
fastq@^1.6.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.14.0.tgz#107f69d7295b11e0fccc264e1fc6389f623731ce"
@ -2852,6 +2906,11 @@ hoist-non-react-statics@^3.3.1:
dependencies:
react-is "^16.7.0"
hyphenate-style-name@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
idb@^7.0.1, idb@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b"
@ -2883,6 +2942,14 @@ inline-style-parser@0.1.1:
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
inline-style-prefixer@^6.0.0:
version "6.0.4"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz#4290ed453ab0e4441583284ad86e41ad88384f44"
integrity sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==
dependencies:
css-in-js-utils "^3.1.0"
fast-loops "^1.1.3"
internal-slot@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3"
@ -3052,6 +3119,11 @@ jest-worker@^26.2.1:
merge-stream "^2.0.0"
supports-color "^7.0.0"
js-cookie@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -3204,6 +3276,11 @@ mdast-util-to-string@^3.1.0:
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9"
integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==
mdn-data@2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@ -3441,6 +3518,20 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nano-css@^5.3.1:
version "5.3.5"
resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.5.tgz#3075ea29ffdeb0c7cb6d25edb21d8f7fa8e8fe8e"
integrity sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==
dependencies:
css-tree "^1.1.2"
csstype "^3.0.6"
fastest-stable-stringify "^2.0.2"
inline-style-prefixer "^6.0.0"
rtl-css-js "^1.14.0"
sourcemap-codec "^1.4.8"
stacktrace-js "^2.0.2"
stylis "^4.0.6"
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
@ -3698,6 +3789,31 @@ react-style-singleton@^2.2.1:
invariant "^2.2.4"
tslib "^2.0.0"
react-universal-interface@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==
react-use@^17.4.0:
version "17.4.0"
resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.4.0.tgz#cefef258b0a6c534a5c8021c2528ac6e1a4cdc6d"
integrity sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==
dependencies:
"@types/js-cookie" "^2.2.6"
"@xobotyi/scrollbar-width" "^1.9.5"
copy-to-clipboard "^3.3.1"
fast-deep-equal "^3.1.3"
fast-shallow-equal "^1.0.0"
js-cookie "^2.2.1"
nano-css "^5.3.1"
react-universal-interface "^0.6.2"
resize-observer-polyfill "^1.5.1"
screenfull "^5.1.0"
set-harmonic-interval "^1.0.1"
throttle-debounce "^3.0.1"
ts-easing "^0.2.0"
tslib "^2.1.0"
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@ -3786,6 +3902,11 @@ require-from-string@^2.0.2:
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@ -3829,6 +3950,13 @@ rollup@^3.7.0, rollup@^3.7.2:
optionalDependencies:
fsevents "~2.3.2"
rtl-css-js@^1.14.0:
version "1.16.0"
resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.16.0.tgz#e8d682982441aadb63cabcb2f7385f3fb78ff26e"
integrity sha512-Oc7PnzwIEU4M0K1J4h/7qUUaljXhQ0kCObRsZjxs2HjkpKsnoTMvSmvJ4sqgJZd0zBoEfAyTdnK/jMIYvrjySQ==
dependencies:
"@babel/runtime" "^7.1.2"
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@ -3864,6 +3992,11 @@ scheduler@^0.23.0:
dependencies:
loose-envify "^1.1.0"
screenfull@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@ -3876,6 +4009,11 @@ serialize-javascript@^4.0.0:
dependencies:
randombytes "^2.1.0"
set-harmonic-interval@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249"
integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@ -3898,12 +4036,17 @@ source-map-support@~0.5.20:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map@0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==
source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
source-map@^0.6.0:
source-map@^0.6.0, source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@ -3925,6 +4068,35 @@ space-separated-tokens@^2.0.0:
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f"
integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
stack-generator@^2.0.5:
version "2.0.10"
resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d"
integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==
dependencies:
stackframe "^1.3.4"
stackframe@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
stacktrace-gps@^3.0.4:
version "3.1.2"
resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0"
integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==
dependencies:
source-map "0.5.6"
stackframe "^1.3.4"
stacktrace-js@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b"
integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==
dependencies:
error-stack-parser "^2.0.6"
stack-generator "^2.0.5"
stacktrace-gps "^3.0.4"
string.prototype.matchall@^4.0.6:
version "4.0.8"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3"
@ -3978,7 +4150,7 @@ style-to-object@^0.3.0:
dependencies:
inline-style-parser "0.1.1"
stylis@4.1.3:
stylis@4.1.3, stylis@^4.0.6:
version "4.1.3"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7"
integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==
@ -4027,6 +4199,11 @@ terser@^5.0.0:
commander "^2.20.0"
source-map-support "~0.5.20"
throttle-debounce@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
tiny-invariant@^1.0.6:
version "1.3.1"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
@ -4066,6 +4243,11 @@ trough@^2.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876"
integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==
ts-easing@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec"
integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==
tslib@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"