optimize bundle

This commit is contained in:
Kieran 2023-05-16 18:54:49 +01:00
parent 6ca55309e9
commit 09cde5ee86
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
27 changed files with 205 additions and 314 deletions

2
_headers Normal file
View File

@ -0,0 +1,2 @@
/*
Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src youtube.com www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://nostrnests.com https://embed.wavlake.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src *; img-src * data:; font-src https://fonts.gstatic.com; media-src *; script-src 'self' 'wasm-unsafe-eval' https://static.cloudflareinsights.com https://platform.twitter.com https://embed.tidal.com;

View File

@ -30,7 +30,6 @@
"react-intersection-observer": "^9.4.1",
"react-intl": "^6.2.8",
"react-markdown": "^8.0.4",
"react-query": "^3.39.2",
"react-redux": "^8.0.5",
"react-router-dom": "^6.5.0",
"react-textarea-autosize": "^8.4.0",

View File

@ -4,21 +4,17 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Fast nostr web ui" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src youtube.com www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://nostrnests.com https://embed.wavlake.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src *; img-src * data:; font-src https://fonts.gstatic.com; media-src *; script-src 'self' 'wasm-unsafe-eval' https://static.cloudflareinsights.com https://platform.twitter.com https://embed.tidal.com;" />
<meta name="description" content="Feature packed nostr client" />
<meta name="keywords" content="nostr snort fast decentralized social media censorship resistant open source software" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://imgproxy.snort.social" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="apple-touch-icon" href="/nostrich_512.png" />
<link rel="manifest" href="/manifest.json" />
<title>snort.social - Nostr interface</title>
<title>Snort - Nostr</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

View File

@ -2,13 +2,16 @@ import FeedCache from "Cache/FeedCache";
import { db } from "Db";
import { MetadataCache } from "Cache";
import { LNURL } from "LNURL";
import { fetchNip05Pubkey } from "Nip05/Verifier";
class UserProfileCache extends FeedCache<MetadataCache> {
#zapperQueue: Array<{ pubkey: string; lnurl: string }> = [];
#nip5Queue: Array<{ pubkey: string; nip05: string }> = [];
constructor() {
super("UserCache", db.users);
this.#processZapperQueue();
this.#processNip5Queue();
}
key(of: MetadataCache): string {
@ -80,6 +83,12 @@ class UserProfileCache extends FeedCache<MetadataCache> {
});
}
}
if (m.nip05) {
this.#nip5Queue.push({
pubkey: m.pubkey,
nip05: m.nip05,
});
}
}
return updateType;
}
@ -119,6 +128,29 @@ class UserProfileCache extends FeedCache<MetadataCache> {
setTimeout(() => this.#processZapperQueue(), 1_000);
}
async #processNip5Queue() {
while (this.#nip5Queue.length > 0) {
const i = this.#nip5Queue.shift();
if (i) {
try {
const [name, domain] = i.nip05.split("@");
const nip5pk = await fetchNip05Pubkey(name, domain);
const p = this.getFromCache(i.pubkey);
if (p) {
this.#setItem({
...p,
isNostrAddressValid: i.pubkey === nip5pk,
});
}
} catch {
console.warn("Failed to load nip-05", i.nip05);
}
}
}
setTimeout(() => this.#processNip5Queue(), 1_000);
}
}
export const UserCache = new UserProfileCache();

View File

@ -29,6 +29,11 @@ export interface MetadataCache extends UserMetadata {
* Pubkey of zapper service
*/
zapService?: HexKey;
/**
* If the nip05 is valid for this user
*/
isNostrAddressValid: boolean;
}
export function mapEventToProfile(ev: RawEvent) {

View File

@ -1,10 +1,19 @@
import { getDecodedToken } from "@cashu/cashu-ts";
import { useMemo } from "react";
import { useEffect, useState } from "react";
import { FormattedMessage } from "react-intl";
import useLogin from "Hooks/useLogin";
import { useUserProfile } from "Hooks/useUserProfile";
interface Token {
token: Array<{
mint: string;
proofs: Array<{
amount: number;
}>;
}>;
memo?: string;
}
export default function CashuNuts({ token }: { token: string }) {
const login = useLogin();
const profile = useUserProfile(login.publicKey);
@ -22,12 +31,16 @@ export default function CashuNuts({ token }: { token: string }) {
window.open(url, "_blank");
}
const cashu = useMemo(() => {
const [cashu, setCashu] = useState<Token>();
useEffect(() => {
try {
if (!token.startsWith("cashuA") || token.length < 10) {
return;
}
return getDecodedToken(token);
import("@cashu/cashu-ts").then(({ getDecodedToken }) => {
const tkn = getDecodedToken(token);
setCashu(tkn);
});
} catch {
// ignored
}

View File

@ -1,59 +1,12 @@
import "./Nip05.css";
import { useQuery } from "react-query";
import { HexKey } from "@snort/nostr";
import DnsOverHttpResolver from "dns-over-http-resolver";
import Icon from "Icons/Icon";
import { bech32ToHex } from "Util";
import { useUserProfile } from "Hooks/useUserProfile";
interface NostrJson {
names: Record<string, string>;
}
const resolver = new DnsOverHttpResolver();
async function fetchNip05Pubkey(name: string, domain: string) {
if (!name || !domain) {
return undefined;
}
try {
const res = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(name)}`);
const data: NostrJson = await res.json();
const match = Object.keys(data.names).find(n => {
return n.toLowerCase() === name.toLowerCase();
});
return match ? data.names[match] : undefined;
} catch {
// ignored
}
// Check as DoH TXT entry
try {
const resDns = await resolver.resolveTxt(`${name}._nostr.${domain}`);
return bech32ToHex(resDns[0][0]);
} catch {
// ignored
}
return undefined;
}
const VERIFICATION_CACHE_TIME = 24 * 60 * 60 * 1000;
const VERIFICATION_STALE_TIMEOUT = 10 * 60 * 1000;
export function useIsVerified(pubkey: HexKey, nip05?: string, bypassCheck?: boolean) {
const [name, domain] = nip05 ? nip05.split("@") : [];
const { isError, isSuccess, data } = useQuery(
["nip05", nip05],
() => (bypassCheck ? Promise.resolve(pubkey) : fetchNip05Pubkey(name, domain)),
{
retry: false,
retryOnMount: false,
cacheTime: VERIFICATION_CACHE_TIME,
staleTime: VERIFICATION_STALE_TIMEOUT,
}
);
const isVerified = isSuccess && data === pubkey;
const cantVerify = isSuccess && data !== pubkey;
return { isVerified, couldNotVerify: isError || cantVerify };
export function useIsVerified(pubkey: HexKey, bypassCheck?: boolean) {
const profile = useUserProfile(pubkey);
return { isVerified: bypassCheck || profile?.isNostrAddressValid };
}
export interface Nip05Params {
@ -65,10 +18,10 @@ export interface Nip05Params {
const Nip05 = ({ nip05, pubkey, verifyNip = true }: Nip05Params) => {
const [name, domain] = nip05 ? nip05.split("@") : [];
const isDefaultUser = name === "_";
const { isVerified, couldNotVerify } = useIsVerified(pubkey, nip05, !verifyNip);
const { isVerified } = useIsVerified(pubkey, !verifyNip);
return (
<div className={`flex nip05${couldNotVerify ? " failed" : ""}`}>
<div className={`flex nip05${!isVerified ? " failed" : ""}`}>
{!isDefaultUser && isVerified && <span className="nick">{`${name}@`}</span>}
{isVerified && (
<>

View File

@ -180,6 +180,8 @@
.note .poll-body > div > .progress {
background-color: var(--gray);
height: stretch;
height: -webkit-fill-available;
height: -moz-available;
position: absolute;
z-index: 1;
}

View File

@ -18,8 +18,9 @@
background-color: var(--note-bg);
border-radius: 10px 10px 0 0;
min-height: 100px;
max-width: stretch;
min-width: stretch;
width: stretch;
width: -webkit-fill-available;
width: -moz-available;
max-height: 210px;
}
@ -57,6 +58,8 @@
display: flex;
justify-content: flex-end;
width: stretch;
width: -webkit-fill-available;
width: -moz-available;
}
.note-creator .insert > button {

View File

@ -3,20 +3,19 @@
align-items: center;
}
.note-to-self {
margin-left: 5px;
margin-top: 3px;
}
.nts .avatar-wrapper {
margin-right: 8px;
}
.nts .avatar {
border-width: 1px;
width: 40px;
height: 40px;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
}
.nts .avatar.clickable {
cursor: pointer;
}

View File

@ -16,7 +16,7 @@ export interface NoteToSelfProps {
function NoteLabel() {
return (
<div>
<FormattedMessage {...messages.NoteToSelf} /> <Icon name="book-closed" />
<FormattedMessage {...messages.NoteToSelf} /> <Icon name="badge" size={15} />
</div>
);
}
@ -34,7 +34,7 @@ export default function NoteToSelf({ pubkey, clickable, className, link }: NoteT
<div className={`nts${className ? ` ${className}` : ""}`}>
<div className="avatar-wrapper">
<div className={`avatar${clickable ? " clickable" : ""}`}>
<Icon onClick={clickLink} className="note-to-self" name="book-closed" size={48} />
<Icon onClick={clickLink} name="book-closed" size={20} />
</div>
</div>
<div className="f-grow">

View File

@ -31,6 +31,8 @@ a.pfp {
.pfp .profile-name {
max-width: stretch;
max-width: -webkit-fill-available;
max-width: -moz-available;
}
.pfp a {

View File

@ -0,0 +1,32 @@
import DnsOverHttpResolver from "dns-over-http-resolver";
import { bech32ToHex } from "Util";
const resolver = new DnsOverHttpResolver();
interface NostrJson {
names: Record<string, string>;
}
export async function fetchNip05Pubkey(name: string, domain: string) {
if (!name || !domain) {
return undefined;
}
try {
const res = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(name)}`);
const data: NostrJson = await res.json();
const match = Object.keys(data.names).find(n => {
return n.toLowerCase() === name.toLowerCase();
});
return match ? data.names[match] : undefined;
} catch {
// ignored
}
// Check as DoH TXT entry
try {
const resDns = await resolver.resolveTxt(`${name}._nostr.${domain}`);
return bech32ToHex(resDns[0][0]);
} catch {
// ignored
}
return undefined;
}

View File

@ -62,11 +62,11 @@ export default function WalletPage() {
function stateIcon(s: WalletInvoiceState) {
switch (s) {
case WalletInvoiceState.Pending:
return <Icon name="clock" className="mr5" />;
return <Icon name="clock" className="mr5" size={15} />;
case WalletInvoiceState.Paid:
return <Icon name="check" className="mr5" />;
return <Icon name="check" className="mr5" size={15} />;
case WalletInvoiceState.Expired:
return <Icon name="close" className="mr5" />;
return <Icon name="close" className="mr5" size={15} />;
}
}

View File

@ -64,13 +64,14 @@ export default function ProfileSettings(props: ProfileSettingsProps) {
website,
nip05,
lud16,
} as Record<string, string | number | undefined>;
} as Record<string, string | number | undefined | boolean>;
delete userCopy["loaded"];
delete userCopy["created"];
delete userCopy["pubkey"];
delete userCopy["npub"];
delete userCopy["deleted"];
delete userCopy["zapService"];
delete userCopy["isNostrAddressValid"];
console.debug(userCopy);
if (publisher) {

View File

@ -4,7 +4,6 @@ import { v4 as uuid } from "uuid";
import AsyncButton from "Element/AsyncButton";
import { unwrap } from "Util";
import { CashuWallet } from "Wallet/Cashu";
import { WalletConfig, WalletKind, Wallets } from "Wallet";
import { useNavigate } from "react-router-dom";
@ -19,6 +18,8 @@ const ConnectCashu = () => {
if (!mintUrl) {
throw new Error("Mint URL is required");
}
const { CashuWallet } = await import("Wallet/Cashu");
const connection = new CashuWallet(config);
await connection.login();
const info = await connection.getInfo();

View File

@ -4,8 +4,7 @@ import { useNavigate } from "react-router-dom";
import { v4 as uuid } from "uuid";
import AsyncButton from "Element/AsyncButton";
import { LNCWallet } from "Wallet/LNCWallet";
import { WalletInfo, WalletKind, Wallets } from "Wallet";
import { LNWallet, WalletInfo, WalletKind, Wallets } from "Wallet";
import { unwrap } from "Util";
const ConnectLNC = () => {
@ -13,12 +12,13 @@ const ConnectLNC = () => {
const navigate = useNavigate();
const [pairingPhrase, setPairingPhrase] = useState<string>();
const [error, setError] = useState<string>();
const [connectedLNC, setConnectedLNC] = useState<LNCWallet>();
const [connectedLNC, setConnectedLNC] = useState<LNWallet & { setPassword(pw: string): void }>();
const [walletInfo, setWalletInfo] = useState<WalletInfo>();
const [walletPassword, setWalletPassword] = useState<string>();
async function tryConnect(cfg: string) {
try {
const { LNCWallet } = await import("Wallet/LNCWallet");
const lnc = await LNCWallet.Initialize(cfg);
const info = await lnc.getInfo();

View File

@ -1,4 +1,4 @@
import { LNWallet, Sats, WalletError, WalletErrorCode, WalletInfo, WalletInvoice } from "Wallet";
import { InvoiceRequest, LNWallet, Sats, WalletError, WalletErrorCode, WalletInfo, WalletInvoice } from "Wallet";
import { CashuMint, CashuWallet as TheCashuWallet, Proof } from "@cashu/cashu-ts";

View File

@ -1,10 +1,8 @@
import { useSyncExternalStore } from "react";
import { decodeInvoice, unwrap } from "Util";
import { LNCWallet } from "./LNCWallet";
import LNDHubWallet from "./LNDHub";
import { NostrConnectWallet } from "./NostrWalletConnect";
import { CashuWallet } from "./Cashu";
import { setupWebLNWalletConfig, WebLNWallet } from "./WebLN";
export enum WalletKind {
@ -171,7 +169,13 @@ export class WalletStore {
} else {
const w = this.#activateWallet(activeConfig);
if (w) {
this.#instance.set(activeConfig.id, w);
if ("then" in w) {
w.then(wx => {
this.#instance.set(activeConfig.id, wx);
this.snapshotState();
});
return undefined;
}
return w;
} else {
throw new Error("Unable to activate wallet config");
@ -238,11 +242,10 @@ export class WalletStore {
}
}
#activateWallet(cfg: WalletConfig): LNWallet | undefined {
#activateWallet(cfg: WalletConfig): LNWallet | Promise<LNWallet> | undefined {
switch (cfg.kind) {
case WalletKind.LNC: {
const w = LNCWallet.Empty();
return w;
return import("./LNCWallet").then(({ LNCWallet }) => LNCWallet.Empty());
}
case WalletKind.WebLN: {
return new WebLNWallet();
@ -254,7 +257,7 @@ export class WalletStore {
return new NostrConnectWallet(unwrap(cfg.data));
}
case WalletKind.Cashu: {
return new CashuWallet(unwrap(cfg.data));
return import("./Cashu").then(({ CashuWallet }) => new CashuWallet(unwrap(cfg.data)));
}
}
}

View File

@ -366,11 +366,15 @@ input:disabled {
.w-max {
width: 100%;
width: stretch;
width: -webkit-fill-available;
width: -moz-available;
}
.w-max-w {
max-width: 100%;
max-width: stretch;
max-width: -webkit-fill-available;
max-width: -moz-available;
}
a {

View File

@ -3,7 +3,6 @@ import "@szhsin/react-menu/dist/index.css";
import "public/manifest.json";
import { StrictMode } from "react";
import { QueryClient, QueryClientProvider } from "react-query";
import * as ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
@ -33,10 +32,8 @@ import Thread from "Element/Thread";
import { SubscribeRoutes } from "Pages/subscribe";
import ZapPoolPage from "Pages/ZapPool";
/**
* HTTP query provider
*/
const HTTP = new QueryClient();
// @ts-ignore
window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE=";
serviceWorkerRegistration.register();
@ -114,11 +111,9 @@ const root = ReactDOM.createRoot(unwrap(document.getElementById("root")));
root.render(
<StrictMode>
<Provider store={Store}>
<QueryClientProvider client={HTTP}>
<IntlProvider>
<RouterProvider router={router} />
</IntlProvider>
</QueryClientProvider>
<IntlProvider>
<RouterProvider router={router} />
</IntlProvider>
</Provider>
</StrictMode>
);

View File

@ -1,4 +1,6 @@
/* eslint-disable no-restricted-globals */
/// <reference lib="webworker" />
import {} from ".";
declare var self: ServiceWorkerGlobalScope;
import { clientsClaim } from "workbox-core";
import { ExpirationPlugin } from "workbox-expiration";

View File

@ -1,134 +0,0 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://cra.link/PWA
const isLocalhost = Boolean(
window.location.hostname === "localhost" ||
// [::1] is the IPv6 localhost address.
window.location.hostname === "[::1]" ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
);
export function register(config) {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener("load", () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
"This web app is being served cache-first by a service " +
"worker. To learn more, visit https://cra.link/PWA"
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
"New content is available and will be used when all " +
"tabs for this page are closed. See https://cra.link/PWA."
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log("Content is cached for offline use.");
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error("Error during service worker registration:", error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { "Service-Worker": "script" },
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get("content-type");
if (response.status === 404 || (contentType != null && contentType.indexOf("javascript") === -1)) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log("No internet connection found. App is running in offline mode.");
});
}
export function unregister() {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready
.then(registration => {
registration.unregister();
})
.catch(error => {
console.error(error.message);
});
}
}

View File

@ -0,0 +1,37 @@
export function register() {
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
window.addEventListener("load", () => {
registerValidSW("/service-worker.js");
});
}
}
async function registerValidSW(swUrl: string) {
try {
const registration = await navigator.serviceWorker.register(swUrl);
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
console.log("Service worker updated, pending reload");
} else {
console.log("Content is cached for offline use.");
}
}
};
};
} catch (e) {
console.error("Error during service worker registration:", e);
}
}
export async function unregister() {
if ("serviceWorker" in navigator) {
const registration = await navigator.serviceWorker.ready;
await registration.unregister();
}
}

View File

@ -2,6 +2,7 @@
"compilerOptions": {
"baseUrl": "src",
"target": "es2020",
"module": "es2020",
"jsx": "react-jsx",
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,

View File

@ -11,14 +11,21 @@ const TsTransformer = require("@formatjs/ts-transformer");
const isProduction = process.env.NODE_ENV == "production";
const config = {
entry: "./src/index.tsx",
entry: {
main: "./src/index.tsx",
},
target: "browserslist",
devtool: "source-map",
devtool: isProduction ? "source-map" : "eval",
output: {
publicPath: "/",
path: path.resolve(__dirname, "build"),
filename: "[name].[chunkhash].js",
clean: true,
filename: ({ runtime }) => {
if (runtime === "sw") {
return "[name].js";
}
return isProduction ? "[name].[chunkhash].js" : "[name].js";
},
clean: isProduction,
},
devServer: {
open: true,
@ -32,7 +39,7 @@ const config = {
}),
new ESLintPlugin(),
new MiniCssExtractPlugin({
filename: "[name].[chunkhash].css",
filename: isProduction ? "[name].[chunkhash].css" : "[name].css",
}),
],
module: {
@ -70,6 +77,7 @@ const config = {
},
optimization: {
usedExports: true,
chunkIds: "deterministic",
minimizer: ["...", new CssMinimizerPlugin()],
},
resolve: {
@ -81,11 +89,12 @@ const config = {
module.exports = () => {
if (isProduction) {
config.mode = "production";
config.plugins.push(new WorkboxWebpackPlugin.GenerateSW());
config.entry.sw = {
import: "./src/service-worker.ts",
name: "sw.js",
};
} else {
config.mode = "development";
config.output.clean = false;
}
return config;
};

View File

@ -1169,7 +1169,7 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.20.13", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.20.13", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.21.0"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz"
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
@ -3280,11 +3280,6 @@ before-after-hook@^2.2.0:
resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz"
integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==
big-integer@^1.6.16:
version "1.6.51"
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz"
integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
bin-links@^3.0.0:
version "3.0.3"
resolved "https://registry.npmjs.org/bin-links/-/bin-links-3.0.3.tgz"
@ -3376,20 +3371,6 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
broadcast-channel@^3.4.1:
version "3.7.0"
resolved "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz"
integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
dependencies:
"@babel/runtime" "^7.7.2"
detect-node "^2.1.0"
js-sha3 "0.8.0"
microseconds "0.2.0"
nano-time "1.0.0"
oblivious-set "1.0.0"
rimraf "3.0.2"
unload "2.2.0"
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz"
@ -4208,7 +4189,7 @@ destroy@1.2.0:
resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
detect-node@^2.0.4, detect-node@^2.1.0:
detect-node@^2.0.4:
version "2.1.0"
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
@ -6003,11 +5984,6 @@ js-sdsl@^4.1.4:
resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz"
integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==
js-sha3@0.8.0:
version "0.8.0"
resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
@ -6403,14 +6379,6 @@ match-sorter@4.0.0:
dependencies:
remove-accents "0.4.2"
match-sorter@^6.0.2:
version "6.3.1"
resolved "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz"
integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
dependencies:
"@babel/runtime" "^7.12.5"
remove-accents "0.4.2"
mdast-util-definitions@^5.0.0:
version "5.1.2"
resolved "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz"
@ -6730,11 +6698,6 @@ micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.2"
picomatch "^2.3.1"
microseconds@0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz"
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
@ -6982,13 +6945,6 @@ mute-stream@0.0.8:
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
nano-time@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz"
integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==
dependencies:
big-integer "^1.6.16"
nanoid@3.3.3:
version "3.3.3"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz"
@ -7218,11 +7174,6 @@ object.assign@^4.1.4:
has-symbols "^1.0.3"
object-keys "^1.1.1"
oblivious-set@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz"
integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
obuf@^1.0.0, obuf@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz"
@ -8008,15 +7959,6 @@ react-markdown@^8.0.4:
unist-util-visit "^4.0.0"
vfile "^5.0.0"
react-query@^3.39.2:
version "3.39.3"
resolved "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz"
integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==
dependencies:
"@babel/runtime" "^7.5.5"
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
react-redux@^8.0.5:
version "8.0.5"
resolved "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz"
@ -8352,7 +8294,7 @@ rfdc@^1.3.0:
resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz"
integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@ -9501,14 +9443,6 @@ universalify@^2.0.0:
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unload@2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz"
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
dependencies:
"@babel/runtime" "^7.6.2"
detect-node "^2.0.4"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"