1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-04-26 05:45:22 -04:00

more error handling

This commit is contained in:
Jake Jarvis 2025-03-31 09:15:40 -04:00
parent 50c184fb21
commit ec7c9fae54
Signed by: jake
SSH Key Fingerprint: SHA256:nCkvAjYA6XaSPUqc4TfbBQTpzr8Xj7ritg/sGInCdkc
15 changed files with 114 additions and 143 deletions

View File

@ -7,10 +7,35 @@ import * as Sentry from "@sentry/nextjs";
import * as config from "../../lib/config"; import * as config from "../../lib/config";
const ContactSchema = v.object({ const ContactSchema = v.object({
name: v.pipe(v.string(), v.nonEmpty("Your name is required.")), // TODO: replace duplicate error messages with v.message() when released. see:
email: v.pipe(v.string(), v.nonEmpty("Your email address is required."), v.email("Invalid email address.")), // https://valibot.dev/api/message/
message: v.pipe(v.string(), v.nonEmpty("A message is required.")), // https://github.com/fabian-hiller/valibot/blob/main/library/src/methods/message/message.ts
"cf-turnstile-response": v.pipe(v.string(), v.nonEmpty("Just do the stinkin CAPTCHA! 🤖")), name: v.pipe(v.string("Your name is required."), v.trim(), v.nonEmpty("Your name is required.")),
email: v.pipe(
v.string("Your email address is required."),
v.trim(),
v.nonEmpty("Your email address is required."),
v.email("Invalid email address.")
),
message: v.pipe(
v.string("A message is required."),
v.trim(),
v.nonEmpty("A message is required."),
v.minLength(10, "Your message must be at least 10 characters.")
),
"cf-turnstile-response": v.pipe(
// token wasn't submitted at _all_, most likely a direct POST request by a spam bot
v.string("Shoo, bot."),
// form submitted properly but token was missing, might be a forgetful human
v.nonEmpty("Just do the stinkin CAPTCHA, human! 🤖"),
// very rudimentary length check based on Cloudflare's docs
// https://developers.cloudflare.com/turnstile/troubleshooting/testing/
v.minLength("XXXX.DUMMY.TOKEN.XXXX".length),
// "A Turnstile token can have up to 2048 characters."
// https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
v.maxLength(2048),
v.readonly()
),
}); });
export type ContactInput = v.InferInput<typeof ContactSchema>; export type ContactInput = v.InferInput<typeof ContactSchema>;

View File

@ -28,7 +28,7 @@ const Page = () => {
</p> </p>
<p> <p>
🔐 You can grab my public key here:{" "} 🔐 You can grab my public key here:{" "}
<Link href="/pubkey.asc" title="My Public PGP Key" rel="pgpkey authn" openInNewTab> <Link href="https://jrvs.io/pgp" title="My Public Key">
<code style={{ fontSize: "0.925em", letterSpacing: "0.075em", wordSpacing: "-0.3em" }}> <code style={{ fontSize: "0.925em", letterSpacing: "0.075em", wordSpacing: "-0.3em" }}>
6BF3 79D3 6F67 1480 2B0C 9CF2 51E6 9A39 6BF3 79D3 6F67 1480 2B0C 9CF2 51E6 9A39
</code> </code>

View File

@ -23,6 +23,8 @@ const HitCounter = async ({ slug }: { slug: string }) => {
); );
} catch (error) { } catch (error) {
Sentry.captureException(error); Sentry.captureException(error);
return <span title="Error getting views! :(">?</span>;
} }
}; };

View File

@ -258,13 +258,12 @@ const Page = () => {
</Link>{" "} </Link>{" "}
<sup> <sup>
<Link <Link
href="/pubkey.asc" href="https://jrvs.io/pgp"
rel="pgpkey authn" rel="pgpkey"
title="My Public Key" title="My Public Key"
lightColor="#757575" lightColor="#757575"
darkColor="#959595" darkColor="#959595"
plain plain
openInNewTab
> >
<LockIcon size="1.25em" style={{ verticalAlign: "-0.25em" }} />{" "} <LockIcon size="1.25em" style={{ verticalAlign: "-0.25em" }} />{" "}
<span <span

View File

@ -10,8 +10,6 @@ import type { User, Repository } from "@octokit/graphql-schema";
import styles from "./page.module.css"; import styles from "./page.module.css";
export const revalidate = 600; // 10 minutes
export const metadata = addMetadata({ export const metadata = addMetadata({
title: "Projects", title: "Projects",
description: `Most-starred repositories by @${config.authorSocial?.github} on GitHub`, description: `Most-starred repositories by @${config.authorSocial?.github} on GitHub`,
@ -80,6 +78,20 @@ const getRepos = async (): Promise<Project[] | null> => {
accept: "application/vnd.github.v3+json", accept: "application/vnd.github.v3+json",
authorization: `token ${process.env.GITHUB_TOKEN}`, authorization: `token ${process.env.GITHUB_TOKEN}`,
}, },
request: {
// override fetch() to use next's extension to cache the response
// https://nextjs.org/docs/app/api-reference/functions/fetch#fetchurl-options
fetch: (url: string | URL | Request, options?: RequestInit) => {
return fetch(url, {
...options,
cache: "force-cache",
next: {
// 10 minutes
revalidate: 600,
},
});
},
},
} }
); );

View File

@ -7,7 +7,7 @@ const robots = (): MetadataRoute.Robots => ({
rules: [ rules: [
{ {
userAgent: "*", userAgent: "*",
disallow: ["/_stream/", "/api/", "/pubkey.asc", "/404", "/500"], disallow: ["/_stream/", "/api/", "/404", "/500"],
}, },
], ],
sitemap: `${BASE_URL}/sitemap.xml`, sitemap: `${BASE_URL}/sitemap.xml`,

View File

@ -9,7 +9,13 @@ const Gist = async ({ id, file }: GistProps) => {
const iframeId = `gist-${id}${file ? `-${file}` : ""}`; const iframeId = `gist-${id}${file ? `-${file}` : ""}`;
const scriptUrl = `https://gist.github.com/${id}.js${file ? `?file=${file}` : ""}`; const scriptUrl = `https://gist.github.com/${id}.js${file ? `?file=${file}` : ""}`;
const scriptResponse = await fetch(scriptUrl); const scriptResponse = await fetch(scriptUrl, {
cache: "force-cache",
next: {
// cache indefinitely in data store
revalidate: 0,
},
});
if (!scriptResponse.ok) { if (!scriptResponse.ok) {
console.warn(`[gist] failed to fetch js:`, scriptResponse.statusText); console.warn(`[gist] failed to fetch js:`, scriptResponse.statusText);

View File

@ -1,4 +0,0 @@
/* stylelint-disable-next-line selector-type-no-unknown */
.wrapper lite-youtube {
margin: 0 auto;
}

View File

@ -1,17 +1,14 @@
import { YouTubeEmbed } from "@next/third-parties/google"; "use client";
import styles from "./YouTube.module.css"; import YouTubeEmbed from "react-lite-youtube-embed";
import type { ComponentPropsWithoutRef } from "react";
export type YouTubeProps = { import "react-lite-youtube-embed/dist/LiteYouTubeEmbed.css";
id: string;
};
const YouTube = ({ id }: YouTubeProps) => { export type YouTubeProps = Omit<ComponentPropsWithoutRef<typeof YouTubeEmbed>, "title">;
return (
<div className={styles.wrapper}> const YouTube = ({ ...rest }: YouTubeProps) => {
<YouTubeEmbed videoid={id} /> return <YouTubeEmbed cookie={false} containerElement="div" title="" {...rest} />;
</div>
);
}; };
export default YouTube; export default YouTube;

View File

@ -2,7 +2,6 @@ import * as Sentry from "@sentry/nextjs";
Sentry.init({ Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
integrations: [Sentry.browserTracingIntegration(), Sentry.httpClientIntegration()], integrations: [Sentry.browserTracingIntegration(), Sentry.httpClientIntegration()],
tracesSampleRate: 1.0, tracesSampleRate: 1.0,
}); });

View File

@ -5,7 +5,6 @@ export const onRequestError = Sentry.captureRequestError;
export const register = () => { export const register = () => {
Sentry.init({ Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN, dsn: process.env.NEXT_PUBLIC_SENTRY_DSN || process.env.SENTRY_DSN,
environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
integrations: [Sentry.captureConsoleIntegration()], integrations: [Sentry.captureConsoleIntegration()],
tracesSampleRate: 1.0, tracesSampleRate: 1.0,
// https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#normalizeDepth // https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#normalizeDepth

View File

@ -58,7 +58,23 @@ const nextConfig: NextConfig = {
}, },
headers: async () => [ headers: async () => [
{ {
// matches any path without a file extension (aka period) or an underscore (e.g. /_next/image) // matches any path
source: "/(.*)",
headers: [
{
key: "strict-transport-security",
value: "max-age=63072000; includeSubDomains; preload",
},
{
key: "x-got-milk",
value: "2%",
},
],
},
{
// https://community.torproject.org/onion-services/advanced/onion-location/
// only needed on actual pages, not static assets, so make a best effort by matching any path **without** a file
// extension (aka a period) and/or an underscore (e.g. /_next/image).
source: "/:path([^._]*)", source: "/:path([^._]*)",
headers: [ headers: [
{ {
@ -67,15 +83,6 @@ const nextConfig: NextConfig = {
}, },
], ],
}, },
{
source: "/pubkey.asc",
headers: [
{
key: "Content-Type",
value: "text/plain; charset=utf-8",
},
],
},
], ],
rewrites: async () => [ rewrites: async () => [
{ {
@ -88,6 +95,11 @@ const nextConfig: NextConfig = {
source: "/tweets/:path*", source: "/tweets/:path*",
destination: "https://tweets-khaki.vercel.app/:path*", destination: "https://tweets-khaki.vercel.app/:path*",
}, },
{
source: "/pubkey.asc",
destination:
"https://keys.openpgp.org/pks/lookup?op=get&options=mr&search=0x3bc6e5776bf379d36f6714802b0c9cf251e69a39",
},
], ],
redirects: async () => [ redirects: async () => [
{ source: "/y2k", destination: "https://y2k.pages.dev", permanent: false }, { source: "/y2k", destination: "https://y2k.pages.dev", permanent: false },
@ -231,10 +243,4 @@ const nextPlugins: Array<
// eslint-disable-next-line import/no-anonymous-default-export // eslint-disable-next-line import/no-anonymous-default-export
export default (): NextConfig => export default (): NextConfig =>
nextPlugins.reduce((acc, next) => { nextPlugins.reduce((acc, plugin) => (Array.isArray(plugin) ? plugin[0](acc, plugin[1]) : plugin(acc)), nextConfig);
if (Array.isArray(next)) {
return next[0](acc, next[1]);
}
return next(acc);
}, nextConfig);

View File

@ -25,7 +25,6 @@
"@mdx-js/react": "^3.1.0", "@mdx-js/react": "^3.1.0",
"@next/bundle-analyzer": "15.3.0-canary.25", "@next/bundle-analyzer": "15.3.0-canary.25",
"@next/mdx": "15.3.0-canary.25", "@next/mdx": "15.3.0-canary.25",
"@next/third-parties": "15.3.0-canary.25",
"@octokit/graphql": "^8.2.1", "@octokit/graphql": "^8.2.1",
"@octokit/graphql-schema": "^15.26.0", "@octokit/graphql-schema": "^15.26.0",
"@sentry/nextjs": "^9.10.1", "@sentry/nextjs": "^9.10.1",
@ -37,7 +36,7 @@
"fast-glob": "^3.3.3", "fast-glob": "^3.3.3",
"feed": "^4.2.2", "feed": "^4.2.2",
"geist": "^1.3.1", "geist": "^1.3.1",
"html-entities": "^2.5.5", "html-entities": "^2.6.0",
"lucide-react": "0.485.0", "lucide-react": "0.485.0",
"modern-normalize": "^3.0.1", "modern-normalize": "^3.0.1",
"next": "15.3.0-canary.25", "next": "15.3.0-canary.25",
@ -49,8 +48,9 @@
"react-dom": "19.1.0", "react-dom": "19.1.0",
"react-innertext": "^1.1.5", "react-innertext": "^1.1.5",
"react-is": "19.1.0", "react-is": "19.1.0",
"react-lite-youtube-embed": "^2.4.0",
"react-schemaorg": "^2.0.0", "react-schemaorg": "^2.0.0",
"react-textarea-autosize": "^8.5.8", "react-textarea-autosize": "^8.5.9",
"react-timeago": "^8.0.0", "react-timeago": "^8.0.0",
"react-turnstile": "^1.1.4", "react-turnstile": "^1.1.4",
"react-tweet": "^3.2.2", "react-tweet": "^3.2.2",

54
pnpm-lock.yaml generated
View File

@ -32,9 +32,6 @@ importers:
'@next/mdx': '@next/mdx':
specifier: 15.3.0-canary.25 specifier: 15.3.0-canary.25
version: 15.3.0-canary.25(@mdx-js/loader@3.1.0(acorn@8.14.1)(webpack@5.98.0))(@mdx-js/react@3.1.0(@types/react@19.0.12)(react@19.1.0)) version: 15.3.0-canary.25(@mdx-js/loader@3.1.0(acorn@8.14.1)(webpack@5.98.0))(@mdx-js/react@3.1.0(@types/react@19.0.12)(react@19.1.0))
'@next/third-parties':
specifier: 15.3.0-canary.25
version: 15.3.0-canary.25(next@15.3.0-canary.25(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.0.0-beta-aeaed83-20250323)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
'@octokit/graphql': '@octokit/graphql':
specifier: ^8.2.1 specifier: ^8.2.1
version: 8.2.1 version: 8.2.1
@ -69,8 +66,8 @@ importers:
specifier: ^1.3.1 specifier: ^1.3.1
version: 1.3.1(next@15.3.0-canary.25(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.0.0-beta-aeaed83-20250323)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) version: 1.3.1(next@15.3.0-canary.25(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.0.0-beta-aeaed83-20250323)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
html-entities: html-entities:
specifier: ^2.5.5 specifier: ^2.6.0
version: 2.5.5 version: 2.6.0
lucide-react: lucide-react:
specifier: 0.485.0 specifier: 0.485.0
version: 0.485.0(react@19.1.0) version: 0.485.0(react@19.1.0)
@ -104,12 +101,15 @@ importers:
react-is: react-is:
specifier: 19.1.0 specifier: 19.1.0
version: 19.1.0 version: 19.1.0
react-lite-youtube-embed:
specifier: ^2.4.0
version: 2.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-schemaorg: react-schemaorg:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0(react@19.1.0)(schema-dts@1.1.5)(typescript@5.8.2) version: 2.0.0(react@19.1.0)(schema-dts@1.1.5)(typescript@5.8.2)
react-textarea-autosize: react-textarea-autosize:
specifier: ^8.5.8 specifier: ^8.5.9
version: 8.5.8(@types/react@19.0.12)(react@19.1.0) version: 8.5.9(@types/react@19.0.12)(react@19.1.0)
react-timeago: react-timeago:
specifier: ^8.0.0 specifier: ^8.0.0
version: 8.0.0(react@19.1.0) version: 8.0.0(react@19.1.0)
@ -728,12 +728,6 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@next/third-parties@15.3.0-canary.25':
resolution: {integrity: sha512-UiwiSeKKyBW31YZ6v4QEb9+Sx22ubWcf/74Jv1dIQnXrKLMd3OTNj9r0Z3Bj5sIpuBx0NIf8LCr90H2sJRRYDw==}
peerDependencies:
next: ^13.0.0 || ^14.0.0 || ^15.0.0
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -2687,8 +2681,8 @@ packages:
resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
engines: {node: ^16.14.0 || >=18.0.0} engines: {node: ^16.14.0 || >=18.0.0}
html-entities@2.5.5: html-entities@2.6.0:
resolution: {integrity: sha512-24CG9o869vSa86BGCf7x65slrAztzFTU5VBQzEIwqjhKuB4zCC7xlH/7NCcZ1EN5MdmGx9lUqugfutuT6J+jKQ==} resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
html-escaper@2.0.2: html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@ -3702,6 +3696,12 @@ packages:
react-is@19.1.0: react-is@19.1.0:
resolution: {integrity: sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==} resolution: {integrity: sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==}
react-lite-youtube-embed@2.4.0:
resolution: {integrity: sha512-Xo6cM1zPlROvvM97JkqQIoXstlQDaC4+DawmM7BB7Hh1cXrkBHEGq1iJlQxBTUWAUklmpcC7ph7qg7CztXtABQ==}
peerDependencies:
react: '>=18.2.0'
react-dom: '>=18.2.0'
react-promise-suspense@0.3.4: react-promise-suspense@0.3.4:
resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==}
@ -3713,8 +3713,8 @@ packages:
schema-dts: '>=0.7.4' schema-dts: '>=0.7.4'
typescript: '>=3.1.6' typescript: '>=3.1.6'
react-textarea-autosize@8.5.8: react-textarea-autosize@8.5.9:
resolution: {integrity: sha512-iUiIj70JefrTuSJ4LbVFiSqWiHHss5L63L717bqaWHMgkm9sz6eEvro4vZ3uQfGJbevzwT6rHOszHKA8RkhRMg==} resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==}
engines: {node: '>=10'} engines: {node: '>=10'}
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
@ -4246,9 +4246,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
third-party-capital@1.0.20:
resolution: {integrity: sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==}
tinyglobby@0.2.12: tinyglobby@0.2.12:
resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
@ -5081,12 +5078,6 @@ snapshots:
'@next/swc-win32-x64-msvc@15.3.0-canary.25': '@next/swc-win32-x64-msvc@15.3.0-canary.25':
optional: true optional: true
'@next/third-parties@15.3.0-canary.25(next@15.3.0-canary.25(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.0.0-beta-aeaed83-20250323)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
dependencies:
next: 15.3.0-canary.25(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.0.0-beta-aeaed83-20250323)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
third-party-capital: 1.0.20
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
'@nodelib/fs.stat': 2.0.5 '@nodelib/fs.stat': 2.0.5
@ -7460,7 +7451,7 @@ snapshots:
dependencies: dependencies:
lru-cache: 10.4.3 lru-cache: 10.4.3
html-entities@2.5.5: {} html-entities@2.6.0: {}
html-escaper@2.0.2: {} html-escaper@2.0.2: {}
@ -8727,6 +8718,11 @@ snapshots:
react-is@19.1.0: {} react-is@19.1.0: {}
react-lite-youtube-embed@2.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
react-promise-suspense@0.3.4: react-promise-suspense@0.3.4:
dependencies: dependencies:
fast-deep-equal: 2.0.1 fast-deep-equal: 2.0.1
@ -8737,7 +8733,7 @@ snapshots:
schema-dts: 1.1.5 schema-dts: 1.1.5
typescript: 5.8.2 typescript: 5.8.2
react-textarea-autosize@8.5.8(@types/react@19.0.12)(react@19.1.0): react-textarea-autosize@8.5.9(@types/react@19.0.12)(react@19.1.0):
dependencies: dependencies:
'@babel/runtime': 7.27.0 '@babel/runtime': 7.27.0
react: 19.1.0 react: 19.1.0
@ -9527,8 +9523,6 @@ snapshots:
commander: 2.20.3 commander: 2.20.3
source-map-support: 0.5.21 source-map-support: 0.5.21
third-party-capital@1.0.20: {}
tinyglobby@0.2.12: tinyglobby@0.2.12:
dependencies: dependencies:
fdir: 6.4.3(picomatch@4.0.2) fdir: 6.4.3(picomatch@4.0.2)

View File

@ -1,64 +0,0 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBF1ubd4BEACw2yUrUn6ChZlFzDrzssKLqN+4ibW+lvNBJdstmRvtYINDLmRb
RJzfYn1iCGfrUXAIAcuVsZjeEky0g+i5rgyNFm9/SDOExm0SMQjltHsCukhG9fof
HYyYJm8zJdpI7HW/KmAqhvV6HQSBAoBNRJWcs4pwoXKWAc2+fLSJXtf23mzsI3XL
GWWfM9yxS/clrk/MfyNgG4pqtlr+IFslhke12Eyh1dl2ma+IV/aiZldk7ODJ8q4a
e8C8FeTlNVOc89eksbNqWdO5IGs21gaKZfbvwIXgWVUmfMdhuS1UfEe5P0hRdMrZ
qBUMPES9FFgq4xL9YPPmYkWPPaFo1rSAvnEf9oQELeiWg2RJ19niSee7z2roM333
fM7orSmsMdjPxbeY8wO9tXKa/szzB34S+yMDQm2IortBKJlp8lMnlmEZlV3+S9Ur
AY5SsN9PEa0nKXBiatpfLwwvhUmTm6dvZfExmWVUZD32uIwd+81OA1DqkphYngAp
pevBOMyE24U4xTaN4DGgI47GI+O41aocn+eOvluqpKydSccarZ5AvRWgcQRfK5qj
YBXH/SuAAJPB9De2MynkQBoIW38hzXcMFFjP9YIuVo7QcPZeWmswo65o/fGvHuGE
CEM1tiXLlAVX2vje/5sI/jDPEAslEUaxRIazonf+BLzAU8xV/Y4shz956QARAQAB
tCJKYWtlIEphcnZpcyA8amFrZWphcnZpc0BnbWFpbC5jb20+iQJOBBMBCAA4FiEE
O8bld2vzedNvZxSAKwyc8lHmmjkFAl33uP4CGwMFCwkIBwIGFQoJCAsCBBYCAwEC
HgECF4AACgkQKwyc8lHmmjnmqBAAj5izO6CuwNopHwyHq6K68RmZ1nAlMaIGcLwL
owct5qhRl4EMKdGcADz9WTgvpW6WGPKDiTgctMyfjFpk4qu1A72OOPwdLL7n4qcP
ylqiUROExLjYvg4rb6PsYet+RWlp9aqS35OivYyl8HY4Y1bf5mRWHcGTGmhuGyPO
TihSB8mdKecdR78OktlZFokZGlBpDERkO1MPKVGZy2e3FIM6s5jG/wNWFAtnVzYn
mfOgXYQqXN73YoM5kGN0XyUX2fNcDoy7Z+fquMGbNlzS2/Ri1hfXQOXHW69xZIDF
Cqs0AHS7C1xA3qYPd6dVL6wNIhXEg95RY5Q6SYNLNloJJBcvQa09wu164abtRDpu
wAYZ5t5aG+AcGtg9LNqs6ku1dAKagjNLnmp+TfQziRa28W5eYrglrZ7QyXjNsppo
KhTi0y868snRCwWxBu6i0U1lw/grjkmxeiy7W/y77EhJl5iQLU1Jx4qF6PpnPN++
Ajt0D0S/5/WDXfVo5V3zmZJUngEuKwdF947WC6GQSocPi+rGCZGKWl+YH1CuFayD
R3nrbqtVcXlsJfq+X1raLbBs62uIx3a6ROA5mievrJpdH+8tmJbIv4KH2Lz+Eh+e
I0FFPQbBGfoEfgOtHyKpzuL+Gn3MtH07s83+sgrwF2oIEIAOmgQW2aDIYTMlDpQH
KmmkkfS0Gkpha2UgSmFydmlzIDxqYWtlQGphcnYuaXM+iQJRBBMBCAA7AhsDBQsJ
CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEEO8bld2vzedNvZxSAKwyc8lHmmjkFAl33
uP4CGQEACgkQKwyc8lHmmjmTKA//UWYnA46Kt5lvx8xI4Qp01818/scmieO7e8zN
YhrBwNuWTjw+xZL22tgat9ueSFEayWV1trQs6jNLe5Wfbs7eAxc3izRA+74lDaJw
uDBfBs6RQ/BE5rh2A7h9QziOHJNJ0356dGSJ00PrpUm68c/ng4EVYsISUgt6Xs4a
V22Pdz6W/yGqr7LfmcAiEYAQ46Xn/a0bskq9scxwctIfHkQwIZJEUIcXRlQaDnk5
oszQt7F5xQKP8k/nNIUBXPmzzYCAKRi3VFjbZ7JXT7ZyIW1EYJdXHV5MEBjgD+0u
OC0Q+SjVQ1XoEGkdWXSqbaBa9bN6fP8gZTsioj6WTAnFw/700kNPR8sNw7C8tytv
nJ2ONWzdggcak48P7bpllbiL4yMKZE3PxUjDMhpPIllehPpXJrYoKoEyRSaOt6g6
k6SVb/vU4B/ho0NParxynaywJmSr2olWHU6zx1UPqNVWid1Xh2sK8qFh+7whBE4f
2CeRPogwjydpFavudCby+YK/YJfVoxgcak3+L1xJ3gWYqIoRE4Ddnw6AG15k/11w
3Cg3yYbSalqpKlHmozXKNYkFC1E1tyoeymY62P9ImG5729pqYVOZQAYB6NZwnrzj
8NZxNsG901Rt3ctcGEpC956RFvt0C6iV4DuZ/eGyBlaydWz58EyVChTrU9ZXAuA+
Ttcgwbe5Ag0EXW5t3gEQAJdoOH8StmabgMaR9Vw++X3I/F/14YDU4NeUGussRD4J
0SdGA1nwUGEXDW1cnhMug9LyTCfWlnjrRHlTILUcqReLvDOEoYV76udGF3NRMm+w
QJfDKRNhoyNdhrL9jQENn/BQYP7sQ1P7vmb6pIuJ/nIUkfEIhGOmgNpzzKP0qhca
ncnP51X6vIWf3Xz5AH00HeMCSn247dygGGrFVRfpfpS5k/lqbyIPtCGyY7Y3lmDM
KyGxCbcCdVQvVKY33IzTIhw+v5o5eLRiodQH/C1TF2cAP3aUfRRBT+K7J7bMxtBy
415eekRILHN+ogLAJJkH4lzVunXST6hgeFEvxVEgsCJTCbtnYO4Ju6onl/+ReWLL
9JhJmSBKnxxTRoenVgAryPckBznAAfno0kcO78XIhkKjsA/j4AwerYT8hwENDlsv
eg6i/qiL5hqpCjkPER90ylZ/zJ3bvBuKdcNUtMjvbQGHi+GOgcPYO5B3h1Y3wtv2
Ouy1DwbGwWVxE+pplRkVTMoSm7rnSUi48XwugDThbxaC9ypGfAo3bcPE0HUOJG1r
NwyYSwBAYyJ7SH3nZj6kmgmiDqrd9fy0qrPZ47X8HjLIRm3RhRxk77EIjcCMGWF4
NZr++sj+4hcBZT0H/v6dOFKu0Z7PnsfsZG3wFJn+TtP5PlM1ZO6F5Al2RRyCVAyJ
ABEBAAGJAjYEGAEIACAWIQQ7xuV3a/N5029nFIArDJzyUeaaOQUCXW5t3gIbDAAK
CRArDJzyUeaaOfoTD/4h1vZqdm4RZtpImMk1O5tnloUtWPDGMQXdZ2TD8IdyZJqs
KjgRauUUfgrrQCqaLg6LQTd2d8QgdrDi9MrfB685m+s2OZvEgGj0sSxeUZ/+mzqz
4H0fRtIcRcAePRze1tkpdFbGhR0I9ojwgS8cBlpAGiN6BYdtSfNMjrUA353PWeCQ
br2Qqbg9AhPQ26jIcBD+HpaGaxcdSZUSoXo577ZY3GK8k1noH/3msznLWMRx+3B6
XEAKwt6Ln2Gxx43E0X2AzBOwEb/pKByJoXDGNIA5E/wa5CEbGcUAc6qUGZ8z67fK
cFhDRhqTvrrAWmvwoI6wd7m3mIP0ds+v2/dXEs56R/b/NjRe4PkJ7axhDmlw9hzZ
9ZAdDhb5k/+sdaOwx2Mpy36rUM2yq7sCky7/QNTvavFnP5f/jDr2lTl64j1WftzS
JWlkwLOBTsKyiY51JS3LyMCjmOs2sSrFZ1mbIIHxD5KAPm3MsECPqFoMs887Z/PN
HsKPG5rNZW8Ka4WJTpjvtDCxl65v2mNXnoeZHbx9NvFcwTN/4h4SqrAffaH6Db1a
4Y1LWwegtdld44VbF99hbnLFK2MoCjgS5iUdici4cNc3Kq+2eWgqkEWyhIzY5+uU
c640+ZqRCUd6AtID3GnmrXYg3g3LkoF3Tkjo/T3QbFoSdiFycYSSFGCrleNF7Q==
=sqOH
-----END PGP PUBLIC KEY BLOCK-----