Add comprehensive error handling and recovery UI (#173)

* feat(electron): error handling foundation

Adds the infrastructure for a coherent client-side error story:

- `lib/errors.ts`: canonical ApiError + QuotaExceededError, `toUserMessage`
  (friendly strings for ApiError/ZodError/network errors, strips Electron
  IPC message prefixes), `logError` (expected), `reportError` (unexpected).
- `lib/query-client.ts`: QueryClient factory with sane retry defaults (no
  retry on 4xx except 408/429, 2 retries otherwise; 0 mutation retries),
  `QueryCache` onError logs + opts in via `meta.toastOnError`, and
  `MutationCache` onError toasts `toUserMessage(err)` by default with
  `meta.suppressToast` as the opt-out.
- `components/app-error-boundary.tsx` + `error-fallback.tsx`: two boundaries
  (top-level outside the router, route-level inside) with a Card-based
  fallback offering 'Go home' + 'Try again'. Route boundary resets on
  pathname change and clears React Query error cache on retry.
- `main/ipc-utils.ts` + main.ts migration: `safeHandle` wraps ipcMain.handle
  so main-process failures log with full stack and surface a sanitized
  message to the renderer. `link:fetch-metadata` keeps its null contract
  but now logs.
- `useCreateParticle` opts out of the global toast (compose-overlay renders
  its own quota UX) so nothing double-toasts.

Render crashes now have a recovery UI, every mutation gets a free error
toast, and silent-catch cleanup + Sentry land in follow-up PRs.

* refactor(errors): defer mutation errors to global handler

Now that MutationCache toasts via toUserMessage by default, the per-hook
onError duplicates drop away. Also normalizes inline query-error UI and
surfaces a previously-silent failure.

Mutations — removed redundant onError toasts:
- network-selector: useAcceptInvitation, createNetwork (inline)
- network-settings: useInviteMembers, useRevokeInvitation, useRemoveMember
- network-billing: useCreateCheckoutSession, useCreatePortalSession
  (onSuccess toasts stay — they carry domain context like network name)

Queries — consistent inline error UX via toUserMessage:
- network-selector: failed-to-load state gets a "Try again" button
- network-billing: "Couldn't load billing" includes friendly reason
- network-settings: useNetworkInvitations failure now surfaces a hint
  (previously rendered as "0 pending" — silently wrong)

Trimmed noisy JSDoc from PR 1 files (errors.ts, query-client.ts,
app-error-boundary.tsx, error-fallback.tsx, ipc-utils.ts).

* refactor(errors): route silent catches through logError/reportError

Every catch now either surfaces, re-throws, or calls logError with a scope
tag. No more empty catches or bare console.error:

- auth-store: signInToFirebase / restoreSession / signOut paths gain
  logError context. Behavior is unchanged (best-effort local sign-out,
  fall back to login on restore failure).
- use-stream-autoplay: Audio.play() and download-URL fetches log their
  failures instead of dropping silently (both are nice-to-haves so UX
  stays silent — but we can now trace "why didn't autoplay trigger?").
- pusher-client: ws errors / parse failures / server errors / listener
  crashes all routed through logError, and listener bugs (which silently
  break user flows) now go through reportError so they're actually
  surfaced in observability.
- settings-page: email-notifications toggle now toasts on failure
  instead of silently reverting with no explanation.
- huddle-app: screen-share failures use logError.

* feat(errors): add Sentry observability behind a sinks facade

logError / reportError now route through a pair of sinks installed at
bootstrap by each process (main + every renderer entry). No call site
knows about Sentry — if sentryDsn is empty, the sinks simply aren't
installed and logError/reportError stay console-only.

- appConfig.sentryDsn: per-env string (empty for now — populate when
  ops creates the DSNs). Empty is the no-op mode for dev.
- lib/errors.ts: installErrorSinks({ capture, breadcrumb }) gates
  Sentry.captureException / Sentry.addBreadcrumb. Everything flows
  through toUserMessage and the two existing call types.
- lib/sentry.ts: initSentryRenderer() for the main window + autoplay,
  huddle, and screen-record renderers.
- main/sentry.ts: initSentryMain() runs before anything else in
  main.ts to catch bootstrap failures. Captures uncaught exceptions
  and the crash reporter automatically.
- main/ipc-utils.ts::safeHandle now routes through reportError.
- main.ts::fetchLinkMetadata logs via logError.

* chore: cleanup reexports

* fix(errors): use relative import so main-process bundle resolves

Rollup in vite.main.config.ts doesn't know about the `@/` alias (only the
renderer configs do). After PR 4 made `lib/errors.ts` reachable from main,
its `@/config/env` import broke the main build with:

  [vite]: Rollup failed to resolve import "@/config/env" from "lib/errors.ts"

Swap to a relative import — the module is now genuinely isomorphic.

* build(vite): resolve @/ alias in main + preload configs

The four renderer vite configs all resolve "@" to ./src; main and preload
didn't, so any main-reachable file using @/ imports would break the
bundle. Mirror the same alias block in both configs and revert
lib/errors.ts + src/main/* to the project's @/ convention.

* fix invalid tsconfig field

* comment

* feat(errors): fallback handlers for unhandled rejections and errors

Sentry's global integrations natively capture window.onerror /
unhandledrejection (renderer) and uncaughtException / unhandledRejection
(main). When a DSN is configured we let Sentry own those paths to avoid
double-capturing. When it isn't (dev, unconfigured prod), we attach
minimal listeners that route through reportError so stray promise
rejections and uncaught errors at least hit the console and the facade.

This closes the one gap where a bare click-handler promise rejection
(no try/catch, not a useMutation) would otherwise be invisible.

* Revert "feat(errors): fallback handlers for unhandled rejections and errors"

This reverts commit ed84b29c6b.

---------

Co-authored-by: Claude <[email protected]>
Co-authored-by: Arjun from Flowy Labs <[email protected]>
This commit was merged in pull request #173.
This commit is contained in:
Arjun Patel
2026-04-20 19:22:02 -07:00
committed by GitHub
co-authored by Claude Arjun from Flowy Labs
parent bd7a0c4fb8
commit 64a21198b3
31 changed files with 1096 additions and 100 deletions
+2
View File
@@ -53,6 +53,7 @@
"dependencies": { "dependencies": {
"@livekit/components-react": "^2.9.20", "@livekit/components-react": "^2.9.20",
"@livekit/components-styles": "^1.2.0", "@livekit/components-styles": "^1.2.0",
"@sentry/electron": "^7.11.0",
"@tanstack/react-query": "^5.90.21", "@tanstack/react-query": "^5.90.21",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@@ -65,6 +66,7 @@
"radix-ui": "^1.4.3", "radix-ui": "^1.4.3",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
"react-error-boundary": "^6.1.1",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"react-router-dom": "^7.13.0", "react-router-dom": "^7.13.0",
"react-use": "^17.6.0", "react-use": "^17.6.0",
+28 -22
View File
@@ -3,10 +3,7 @@ import { HashRouter, Routes, Route, useNavigate } from "react-router-dom";
import { TooltipProvider } from "@/components/ui/tooltip"; import { TooltipProvider } from "@/components/ui/tooltip";
import { useAuthStore } from "@/stores/auth-store"; import { useAuthStore } from "@/stores/auth-store";
import { LoginPage } from "@/features/auth/login-page"; import { LoginPage } from "@/features/auth/login-page";
import { import { QueryClientProvider } from '@tanstack/react-query';
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import SettingsPage from "@/features/settings-page"; import SettingsPage from "@/features/settings-page";
import AudioVideoSettingsPage from "@/features/settings/audio-video-settings-page"; import AudioVideoSettingsPage from "@/features/settings/audio-video-settings-page";
import NetworkSelector from "@/features/network-selector"; import NetworkSelector from "@/features/network-selector";
@@ -16,8 +13,13 @@ import Layout from "@/features/layout";
import NetworkSettingsPage from "@/features/network-settings"; import NetworkSettingsPage from "@/features/network-settings";
import { Toaster } from "@/components/ui/sonner"; import { Toaster } from "@/components/ui/sonner";
import { PusherProvider } from "@/lib/pusher-provider"; import { PusherProvider } from "@/lib/pusher-provider";
import { createQueryClient } from "@/lib/query-client";
import {
RouteErrorBoundary,
TopLevelErrorBoundary,
} from "@/components/app-error-boundary";
const queryClient = new QueryClient(); const queryClient = createQueryClient();
const App = () => { const App = () => {
const status = useAuthStore((s) => s.status); const status = useAuthStore((s) => s.status);
@@ -62,30 +64,34 @@ function AuthenticatedApp() {
return ( return (
<HashRouter> <HashRouter>
<AutoplayNavigationListener /> <AutoplayNavigationListener />
<Routes> <RouteErrorBoundary>
<Route path="settings" element={<SettingsPage />} /> <Routes>
<Route path="settings/audio-video" element={<AudioVideoSettingsPage />} /> <Route path="settings" element={<SettingsPage />} />
<Route path="settings/audio-video" element={<AudioVideoSettingsPage />} />
<Route path="/"> <Route path="/">
<Route index element={<Layout><NetworkSelector /></Layout>} /> <Route index element={<Layout><NetworkSelector /></Layout>} />
<Route path=":networkId"> <Route path=":networkId">
<Route index element={<Layout><NetworkRoot /></Layout>} /> <Route index element={<Layout><NetworkRoot /></Layout>} />
<Route path="settings" element={<NetworkSettingsPage />} /> <Route path="settings" element={<NetworkSettingsPage />} />
<Route path="*" element={<ParticleViewResolver />} /> <Route path="*" element={<ParticleViewResolver />} />
</Route>
</Route> </Route>
</Route> </Routes>
</Routes> </RouteErrorBoundary>
</HashRouter> </HashRouter>
); );
} }
const AppWithProviders = () => ( const AppWithProviders = () => (
<TooltipProvider> <TopLevelErrorBoundary>
<QueryClientProvider client={queryClient}> <TooltipProvider>
<App /> <QueryClientProvider client={queryClient}>
<Toaster /> <App />
</QueryClientProvider> <Toaster />
</TooltipProvider> </QueryClientProvider>
</TooltipProvider>
</TopLevelErrorBoundary>
); );
export default AppWithProviders; export default AppWithProviders;
+1 -10
View File
@@ -1,5 +1,6 @@
import { appConfig } from "@/config/env"; import { appConfig } from "@/config/env";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { ApiError } from "@/lib/errors";
import type { z } from "zod"; import type { z } from "zod";
import { import {
BillingStatusSchema, BillingStatusSchema,
@@ -27,16 +28,6 @@ import type {
SignInRequest, SignInRequest,
} from "./types"; } from "./types";
export class ApiError extends Error {
constructor(
public status: number,
message: string,
) {
super(message);
this.name = "ApiError";
}
}
interface ApiClientConfig { interface ApiClientConfig {
baseUrl: string; baseUrl: string;
getToken: () => string | null; getToken: () => string | null;
+3
View File
@@ -1,6 +1,9 @@
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { AutoplayApp } from './AutoplayApp'; import { AutoplayApp } from './AutoplayApp';
import { initSentryRenderer } from '@/lib/sentry';
import '@/styles/globals.css'; import '@/styles/globals.css';
initSentryRenderer();
const root = createRoot(document.getElementById('root')!); const root = createRoot(document.getElementById('root')!);
root.render(<AutoplayApp />); root.render(<AutoplayApp />);
+51
View File
@@ -0,0 +1,51 @@
import type { PropsWithChildren } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { useLocation } from "react-router-dom";
import { useQueryErrorResetBoundary } from "@tanstack/react-query";
import { reportError } from "@/lib/errors";
import {
RouteErrorFallback,
TopLevelErrorFallback,
} from "@/components/error-fallback";
/** Catches render crashes OUTSIDE the router so bootstrap failures still recover. */
export function TopLevelErrorBoundary({ children }: PropsWithChildren) {
return (
<ErrorBoundary
FallbackComponent={TopLevelErrorFallback}
onError={(error, info) =>
reportError(error, {
boundary: "top",
componentStack: info.componentStack,
})
}
>
{children}
</ErrorBoundary>
);
}
/**
* Route-scoped boundary. Resets on pathname change and clears the React
* Query error cache on retry so stale failures don't stick.
*/
export function RouteErrorBoundary({ children }: PropsWithChildren) {
const location = useLocation();
const { reset: resetQueries } = useQueryErrorResetBoundary();
return (
<ErrorBoundary
FallbackComponent={RouteErrorFallback}
onError={(error, info) =>
reportError(error, {
boundary: "route",
pathname: location.pathname,
componentStack: info.componentStack,
})
}
onReset={() => resetQueries()}
resetKeys={[location.pathname]}
>
{children}
</ErrorBoundary>
);
}
+94
View File
@@ -0,0 +1,94 @@
import type { FallbackProps } from "react-error-boundary";
import { useNavigate } from "react-router-dom";
import { AlertTriangle } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { toUserMessage } from "@/lib/errors";
import { appEnv } from "@/config/env";
function ErrorCard({
error,
onGoHome,
onRetry,
}: {
error: unknown;
onGoHome: () => void;
onRetry: () => void;
}) {
return (
<div className="flex h-screen w-full items-center justify-center p-6">
<Card className="w-full max-w-md">
<CardHeader>
<div className="flex items-center gap-2">
<AlertTriangle className="text-destructive size-4" />
<CardTitle>Something went wrong</CardTitle>
</div>
<CardDescription>{toUserMessage(error)}</CardDescription>
</CardHeader>
{appEnv === "dev" && error instanceof Error ? (
<CardContent>
<details className="text-muted-foreground text-xs">
<summary className="cursor-pointer select-none">
Technical details
</summary>
<pre className="mt-2 max-h-48 overflow-auto whitespace-pre-wrap break-all">
{error.stack ?? error.message}
</pre>
</details>
</CardContent>
) : null}
<CardFooter className="flex gap-2">
<Button size="sm" onClick={onGoHome}>
Go home
</Button>
<Button size="sm" variant="outline" onClick={onRetry}>
Try again
</Button>
</CardFooter>
</Card>
</div>
);
}
// Outside the router `useNavigate` is unavailable, so "Go home" drops the hash directly.
export function TopLevelErrorFallback({
error,
resetErrorBoundary,
}: FallbackProps) {
const goHome = () => {
window.location.hash = "#/";
resetErrorBoundary();
};
return (
<ErrorCard
error={error}
onGoHome={goHome}
onRetry={resetErrorBoundary}
/>
);
}
export function RouteErrorFallback({
error,
resetErrorBoundary,
}: FallbackProps) {
const navigate = useNavigate();
const goHome = () => {
navigate("/");
resetErrorBoundary();
};
return (
<ErrorCard
error={error}
onGoHome={goHome}
onRetry={resetErrorBoundary}
/>
);
}
+4
View File
@@ -23,6 +23,8 @@ type AppConfig = {
orionUrl: string; orionUrl: string;
pusherUrl: string; pusherUrl: string;
firebase: FirebaseConfig; firebase: FirebaseConfig;
/** Empty string disables Sentry. Populate per-env when ready to ship. */
sentryDsn: string;
}; };
const configs: Record<"dev" | "prod", AppConfig> = { const configs: Record<"dev" | "prod", AppConfig> = {
@@ -37,6 +39,7 @@ const configs: Record<"dev" | "prod", AppConfig> = {
projectId: "flowy-dev-440017", projectId: "flowy-dev-440017",
storageBucket: "flowy-dev-440017.firebasestorage.app", storageBucket: "flowy-dev-440017.firebasestorage.app",
}, },
sentryDsn: "",
}, },
prod: { prod: {
orionUrl: "https://orion.flowy.live", orionUrl: "https://orion.flowy.live",
@@ -49,6 +52,7 @@ const configs: Record<"dev" | "prod", AppConfig> = {
projectId: "flowy-prod-440017", projectId: "flowy-prod-440017",
storageBucket: "flowy-prod-440017.firebasestorage.app", storageBucket: "flowy-prod-440017.firebasestorage.app",
}, },
sentryDsn: "",
}, },
}; };
+2 -1
View File
@@ -1,7 +1,8 @@
import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react"; import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { useAuthStore } from "@/stores/auth-store"; import { useAuthStore } from "@/stores/auth-store";
import { QuotaExceededError, useCreateParticle, useCreateStreamParticle } from "@/hooks/use-create-particle"; import { useCreateParticle, useCreateStreamParticle } from "@/hooks/use-create-particle";
import { QuotaExceededError } from "@/lib/errors";
import { isUsageExhausted, useInvalidateNetworkUsage, useNetworkUsage } from "@/hooks/use-network-usage"; import { isUsageExhausted, useInvalidateNetworkUsage, useNetworkUsage } from "@/hooks/use-network-usage";
import { useRecorder } from "@/features/compose/use-recorder"; import { useRecorder } from "@/features/compose/use-recorder";
import { useScreenRecorder } from "@/features/compose/use-screen-recorder"; import { useScreenRecorder } from "@/features/compose/use-screen-recorder";
+2 -1
View File
@@ -20,6 +20,7 @@ import type { Particle } from "@/api/types";
import { PropsWithChildren, useCallback } from "react"; import { PropsWithChildren, useCallback } from "react";
import { useDockBadge } from "@/hooks/use-dock-badge"; import { useDockBadge } from "@/hooks/use-dock-badge";
import { toast } from "sonner"; import { toast } from "sonner";
import { RouteErrorBoundary } from "@/components/app-error-boundary";
function getParticleDisplayName(particle: Particle): string { function getParticleDisplayName(particle: Particle): string {
switch (particle.type) { switch (particle.type) {
@@ -177,7 +178,7 @@ export default function Layout({ children }: PropsWithChildren) {
return ( return (
<div className="flex h-screen flex-col"> <div className="flex h-screen flex-col">
<TopBar /> <TopBar />
{children} <RouteErrorBoundary>{children}</RouteErrorBoundary>
</div> </div>
); );
} }
+2 -5
View File
@@ -1,6 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { ExternalLink } from "lucide-react"; import { ExternalLink } from "lucide-react";
import { toast } from "sonner";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@@ -9,6 +8,7 @@ import { Separator } from "@/components/ui/separator";
import { Muted } from "@/components/ui/typography"; import { Muted } from "@/components/ui/typography";
import { CopyableEmail } from "@/components/copyable-email"; import { CopyableEmail } from "@/components/copyable-email";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { toUserMessage } from "@/lib/errors";
import { SUPPORT_EMAIL } from "@/lib/constants"; import { SUPPORT_EMAIL } from "@/lib/constants";
import { import {
useCreateCheckoutSession, useCreateCheckoutSession,
@@ -172,7 +172,6 @@ function FreeBilling({
const handleUpgrade = () => { const handleUpgrade = () => {
createCheckout.mutate(cadence, { createCheckout.mutate(cadence, {
onSuccess: ({ url }) => window.electronLink.openExternal(url), onSuccess: ({ url }) => window.electronLink.openExternal(url),
onError: (err) => toast.error(err.message || "Failed to start checkout"),
}); });
}; };
@@ -230,8 +229,6 @@ function ProBilling({
const handleManage = () => { const handleManage = () => {
createPortal.mutate(undefined, { createPortal.mutate(undefined, {
onSuccess: ({ url }) => window.electronLink.openExternal(url), onSuccess: ({ url }) => window.electronLink.openExternal(url),
onError: (err) =>
toast.error(err.message || "Failed to open billing portal"),
}); });
}; };
@@ -342,7 +339,7 @@ function AdminBillingControls({ networkId }: { networkId: string }) {
if (error) { if (error) {
return ( return (
<div className="px-4 py-3"> <div className="px-4 py-3">
<Muted className="text-sm">Failed to load billing.</Muted> <Muted className="text-sm">Couldn't load billing: {toUserMessage(error)}</Muted>
</div> </div>
); );
} }
+7 -9
View File
@@ -22,6 +22,7 @@ import { useNetworks } from "@/hooks/use-networks";
import { useMyInvitations, useAcceptInvitation } from "@/hooks/use-member-management"; import { useMyInvitations, useAcceptInvitation } from "@/hooks/use-member-management";
import { apiClient } from "@/api/client"; import { apiClient } from "@/api/client";
import { Progress } from "@/components/ui/progress"; import { Progress } from "@/components/ui/progress";
import { toUserMessage } from "@/lib/errors";
import type { Network, Invitation } from "@/api/types"; import type { Network, Invitation } from "@/api/types";
function NetworkRow({ function NetworkRow({
@@ -86,9 +87,6 @@ function InvitationRow({ invitation }: { invitation: Invitation }) {
onSuccess: () => { onSuccess: () => {
toast.success(`Joined ${invitation.network_name}`); toast.success(`Joined ${invitation.network_name}`);
}, },
onError: (err) => {
toast.error(err.message || "Failed to accept invitation");
},
}); });
}; };
@@ -138,9 +136,6 @@ function CreateNetworkDialog({
setName(""); setName("");
navigate(`/${network.id}/settings`); navigate(`/${network.id}/settings`);
}, },
onError: (err) => {
toast.error(err.message || "Failed to create network");
},
}); });
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
@@ -200,9 +195,12 @@ export default function NetworkSelector() {
if (error) { if (error) {
return ( return (
<div className="flex h-full items-center justify-center"> <div className="flex h-full flex-col items-center justify-center gap-3 p-6 text-center">
<p className="text-destructive text-sm">Failed to load networks</p> <p className="text-sm font-medium">Couldn't load your networks</p>
<p>{error.message}</p> <p className="text-muted-foreground text-xs">{toUserMessage(error)}</p>
<Button size="sm" variant="outline" onClick={() => refetch()}>
Try again
</Button>
</div> </div>
); );
} }
+9 -10
View File
@@ -79,9 +79,6 @@ function InviteForm({ networkId }: { networkId: string }) {
toast.success(`Invitation sent to ${trimmed}`); toast.success(`Invitation sent to ${trimmed}`);
setEmail(""); setEmail("");
}, },
onError: (err) => {
toast.error(err.message || "Failed to send invitation");
},
}); });
}; };
@@ -119,9 +116,6 @@ function PendingInvitationRow({
onSuccess: () => { onSuccess: () => {
toast.success(`Invitation to ${email} revoked`); toast.success(`Invitation to ${email} revoked`);
}, },
onError: (err) => {
toast.error(err.message || "Failed to revoke invitation");
},
}); });
}; };
@@ -188,7 +182,7 @@ export default function NetworkSettingsPage() {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { data: networks } = useNetworks(); const { data: networks } = useNetworks();
const network = networks?.find((n) => n.id === networkId); const network = networks?.find((n) => n.id === networkId);
const { data: invitations } = useNetworkInvitations(networkId!); const { data: invitations, error: invitationsError } = useNetworkInvitations(networkId!);
const currentUser = useAuthStore((s) => s.user); const currentUser = useAuthStore((s) => s.user);
const isAdmin = currentUser?.id === network?.admin_human.id; const isAdmin = currentUser?.id === network?.admin_human.id;
const [memberToRemove, setMemberToRemove] = useState<Human | null>(null); const [memberToRemove, setMemberToRemove] = useState<Human | null>(null);
@@ -286,6 +280,14 @@ export default function NetworkSettingsPage() {
/> />
<Separator /> <Separator />
<InviteForm networkId={networkId!} /> <InviteForm networkId={networkId!} />
{invitationsError && (
<>
<Separator />
<p className="text-muted-foreground px-4 py-3 text-xs">
Couldn't load pending invitations.
</p>
</>
)}
{pendingCount > 0 && ( {pendingCount > 0 && (
<> <>
<Separator /> <Separator />
@@ -354,9 +356,6 @@ export default function NetworkSettingsPage() {
toast.success(`Removed ${target.email}`); toast.success(`Removed ${target.email}`);
setMemberToRemove(null); setMemberToRemove(null);
}, },
onError: (err) => {
toast.error(err.message || "Failed to remove member");
},
}); });
}} }}
onClose={() => { onClose={() => {
+5 -2
View File
@@ -11,6 +11,8 @@ import { ScrollArea } from "@/components/ui/scroll-area";
import { CopyableEmail } from "@/components/copyable-email"; import { CopyableEmail } from "@/components/copyable-email";
import { useAuthStore } from "@/stores/auth-store"; import { useAuthStore } from "@/stores/auth-store";
import { apiClient } from "@/api/client"; import { apiClient } from "@/api/client";
import { logError, toUserMessage } from "@/lib/errors";
import { toast } from "sonner";
import { PRIVACY_URL, SUPPORT_EMAIL, TERMS_URL } from "@/lib/constants"; import { PRIVACY_URL, SUPPORT_EMAIL, TERMS_URL } from "@/lib/constants";
import { ArrowLeft } from "lucide-react"; import { ArrowLeft } from "lucide-react";
@@ -84,12 +86,13 @@ export default function SettingsPage() {
})); }));
try { try {
await apiClient.updateSettings({ email_notifications_enabled: checked }); await apiClient.updateSettings({ email_notifications_enabled: checked });
} catch { } catch (err) {
// Revert on failure
setEmailNotifications(!checked); setEmailNotifications(!checked);
useAuthStore.setState((state) => ({ useAuthStore.setState((state) => ({
user: state.user ? { ...state.user, email_notifications_enabled: !checked } : null, user: state.user ? { ...state.user, email_notifications_enabled: !checked } : null,
})); }));
toast.error(toUserMessage(err));
logError(err, { scope: "settings.emailNotifications" });
} }
}; };
+5 -14
View File
@@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { createParticle, createStreamParticle } from "@/lib/firestore-particles"; import { createParticle, createStreamParticle } from "@/lib/firestore-particles";
import { CONTAINER_TYPES, type NetworkUsage, type ParticleType, type ParticlePropertiesMap } from "@/api/types"; import { CONTAINER_TYPES, type NetworkUsage, type ParticleType, type ParticlePropertiesMap } from "@/api/types";
import { parseParticlePath, particlePath, ParticlePath, toFirestoreChildrenPath } from "@/lib/particle-path"; import { parseParticlePath, particlePath, ParticlePath, toFirestoreChildrenPath } from "@/lib/particle-path";
import { QuotaExceededError } from "@/lib/errors";
import { import {
isUsageExhausted, isUsageExhausted,
networkUsageQueryKey, networkUsageQueryKey,
@@ -9,19 +10,6 @@ import {
useInvalidateNetworkUsage, useInvalidateNetworkUsage,
} from "./use-network-usage"; } from "./use-network-usage";
/**
* Thrown when a free-plan network attempts to create a non-container particle
* after hitting its daily message limit. Callers should surface an upgrade
* prompt; compose UI should also disable triggers proactively via
* `useNetworkUsage` rather than relying on this throw.
*/
export class QuotaExceededError extends Error {
constructor(public readonly networkId: string) {
super("Daily message limit reached");
this.name = "QuotaExceededError";
}
}
interface CreateParticleParams<T extends ParticleType = ParticleType> { interface CreateParticleParams<T extends ParticleType = ParticleType> {
// Path to which the new particle will be added as a child // Path to which the new particle will be added as a child
path: ParticlePath; path: ParticlePath;
@@ -36,10 +24,13 @@ export function useCreateParticle() {
const invalidateUsage = useInvalidateNetworkUsage(); const invalidateUsage = useInvalidateNetworkUsage();
return useMutation({ return useMutation({
// Compose UI renders a custom quota-exceeded toast + cancels the overlay.
// Opt out of the global mutation error toast to avoid a double-toast.
meta: { suppressToast: true },
mutationFn: async (params: CreateParticleParams) => { mutationFn: async (params: CreateParticleParams) => {
const { networkId } = parseParticlePath(params.path); const { networkId } = parseParticlePath(params.path);
// Containers aren't counted server-side, so we don't block them. // Containers aren't counted server-side, so we block them here
if (!CONTAINER_TYPES.has(params.type)) { if (!CONTAINER_TYPES.has(params.type)) {
const cached = qc.getQueryData<NetworkUsage>(networkUsageQueryKey(networkId)); const cached = qc.getQueryData<NetworkUsage>(networkUsageQueryKey(networkId));
if (isUsageExhausted(cached)) { if (isUsageExhausted(cached)) {
+9 -4
View File
@@ -5,6 +5,7 @@ import { apiClient } from "@/api/client";
import { useAuthStore } from "@/stores/auth-store"; import { useAuthStore } from "@/stores/auth-store";
import { useAutoplayStore } from "@/stores/autoplay-store"; import { useAutoplayStore } from "@/stores/autoplay-store";
import { resolveHumanDisplay } from "@/lib/humans"; import { resolveHumanDisplay } from "@/lib/humans";
import { logError } from "@/lib/errors";
/** /**
* Triggers autoplay when a stream's latest child changes to a new media particle. * Triggers autoplay when a stream's latest child changes to a new media particle.
@@ -37,7 +38,11 @@ export function useStreamAutoplay(
if (useAutoplayStore.getState().muted) return; if (useAutoplayStore.getState().muted) return;
if (latestChild.type === "text") { if (latestChild.type === "text") {
new Audio(beepSound).play().catch(() => {}); // Browser autoplay policy can block this before user interaction; that's
// fine — the beep is a nice-to-have, not a critical signal.
new Audio(beepSound).play().catch((err) =>
logError(err, { scope: "autoplay.beep" }),
);
return; return;
} }
@@ -60,8 +65,8 @@ export function useStreamAutoplay(
senderName: displayName, senderName: displayName,
senderInitials: initials, senderInitials: initials,
}); });
}).catch(() => { }).catch((err) =>
// Failed to get download URL — skip autoplay silently logError(err, { scope: "autoplay.fetchUrl", particleId: particle.id }),
}); );
}, [latestChild?.id]); // eslint-disable-line react-hooks/exhaustive-deps }, [latestChild?.id]); // eslint-disable-line react-hooks/exhaustive-deps
} }
+2 -1
View File
@@ -23,6 +23,7 @@ import {
import { RoomEvent, Track } from 'livekit-client'; import { RoomEvent, Track } from 'livekit-client';
import { useState, useEffect, useRef, useCallback } from 'react'; import { useState, useEffect, useRef, useCallback } from 'react';
import { ScreenSourcePicker } from '@/components/screen-source-picker'; import { ScreenSourcePicker } from '@/components/screen-source-picker';
import { logError } from '@/lib/errors';
function readConnectionFromHash(): { token: string; serverUrl: string } | null { function readConnectionFromHash(): { token: string; serverUrl: string } | null {
const params = new URLSearchParams(window.location.hash.slice(1)); const params = new URLSearchParams(window.location.hash.slice(1));
@@ -144,7 +145,7 @@ function HuddleContent() {
setIsSharing(true); setIsSharing(true);
videoTrack.onended = () => stopScreenShare(); videoTrack.onended = () => stopScreenShare();
} catch (err) { } catch (err) {
console.error('Failed to start screen share:', err); logError(err, { scope: "huddle.screenShare" });
} }
}, },
[room], [room],
+3
View File
@@ -1,6 +1,9 @@
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { HuddleApp } from './HuddleApp'; import { HuddleApp } from './HuddleApp';
import { initSentryRenderer } from '@/lib/sentry';
import '@/styles/globals.css'; import '@/styles/globals.css';
initSentryRenderer();
const root = createRoot(document.getElementById('root')!); const root = createRoot(document.getElementById('root')!);
root.render(<HuddleApp />); root.render(<HuddleApp />);
+92
View File
@@ -0,0 +1,92 @@
import { z } from "zod";
import { appEnv } from "@/config/env";
export class ApiError extends Error {
constructor(
public status: number,
message: string,
) {
super(message);
this.name = "ApiError";
}
}
/**
* Thrown when a free-plan network attempts to create a non-container particle
* after hitting its daily message limit. Compose UI also disables triggers
* proactively via `useNetworkUsage` — this throw is a last-line defense.
*/
export class QuotaExceededError extends Error {
constructor(public readonly networkId: string) {
super("Daily message limit reached");
this.name = "QuotaExceededError";
}
}
const IPC_PREFIX = /^Error invoking remote method '[^']+':\s*/;
function normalizeMessage(message: string): string {
return message.replace(IPC_PREFIX, "").replace(/^Error:\s*/, "").trim();
}
export function toUserMessage(err: unknown): string {
if (err instanceof ApiError) {
if (err.status === 401) return "Please sign in again.";
if (err.status === 403) return "You don't have permission to do that.";
if (err.status === 404) return "Not found.";
if (err.status === 408 || err.status === 429) {
return "Please try again in a moment.";
}
if (err.status >= 500) {
return "Something went wrong on our end. Please try again.";
}
return normalizeMessage(err.message) || "Request failed.";
}
if (err instanceof z.ZodError) {
return "Received unexpected data from the server.";
}
if (err instanceof TypeError && /fetch|network/i.test(err.message)) {
return "Network error. Check your connection.";
}
if (err instanceof Error) {
return normalizeMessage(err.message) || "Something went wrong.";
}
return "Something went wrong.";
}
type ErrorContext = Record<string, unknown>;
type ErrorSink = (err: unknown, context?: ErrorContext) => void;
// Sentry (or any observability backend) installs itself via `installErrorSinks`
// from renderer.tsx / main.ts. Until then, logError is a dev-only console call
// and reportError always prints — no call site needs to know.
let captureSink: ErrorSink | null = null;
let breadcrumbSink: ErrorSink | null = null;
export function installErrorSinks(sinks: {
capture: ErrorSink;
breadcrumb: ErrorSink;
}): void {
captureSink = sinks.capture;
breadcrumbSink = sinks.breadcrumb;
}
/** Expected-but-recordable failures. Breadcrumb only — never pages anyone. */
export function logError(err: unknown, context?: ErrorContext): void {
if (appEnv === "dev") {
// eslint-disable-next-line no-console
console.error("[error]", err, context ?? {});
}
breadcrumbSink?.(err, context);
}
/** Unexpected failures the user may not see. Always captured. */
export function reportError(err: unknown, context?: ErrorContext): void {
// eslint-disable-next-line no-console
console.error("[error]", err, context ?? {});
captureSink?.(err, context);
}
+11 -6
View File
@@ -4,6 +4,8 @@
* channel subscriptions, and event dispatching. * channel subscriptions, and event dispatching.
*/ */
import { logError, reportError } from "@/lib/errors";
export type ConnectionState = export type ConnectionState =
| "disconnected" | "disconnected"
| "connecting" | "connecting"
@@ -100,8 +102,8 @@ export class PusherClient {
}; };
this.ws.onerror = (event) => { this.ws.onerror = (event) => {
console.warn("[pusher] websocket error", event); // onclose fires after onerror — reconnection is handled there.
// onclose will fire after onerror, so reconnection is handled there logError(event, { scope: "pusher.ws" });
}; };
this.ws.onmessage = (event) => { this.ws.onmessage = (event) => {
@@ -182,13 +184,15 @@ export class PusherClient {
let msg: ServerMessage; let msg: ServerMessage;
try { try {
msg = JSON.parse(data); msg = JSON.parse(data);
} catch { } catch (err) {
console.warn("[pusher] failed to parse message", data); logError(err, { scope: "pusher.parse", data });
return; return;
} }
if (msg.type === "error") { if (msg.type === "error") {
console.warn("[pusher] server error:", msg.message); logError(new Error(msg.message ?? "pusher server error"), {
scope: "pusher.server",
});
return; return;
} }
@@ -204,7 +208,8 @@ export class PusherClient {
try { try {
cb(msg); cb(msg);
} catch (err) { } catch (err) {
console.error("[pusher] listener error", err); // Listener bugs silently break user flows — escalate to reportError.
reportError(err, { scope: "pusher.listener", channel: msg.channel });
} }
} }
} }
+56
View File
@@ -0,0 +1,56 @@
import {
MutationCache,
QueryCache,
QueryClient,
} from "@tanstack/react-query";
import { toast } from "sonner";
import { ApiError, logError, reportError, toUserMessage } from "@/lib/errors";
declare module "@tanstack/react-query" {
interface Register {
queryMeta: { toastOnError?: boolean };
mutationMeta: { suppressToast?: boolean };
}
}
function shouldRetryQuery(failureCount: number, err: unknown): boolean {
if (err instanceof ApiError) {
// Retry only on transient status codes; 4xx generally won't succeed on retry.
if (err.status === 408 || err.status === 429) return failureCount < 2;
if (err.status >= 400 && err.status < 500) return false;
}
return failureCount < 2;
}
export function createQueryClient(): QueryClient {
return new QueryClient({
defaultOptions: {
queries: {
retry: shouldRetryQuery,
refetchOnWindowFocus: false,
},
mutations: {
// Mutations have side effects — never auto-retry.
retry: 0,
},
},
queryCache: new QueryCache({
onError: (err, query) => {
logError(err, { scope: "query", queryKey: query.queryKey });
if (query.meta?.toastOnError) {
toast.error(toUserMessage(err));
}
},
}),
mutationCache: new MutationCache({
onError: (err, _variables, _context, mutation) => {
reportError(err, {
scope: "mutation",
mutationKey: mutation.options.mutationKey,
});
if (mutation.meta?.suppressToast) return;
toast.error(toUserMessage(err));
},
}),
});
}
+28
View File
@@ -0,0 +1,28 @@
import * as Sentry from "@sentry/electron/renderer";
import { appConfig } from "@/config/env";
import { installErrorSinks } from "@/lib/errors";
/**
* Initialise Sentry for a renderer process. No-ops when `sentryDsn` is empty
* so dev builds and unconfigured envs stay quiet.
*/
export function initSentryRenderer(): void {
if (!appConfig.sentryDsn) return;
Sentry.init({
dsn: appConfig.sentryDsn,
tracesSampleRate: 0,
});
installErrorSinks({
capture: (err, context) =>
Sentry.captureException(err, { extra: context }),
breadcrumb: (err, context) =>
Sentry.addBreadcrumb({
category: "error",
level: "error",
message: err instanceof Error ? err.message : String(err),
data: context,
}),
});
}
+13 -5
View File
@@ -5,6 +5,11 @@ import { updateElectronApp, UpdateSourceType } from 'update-electron-app';
import type { LinkMetadata } from './lib/link-metadata'; import type { LinkMetadata } from './lib/link-metadata';
import { appConfig } from './config/env'; import { appConfig } from './config/env';
import { logError } from './lib/errors';
import { safeHandle } from './main/ipc-utils';
import { initSentryMain } from './main/sentry';
initSentryMain();
if (app.isPackaged) { if (app.isPackaged) {
updateElectronApp({ updateElectronApp({
@@ -291,7 +296,7 @@ ipcMain.on('window:close-huddle', () => {
huddleWindow?.close(); huddleWindow?.close();
}); });
ipcMain.handle('screen:get-sources', async () => { safeHandle('screen:get-sources', async () => {
const sources = await desktopCapturer.getSources({ const sources = await desktopCapturer.getSources({
types: ['screen', 'window'], types: ['screen', 'window'],
thumbnailSize: { width: 320, height: 180 }, thumbnailSize: { width: 320, height: 180 },
@@ -457,7 +462,10 @@ async function fetchLinkMetadata(url: string): Promise<LinkMetadata | null> {
metadataCache.set(url, metadata); metadataCache.set(url, metadata);
return metadata; return metadata;
} catch { } catch (err) {
// Metadata is a progressive enhancement — keep the null contract, but log
// so upstream failures (DNS, TLS, aborted fetches) aren't invisible.
logError(err, { scope: 'link.fetchMetadata', url });
return null; return null;
} }
} }
@@ -470,9 +478,9 @@ ipcMain.on('app:set-dock-badge', (_event, count: number) => {
} }
}); });
ipcMain.handle('app:get-version', () => app.getVersion()); safeHandle('app:get-version', () => app.getVersion());
ipcMain.handle('link:fetch-metadata', async (_event, url: string) => { safeHandle('link:fetch-metadata', async (_event, url) => {
if (typeof url !== 'string') return null; if (typeof url !== 'string') return null;
try { try {
new URL(url); new URL(url);
@@ -482,7 +490,7 @@ ipcMain.handle('link:fetch-metadata', async (_event, url: string) => {
return fetchLinkMetadata(url); return fetchLinkMetadata(url);
}); });
ipcMain.handle('link:open-external', async (_event, url: string) => { safeHandle('link:open-external', async (_event, url) => {
if (typeof url !== 'string') return; if (typeof url !== 'string') return;
// Only allow http(s) URLs for security // Only allow http(s) URLs for security
if (!url.startsWith('http://') && !url.startsWith('https://')) return; if (!url.startsWith('http://') && !url.startsWith('https://')) return;
+23
View File
@@ -0,0 +1,23 @@
import { ipcMain, type IpcMainInvokeEvent } from "electron";
import { reportError } from "@/lib/errors";
type InvokeHandler = (
event: IpcMainInvokeEvent,
...args: unknown[]
) => Promise<unknown> | unknown;
/**
* Wraps `ipcMain.handle` so handler failures log with full stack in main and
* re-throw a sanitized message to the renderer.
*/
export function safeHandle(channel: string, handler: InvokeHandler): void {
ipcMain.handle(channel, async (event, ...args) => {
try {
return await handler(event, ...(args as unknown[]));
} catch (err) {
reportError(err, { scope: `ipc.${channel}` });
const message = err instanceof Error ? err.message : "IPC error";
throw new Error(message);
}
});
}
+30
View File
@@ -0,0 +1,30 @@
import { app } from "electron";
import * as Sentry from "@sentry/electron/main";
import { appConfig } from "@/config/env";
import { installErrorSinks } from "@/lib/errors";
/**
* Initialise Sentry for the main process. Captures uncaught exceptions from
* the Node side and the crash reporter. Safe to call before `app.whenReady`.
*/
export function initSentryMain(): void {
if (!appConfig.sentryDsn) return;
Sentry.init({
dsn: appConfig.sentryDsn,
tracesSampleRate: 0,
release: app.getVersion(),
});
installErrorSinks({
capture: (err, context) =>
Sentry.captureException(err, { extra: context }),
breadcrumb: (err, context) =>
Sentry.addBreadcrumb({
category: "error",
level: "error",
message: err instanceof Error ? err.message : String(err),
data: context,
}),
});
}
+3
View File
@@ -1,6 +1,9 @@
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import App from './App'; import App from './App';
import { initSentryRenderer } from '@/lib/sentry';
import './styles/globals.css'; import './styles/globals.css';
initSentryRenderer();
const root = createRoot(document.getElementById('root')!); const root = createRoot(document.getElementById('root')!);
root.render(<App />); root.render(<App />);
+3
View File
@@ -1,6 +1,9 @@
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { ScreenRecordControlApp } from './ScreenRecordControlApp'; import { ScreenRecordControlApp } from './ScreenRecordControlApp';
import { initSentryRenderer } from '@/lib/sentry';
import '@/styles/globals.css'; import '@/styles/globals.css';
initSentryRenderer();
const root = createRoot(document.getElementById('root')!); const root = createRoot(document.getElementById('root')!);
root.render(<ScreenRecordControlApp />); root.render(<ScreenRecordControlApp />);
+14 -8
View File
@@ -1,16 +1,19 @@
import { create } from "zustand"; import { create } from "zustand";
import { signInWithCustomToken, signOut as firebaseSignOut } from "firebase/auth"; import { signInWithCustomToken, signOut as firebaseSignOut } from "firebase/auth";
import { apiClient, ApiError } from "@/api/client"; import { apiClient } from "@/api/client";
import type { Human } from "@/api/types"; import type { Human } from "@/api/types";
import { firebaseAuth } from "@/firebase"; import { firebaseAuth } from "@/firebase";
import { logError, ApiError } from "@/lib/errors";
import { useSessionStore } from "./session-store"; import { useSessionStore } from "./session-store";
async function signInToFirebase() { async function signInToFirebase() {
try { try {
const { token } = await apiClient.getFirebaseToken(); const { token } = await apiClient.getFirebaseToken();
await signInWithCustomToken(firebaseAuth, token); await signInWithCustomToken(firebaseAuth, token);
} catch (e) { } catch (err) {
console.error("Failed to sign in to Firebase", e); // Firestore subscriptions will fail until the next successful sign-in; the
// rest of the app keeps working against Orion.
logError(err, { scope: "auth.firebase" });
} }
} }
@@ -50,7 +53,9 @@ export const useAuthStore = create<AuthState>((set) => ({
const user = await apiClient.me(); const user = await apiClient.me();
await signInToFirebase(); await signInToFirebase();
set({ status: "authenticated", user }); set({ status: "authenticated", user });
} catch { } catch (err) {
// Expected on expired/invalid tokens — fall back to the login screen.
logError(err, { scope: "auth.restore" });
useSessionStore.getState().clearToken(); useSessionStore.getState().clearToken();
set({ status: "unauthenticated", user: null }); set({ status: "unauthenticated", user: null });
} }
@@ -91,11 +96,12 @@ export const useAuthStore = create<AuthState>((set) => ({
set({ isSigningOut: true }); set({ isSigningOut: true });
try { try {
await apiClient.signOut(); await apiClient.signOut();
} catch { } catch (err) {
// Best-effort — sign out locally regardless // Best-effort — sign out locally regardless.
logError(err, { scope: "auth.signOut" });
} finally { } finally {
await firebaseSignOut(firebaseAuth).catch((e) => await firebaseSignOut(firebaseAuth).catch((err) =>
console.error("Firebase sign-out failed", e), logError(err, { scope: "auth.firebaseSignOut" }),
); );
useSessionStore.getState().clearToken(); useSessionStore.getState().clearToken();
set({ set({
+1 -1
View File
@@ -12,7 +12,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"strict": true, "strict": true,
"baseUrl": ".", "baseUrl": "./",
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }
+6
View File
@@ -1,7 +1,13 @@
import path from "path";
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import { defineEnv } from './vite.env'; import { defineEnv } from './vite.env';
// https://vitejs.dev/config // https://vitejs.dev/config
export default defineConfig({ export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
define: defineEnv, define: defineEnv,
}); });
+6
View File
@@ -1,7 +1,13 @@
import path from "path";
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import { defineEnv } from './vite.env'; import { defineEnv } from './vite.env';
// https://vitejs.dev/config // https://vitejs.dev/config
export default defineConfig({ export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
define: defineEnv, define: defineEnv,
}); });
+581 -1
View File
@@ -881,6 +881,16 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
"@fastify/[email protected]":
version "0.18.0"
resolved "https://registry.yarnpkg.com/@fastify/otel/-/otel-0.18.0.tgz#d21814af7c97579856698e03aae0581beb3e734b"
integrity sha512-3TASCATfw+ctICSb4ymrv7iCm0qJ0N9CarB+CZ7zIJ7KqNbwI5JjyDL1/sxoC0ccTO1Zyd1iQ+oqncPg5FJXaA==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.212.0"
"@opentelemetry/semantic-conventions" "^1.28.0"
minimatch "^10.2.4"
"@firebase/[email protected]": "@firebase/[email protected]":
version "2.9.0" version "2.9.0"
resolved "https://registry.yarnpkg.com/@firebase/ai/-/ai-2.9.0.tgz#9e6f3546eb688e31488f3e081702773300d609f1" resolved "https://registry.yarnpkg.com/@firebase/ai/-/ai-2.9.0.tgz#9e6f3546eb688e31488f3e081702773300d609f1"
@@ -1798,6 +1808,303 @@
resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda" resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda"
integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==
"@opentelemetry/[email protected]":
version "0.207.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz#ae991c51eedda55af037a3e6fc1ebdb12b289f49"
integrity sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==
dependencies:
"@opentelemetry/api" "^1.3.0"
"@opentelemetry/[email protected]":
version "0.212.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.212.0.tgz#ec66a0951b84b1f082e13fd8a027b9f9d65a3f7a"
integrity sha512-TEEVrLbNROUkYY51sBJGk7lO/OLjuepch8+hmpM6ffMJQ2z/KVCjdHuCFX6fJj8OkJP2zckPjrJzQtXU3IAsFg==
dependencies:
"@opentelemetry/api" "^1.3.0"
"@opentelemetry/[email protected]":
version "0.214.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz#74a54ad7b166c6fa30a0df811954c0f5a435deee"
integrity sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==
dependencies:
"@opentelemetry/api" "^1.3.0"
"@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.1":
version "1.9.1"
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.1.tgz#c1b0346de336ba55af2d5a7970882037baedec05"
integrity sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==
"@opentelemetry/context-async-hooks@^2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-2.6.1.tgz#06e60d5b3fba992a832af7f034758574e951bba3"
integrity sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==
"@opentelemetry/[email protected]", "@opentelemetry/core@^2.0.0", "@opentelemetry/core@^2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-2.6.1.tgz#a59d22a9ae3be80bb41b280bbbe1fe9fbdb6c2a5"
integrity sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==
dependencies:
"@opentelemetry/semantic-conventions" "^1.29.0"
"@opentelemetry/[email protected]":
version "0.61.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.61.0.tgz#e9d52f56dfc4cb8a26837f31c1832af18859f1f2"
integrity sha512-mCKoyTGfRNisge4br0NpOFSy2Z1NnEW8hbCJdUDdJFHrPqVzc4IIBPA/vX0U+LUcQqrQvJX+HMIU0dbDRe0i0Q==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.33.0"
"@opentelemetry/[email protected]":
version "0.57.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.57.0.tgz#66b58af135ef6d52ad546cb440b808a149118296"
integrity sha512-FMEBChnI4FLN5TE9DHwfH7QpNir1JzXno1uz/TAucVdLCyrG0jTrKIcNHt/i30A0M2AunNBCkcd8Ei26dIPKdg==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.27.0"
"@types/connect" "3.4.38"
"@opentelemetry/[email protected]":
version "0.31.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.31.0.tgz#43bfbe09f99e84eb0d8b6e9f914c2e51a45e6d95"
integrity sha512-f654tZFQXS5YeLDNb9KySrwtg7SnqZN119FauD7acBoTzuLduaiGTNz88ixcVSOOMGZ+EjJu/RFtx5klObC95g==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/[email protected]":
version "0.62.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.62.0.tgz#c03e353caf04b7074004ce899faf759dec210b8d"
integrity sha512-Tvx+vgAZKEQxU3Rx+xWLiR0mLxHwmk69/8ya04+VsV9WYh8w6Lhx5hm5yAMvo1wy0KqWgFKBLwSeo3sHCwdOww==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.27.0"
"@opentelemetry/[email protected]":
version "0.33.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.33.0.tgz#75f2ccf653b772801b398cc2ad0974e8785f2e3d"
integrity sha512-sCZWXGalQ01wr3tAhSR9ucqFJ0phidpAle6/17HVjD6gN8FLmZMK/8sKxdXYHy3PbnlV1P4zeiSVFNKpbFMNLA==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/[email protected]":
version "0.57.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.57.0.tgz#4220a2fc1974b40a989171a9b5f3d1eeab92683f"
integrity sha512-orhmlaK+ZIW9hKU+nHTbXrCSXZcH83AescTqmpamHRobRmYSQwRbD0a1odc0yAzuzOtxYiHiXAnpnIpaSSY7Ow==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/[email protected]":
version "0.62.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.62.0.tgz#dc2fc92c6be331c4f95b62a40983c8aedb8f9bf9"
integrity sha512-3YNuLVPUxafXkH1jBAbGsKNsP3XVzcFDhCDCE3OqBwCwShlqQbLMRMFh1T/d5jaVZiGVmSsfof+ICKD2iOV8xg==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/[email protected]":
version "0.60.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.60.0.tgz#ad1ba65a32347351c310ac0f194fe66b8e9d9e7d"
integrity sha512-aNljZKYrEa7obLAxd1bCEDxF7kzCLGXTuTJZ8lMR9rIVEjmuKBXN1gfqpm/OB//Zc2zP4iIve1jBp7sr3mQV6w==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.27.0"
"@opentelemetry/[email protected]":
version "0.214.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.214.0.tgz#d4a31a638b798e191f4f556c257a4d3c97d65ba0"
integrity sha512-FlkDhZDRjDJDcO2LcSCtjRpkal1NJ8y0fBqBhTvfAR3JSYY2jAIj1kSS5IjmEBt4c3aWv+u/lqLuoCDrrKCSKg==
dependencies:
"@opentelemetry/core" "2.6.1"
"@opentelemetry/instrumentation" "0.214.0"
"@opentelemetry/semantic-conventions" "^1.29.0"
forwarded-parse "2.1.2"
"@opentelemetry/[email protected]":
version "0.62.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.62.0.tgz#4fd1775577132de5d92165caee6bbc0ae16a8c8a"
integrity sha512-ZYt//zcPve8qklaZX+5Z4MkU7UpEkFRrxsf2cnaKYBitqDnsCN69CPAuuMOX6NYdW2rG9sFy7V/QWtBlP5XiNQ==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/redis-common" "^0.38.2"
"@opentelemetry/semantic-conventions" "^1.33.0"
"@opentelemetry/[email protected]":
version "0.23.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.23.0.tgz#6b7d449d88d674ddc295a0d0cf2156f0f7d5889f"
integrity sha512-4K+nVo+zI+aDz0Z85SObwbdixIbzS9moIuKJaYsdlzcHYnKOPtB7ya8r8Ezivy/GVIBHiKJVq4tv+BEkgOMLaQ==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.30.0"
"@opentelemetry/[email protected]":
version "0.58.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.58.0.tgz#48878fe40bc48834d6b4c4148433c84524a2558a"
integrity sha512-Hc/o8fSsaWxZ8r1Yw4rNDLwTpUopTf4X32y4W6UhlHmW8Wizz8wfhgOKIelSeqFVTKBBPIDUOsQWuIMxBmu8Bw==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.33.1"
"@opentelemetry/[email protected]":
version "0.62.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.62.0.tgz#65fdf96c1b1ffb382167cd3b7a244631afd0cc1f"
integrity sha512-uVip0VuGUQXZ+vFxkKxAUNq8qNl+VFlyHDh/U6IQ8COOEDfbEchdaHnpFrMYF3psZRUuoSIgb7xOeXj00RdwDA==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.36.0"
"@opentelemetry/[email protected]":
version "0.58.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.58.0.tgz#7c730a0cb963e8ac5f3d11023518050e5f124a6a"
integrity sha512-6grM3TdMyHzlGY1cUA+mwoPueB1F3dYKgKtZIH6jOFXqfHAByyLTc+6PFjGM9tKh52CFBJaDwodNlL/Td39z7Q==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/[email protected]":
version "0.67.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.67.0.tgz#ac45611586e363e2d96c735d50f97556dd33c37e"
integrity sha512-1WJp5N1lYfHq2IhECOTewFs5Tf2NfUOwQRqs/rZdXKTezArMlucxgzAaqcgp3A3YREXopXTpXHsxZTGHjNhMdQ==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.33.0"
"@opentelemetry/[email protected]":
version "0.60.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.60.0.tgz#9481a90d3f75d66244d7f63709529cb7f2823103"
integrity sha512-8BahAZpKsOoc+lrZGb7Ofn4g3z8qtp5IxDfvAVpKXsEheQN7ONMH5djT5ihy6yf8yyeQJGS0gXFfpEAEeEHqQg==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.33.0"
"@opentelemetry/[email protected]":
version "0.60.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.60.0.tgz#10eddc3f933a80f11e334ae31c67e9d1156373ca"
integrity sha512-m/5d3bxQALllCzezYDk/6vajh0tj5OijMMvOZGr+qN1NMXm1dzMNwyJ0gNZW7Fo3YFRyj/jJMxIw+W7d525dlw==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.33.0"
"@opentelemetry/sql-common" "^0.41.2"
"@opentelemetry/[email protected]":
version "0.60.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.60.0.tgz#e8e13b60f8d8fe8d0f4941f200ae3e4a4e5e4a3c"
integrity sha512-08pO8GFPEIz2zquKDGteBZDNmwketdgH8hTe9rVYgW9kCJXq1Psj3wPQGx+VaX4ZJKCfPeoLMYup9+cxHvZyVQ==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.33.0"
"@types/mysql" "2.15.27"
"@opentelemetry/[email protected]":
version "0.66.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.66.0.tgz#78d16b50dc4c5d851015823611a46243d63a88fb"
integrity sha512-KxfLGXBb7k2ueaPJfq2GXBDXBly8P+SpR/4Mj410hhNgmQF3sCqwXvUBQxZQkDAmsdBAoenM+yV1LhtsMRamcA==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.34.0"
"@opentelemetry/sql-common" "^0.41.2"
"@types/pg" "8.15.6"
"@types/pg-pool" "2.0.7"
"@opentelemetry/[email protected]":
version "0.62.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.62.0.tgz#ecde90337fa49fec8d243bcbb8d470ce1a9ee7a1"
integrity sha512-y3pPpot7WzR/8JtHcYlTYsyY8g+pbFhAqbwAuG5bLPnR6v6pt1rQc0DpH0OlGP/9CZbWBP+Zhwp9yFoygf/ZXQ==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/redis-common" "^0.38.2"
"@opentelemetry/semantic-conventions" "^1.27.0"
"@opentelemetry/[email protected]":
version "0.33.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.33.0.tgz#00f6698f8afae1b350bf0c463a59eeae3c8d25d7"
integrity sha512-Q6WQwAD01MMTub31GlejoiFACYNw26J426wyjvU7by7fDIr2nZXNW4vhTGs7i7F0TnXBO3xN688g1tdUgYwJ5w==
dependencies:
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.33.0"
"@types/tedious" "^4.0.14"
"@opentelemetry/[email protected]":
version "0.24.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.24.0.tgz#6ad41245012742899294edf65aa79fd190369094"
integrity sha512-oKzZ3uvqP17sV0EsoQcJgjEfIp0kiZRbYu/eD8p13Cbahumf8lb/xpYeNr/hfAJ4owzEtIDcGIjprfLcYbIKBQ==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/semantic-conventions" "^1.24.0"
"@opentelemetry/[email protected]", "@opentelemetry/instrumentation@^0.214.0":
version "0.214.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.214.0.tgz#2649e8a29a8c4748bc583d35281c80632f046e25"
integrity sha512-MHqEX5Dk59cqVah5LiARMACku7jXSVk9iVDWOea4x3cr7VfdByeDCURK6o1lntT1JS/Tsovw01UJrBhN3/uC5w==
dependencies:
"@opentelemetry/api-logs" "0.214.0"
import-in-the-middle "^3.0.0"
require-in-the-middle "^8.0.0"
"@opentelemetry/instrumentation@^0.207.0":
version "0.207.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.207.0.tgz#1a5a921c04f171ff28096fa320af713f3c87ec14"
integrity sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==
dependencies:
"@opentelemetry/api-logs" "0.207.0"
import-in-the-middle "^2.0.0"
require-in-the-middle "^8.0.0"
"@opentelemetry/instrumentation@^0.212.0":
version "0.212.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.212.0.tgz#238b6e3e2131217ff4acfe7e8e7b6ce1f0ac0ba0"
integrity sha512-IyXmpNnifNouMOe0I/gX7ENfv2ZCNdYTF0FpCsoBcpbIHzk81Ww9rQTYTnvghszCg7qGrIhNvWC8dhEifgX9Jg==
dependencies:
"@opentelemetry/api-logs" "0.212.0"
import-in-the-middle "^2.0.6"
require-in-the-middle "^8.0.0"
"@opentelemetry/redis-common@^0.38.2":
version "0.38.2"
resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz#cefa4f3e79db1cd54f19e233b7dfb56621143955"
integrity sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==
"@opentelemetry/[email protected]", "@opentelemetry/resources@^2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-2.6.1.tgz#e1b02772c5f65c0e074d59e4743188f7575e97c7"
integrity sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==
dependencies:
"@opentelemetry/core" "2.6.1"
"@opentelemetry/semantic-conventions" "^1.29.0"
"@opentelemetry/sdk-trace-base@^2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.1.tgz#ed353062be4c28a0649247ad369654020c29bfce"
integrity sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==
dependencies:
"@opentelemetry/core" "2.6.1"
"@opentelemetry/resources" "2.6.1"
"@opentelemetry/semantic-conventions" "^1.29.0"
"@opentelemetry/semantic-conventions@^1.24.0", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.40.0":
version "1.40.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz#10b2944ca559386590683392022a897eefd011d3"
integrity sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==
"@opentelemetry/sql-common@^0.41.2":
version "0.41.2"
resolved "https://registry.yarnpkg.com/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz#7f4a14166cfd6c9ffe89096db1cc75eaf6443b19"
integrity sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==
dependencies:
"@opentelemetry/core" "^2.0.0"
"@prisma/[email protected]":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-7.6.0.tgz#22a4ea3e9d8cdc57cbaa0e26ccf10cb8db854549"
integrity sha512-ZPW2gRiwpPzEfgeZgaekhqXrbW+Y2RJKHVqUmlhZhKzRNCcvR6DykzylDrynpArKKRQtLxoZy36fK7U0p3pdgQ==
dependencies:
"@opentelemetry/instrumentation" "^0.207.0"
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
@@ -2656,6 +2963,118 @@
resolved "https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c" resolved "https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c"
integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg== integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==
"@sentry-internal/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-10.47.0.tgz#452320155e241c4fec44bccfe698effb453d31b4"
integrity sha512-bVFRAeJWMBcBCvJKIFCMJ1/yQToL4vPGqfmlnDZeypcxkqUDKQ/Y3ziLHXoDL2sx0lagcgU2vH1QhCQ67Aujjw==
dependencies:
"@sentry/core" "10.47.0"
"@sentry-internal/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-10.47.0.tgz#2a847b821f60802c4ed3d0da980ef57f593afb26"
integrity sha512-pdvMmi4dQpX5S/vAAzrhHPIw3T3HjUgDNgUiCBrlp7N9/6zGO2gNPhUnNekP+CjgI/z0rvf49RLqlDenpNrMOg==
dependencies:
"@sentry/core" "10.47.0"
"@sentry-internal/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-10.47.0.tgz#157256195f71592cd462fe5a57e71ddbd66e9cff"
integrity sha512-A5OY8friSe6g8WAK4L8IeOPiEd9D3Ps40DzRH5j2f6SUja0t90mKMvHRcRf8zq0d4BkdB+JM7tjOkwxpuv8heA==
dependencies:
"@sentry-internal/replay" "10.47.0"
"@sentry/core" "10.47.0"
"@sentry-internal/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-10.47.0.tgz#33bb78457ee9731056d2b5a4805328e922b28886"
integrity sha512-ScdovxP7hJxgMt70+7hFvwT02GIaIUAxdEM/YPsayZBeCoAukPW8WiwztJfoKtsfPyKJ5A6f0H3PIxTPcA9Row==
dependencies:
"@sentry-internal/browser-utils" "10.47.0"
"@sentry/core" "10.47.0"
"@sentry/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-10.47.0.tgz#286f5051ca82706c03e7a499b9464453225f3648"
integrity sha512-rC0agZdxKA5XWfL4VwPOr/rJMogXDqZgnVzr93YWpFn9DMZT/7LzxSJVPIJwRUjx3bFEby3PcTa3YaX7pxm1AA==
dependencies:
"@sentry-internal/browser-utils" "10.47.0"
"@sentry-internal/feedback" "10.47.0"
"@sentry-internal/replay" "10.47.0"
"@sentry-internal/replay-canvas" "10.47.0"
"@sentry/core" "10.47.0"
"@sentry/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-10.47.0.tgz#175d1865f0d762ebe7be3b2a6ec3ece4e5a76a5a"
integrity sha512-nsYRAx3EWezDut+Zl+UwwP07thh9uY7CfSAi2whTdcJl5hu1nSp2z8bba7Vq/MGbNLnazkd3A+GITBEML924JA==
"@sentry/electron@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@sentry/electron/-/electron-7.11.0.tgz#39a21578d3a92524748ed7b0574dde0aad65b2dc"
integrity sha512-AKz66R/o/tULOg23zJyQZU2RK2uyV7PRYEWxDeyGDIfJeg+tXN1Zwjf/WuPcpoVE3xsXcCGBReboqMLgff587Q==
dependencies:
"@sentry/browser" "10.47.0"
"@sentry/core" "10.47.0"
"@sentry/node" "10.47.0"
"@sentry/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry/node-core/-/node-core-10.47.0.tgz#1a010b8905361138d4c8cd4787431020d5c79629"
integrity sha512-qv6LsqHbkQmd0aQEUox/svRSz26J+l4gGjFOUNEay2armZu9XLD+Ct89jpFgZD5oIPNAj2jraodTRqydXiwS5w==
dependencies:
"@sentry/core" "10.47.0"
"@sentry/opentelemetry" "10.47.0"
import-in-the-middle "^3.0.0"
"@sentry/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-10.47.0.tgz#6bdbe0ee3a6bb568b402ec16456f14a7c1a98e46"
integrity sha512-R+btqPepv88o635G6HtVewLjqCLUedBg5HBs7Nq1qbbKvyti01uArUF2f+3DsLenk5B9LUNiRlE+frZA44Ahmw==
dependencies:
"@fastify/otel" "0.18.0"
"@opentelemetry/api" "^1.9.1"
"@opentelemetry/context-async-hooks" "^2.6.1"
"@opentelemetry/core" "^2.6.1"
"@opentelemetry/instrumentation" "^0.214.0"
"@opentelemetry/instrumentation-amqplib" "0.61.0"
"@opentelemetry/instrumentation-connect" "0.57.0"
"@opentelemetry/instrumentation-dataloader" "0.31.0"
"@opentelemetry/instrumentation-express" "0.62.0"
"@opentelemetry/instrumentation-fs" "0.33.0"
"@opentelemetry/instrumentation-generic-pool" "0.57.0"
"@opentelemetry/instrumentation-graphql" "0.62.0"
"@opentelemetry/instrumentation-hapi" "0.60.0"
"@opentelemetry/instrumentation-http" "0.214.0"
"@opentelemetry/instrumentation-ioredis" "0.62.0"
"@opentelemetry/instrumentation-kafkajs" "0.23.0"
"@opentelemetry/instrumentation-knex" "0.58.0"
"@opentelemetry/instrumentation-koa" "0.62.0"
"@opentelemetry/instrumentation-lru-memoizer" "0.58.0"
"@opentelemetry/instrumentation-mongodb" "0.67.0"
"@opentelemetry/instrumentation-mongoose" "0.60.0"
"@opentelemetry/instrumentation-mysql" "0.60.0"
"@opentelemetry/instrumentation-mysql2" "0.60.0"
"@opentelemetry/instrumentation-pg" "0.66.0"
"@opentelemetry/instrumentation-redis" "0.62.0"
"@opentelemetry/instrumentation-tedious" "0.33.0"
"@opentelemetry/instrumentation-undici" "0.24.0"
"@opentelemetry/resources" "^2.6.1"
"@opentelemetry/sdk-trace-base" "^2.6.1"
"@opentelemetry/semantic-conventions" "^1.40.0"
"@prisma/instrumentation" "7.6.0"
"@sentry/core" "10.47.0"
"@sentry/node-core" "10.47.0"
"@sentry/opentelemetry" "10.47.0"
import-in-the-middle "^3.0.0"
"@sentry/[email protected]":
version "10.47.0"
resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-10.47.0.tgz#499efc362ec9805e7a236a478a47bf3c745c2424"
integrity sha512-f6Hw2lrpCjlOksiosP0Z2jK/+l+21SIdoNglVeG/sttMyx8C8ywONKh0Ha50sFsvB1VaB8n94RKzzf3hkh9V3g==
dependencies:
"@sentry/core" "10.47.0"
"@sindresorhus/is@^4.0.0": "@sindresorhus/is@^4.0.0":
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
@@ -2868,6 +3287,13 @@
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5"
integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==
"@types/[email protected]":
version "3.4.38"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
dependencies:
"@types/node" "*"
"@types/debug@^4.0.0": "@types/debug@^4.0.0":
version "4.1.13" version "4.1.13"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.13.tgz#22d1cc9d542d3593caea764f974306ab36286ee7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.13.tgz#22d1cc9d542d3593caea764f974306ab36286ee7"
@@ -2968,6 +3394,13 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/[email protected]":
version "2.15.27"
resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.27.tgz#fb13b0e8614d39d42f40f381217ec3215915f1e9"
integrity sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==
dependencies:
"@types/node" "*"
"@types/node@*", "@types/node@^25.3.0": "@types/node@*", "@types/node@^25.3.0":
version "25.3.0" version "25.3.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.3.0.tgz#749b1bd4058e51b72e22bd41e9eab6ebd0180470" resolved "https://registry.yarnpkg.com/@types/node/-/node-25.3.0.tgz#749b1bd4058e51b72e22bd41e9eab6ebd0180470"
@@ -2996,6 +3429,31 @@
dependencies: dependencies:
undici-types "~7.16.0" undici-types "~7.16.0"
"@types/[email protected]":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.7.tgz#c17945a74472d9a3beaf8e66d5aa6fc938328734"
integrity sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==
dependencies:
"@types/pg" "*"
"@types/pg@*":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.20.0.tgz#8bd03d3ac6b19143a8de7d66a9d13da32cd91526"
integrity sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==
dependencies:
"@types/node" "*"
pg-protocol "*"
pg-types "^2.2.0"
"@types/[email protected]":
version "8.15.6"
resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.15.6.tgz#4df7590b9ac557cbe5479e0074ec1540cbddad9b"
integrity sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==
dependencies:
"@types/node" "*"
pg-protocol "*"
pg-types "^2.2.0"
"@types/react-dom@^19.2.3": "@types/react-dom@^19.2.3":
version "19.2.3" version "19.2.3"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c"
@@ -3035,6 +3493,13 @@
resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.6.tgz#66748315cc9a96d63403baa8671b2c124f8633aa" resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.6.tgz#66748315cc9a96d63403baa8671b2c124f8633aa"
integrity sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA== integrity sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==
"@types/tedious@^4.0.14":
version "4.0.14"
resolved "https://registry.yarnpkg.com/@types/tedious/-/tedious-4.0.14.tgz#868118e7a67808258c05158e9cad89ca58a2aec1"
integrity sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==
dependencies:
"@types/node" "*"
"@types/tough-cookie@*": "@types/tough-cookie@*":
version "4.0.5" version "4.0.5"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
@@ -3394,6 +3859,11 @@ accepts@^2.0.0:
mime-types "^3.0.0" mime-types "^3.0.0"
negotiator "^1.0.0" negotiator "^1.0.0"
acorn-import-attributes@^1.9.5:
version "1.9.5"
resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef"
integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==
acorn-import-phases@^1.0.3: acorn-import-phases@^1.0.3:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7"
@@ -3735,6 +4205,13 @@ brace-expansion@^5.0.2:
dependencies: dependencies:
balanced-match "^4.0.2" balanced-match "^4.0.2"
brace-expansion@^5.0.5:
version "5.0.5"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb"
integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==
dependencies:
balanced-match "^4.0.2"
braces@^3.0.3: braces@^3.0.3:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
@@ -3919,6 +4396,11 @@ chrome-trace-event@^1.0.2, chrome-trace-event@^1.0.3:
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b"
integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==
cjs-module-lexer@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca"
integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==
class-variance-authority@^0.7.1: class-variance-authority@^0.7.1:
version "0.7.1" version "0.7.1"
resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787" resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787"
@@ -4216,7 +4698,7 @@ data-view-byte-offset@^1.0.1:
es-errors "^1.3.0" es-errors "^1.3.0"
is-data-view "^1.0.1" is-data-view "^1.0.1"
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.4.0, debug@^4.4.3: debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0, debug@^4.4.3:
version "4.4.3" version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
@@ -5301,6 +5783,11 @@ formdata-polyfill@^4.0.10:
dependencies: dependencies:
fetch-blob "^3.1.2" fetch-blob "^3.1.2"
[email protected]:
version "2.1.2"
resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.2.tgz#08511eddaaa2ddfd56ba11138eee7df117a09325"
integrity sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==
[email protected]: [email protected]:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -5955,6 +6442,26 @@ import-fresh@^3.2.1, import-fresh@^3.3.0:
parent-module "^1.0.0" parent-module "^1.0.0"
resolve-from "^4.0.0" resolve-from "^4.0.0"
import-in-the-middle@^2.0.0, import-in-the-middle@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz#1972337bfe020d05f6b5e020c13334567436324f"
integrity sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==
dependencies:
acorn "^8.15.0"
acorn-import-attributes "^1.9.5"
cjs-module-lexer "^2.2.0"
module-details-from-path "^1.0.4"
import-in-the-middle@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz#8a0a1230c9b865c0e12698171646ae1e3fff691d"
integrity sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==
dependencies:
acorn "^8.15.0"
acorn-import-attributes "^1.9.5"
cjs-module-lexer "^2.2.0"
module-details-from-path "^1.0.4"
imurmurhash@^0.1.4: imurmurhash@^0.1.4:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -7387,6 +7894,13 @@ minimatch@^10.2.2:
dependencies: dependencies:
brace-expansion "^5.0.2" brace-expansion "^5.0.2"
minimatch@^10.2.4:
version "10.2.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1"
integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==
dependencies:
brace-expansion "^5.0.5"
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -7484,6 +7998,11 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
module-details-from-path@^1.0.3, module-details-from-path@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz#b662fdcd93f6c83d3f25289da0ce81c8d9685b94"
integrity sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==
[email protected]: [email protected]:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -8024,6 +8543,27 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
[email protected]:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
pg-protocol@*:
version "1.13.0"
resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.13.0.tgz#fdaf6d020bca590d58bb991b4b16fc448efe0511"
integrity sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==
pg-types@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
dependencies:
pg-int8 "1.0.1"
postgres-array "~2.0.0"
postgres-bytea "~1.0.0"
postgres-date "~1.0.4"
postgres-interval "^1.1.0"
picocolors@^1.1.1: picocolors@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
@@ -8080,6 +8620,28 @@ postcss@^8.4.43, postcss@^8.5.6:
picocolors "^1.1.1" picocolors "^1.1.1"
source-map-js "^1.2.1" source-map-js "^1.2.1"
postgres-array@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
postgres-bytea@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.1.tgz#c40b3da0222c500ff1e51c5d7014b60b79697c7a"
integrity sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==
postgres-date@~1.0.4:
version "1.0.7"
resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8"
integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==
postgres-interval@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695"
integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
dependencies:
xtend "^4.0.0"
postject@^1.0.0-alpha.6: postject@^1.0.0-alpha.6:
version "1.0.0-alpha.6" version "1.0.0-alpha.6"
resolved "https://registry.yarnpkg.com/postject/-/postject-1.0.0-alpha.6.tgz#9d022332272e2cfce8dea4cfce1ee6dd1b2ee135" resolved "https://registry.yarnpkg.com/postject/-/postject-1.0.0-alpha.6.tgz#9d022332272e2cfce8dea4cfce1ee6dd1b2ee135"
@@ -8291,6 +8853,11 @@ react-dom@^19.2.4:
dependencies: dependencies:
scheduler "^0.27.0" scheduler "^0.27.0"
react-error-boundary@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-6.1.1.tgz#491d655e86c32434ede852755bb649119fdddd89"
integrity sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==
react-markdown@^10.1.0: react-markdown@^10.1.0:
version "10.1.0" version "10.1.0"
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-10.1.0.tgz#e22bc20faddbc07605c15284255653c0f3bad5ca" resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-10.1.0.tgz#e22bc20faddbc07605c15284255653c0f3bad5ca"
@@ -8525,6 +9092,14 @@ require-from-string@^2.0.2:
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" 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== integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-in-the-middle@^8.0.0:
version "8.0.1"
resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz#dbde2587f669398626d56b20c868ab87bf01cce4"
integrity sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==
dependencies:
debug "^4.3.5"
module-details-from-path "^1.0.3"
resedit@^2.0.0: resedit@^2.0.0:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/resedit/-/resedit-2.0.3.tgz#5145a9faabca44b917d5636dbe8e67ec7f62c6f2" resolved "https://registry.yarnpkg.com/resedit/-/resedit-2.0.3.tgz#5145a9faabca44b917d5636dbe8e67ec7f62c6f2"
@@ -10173,6 +10748,11 @@ xmlbuilder@^15.1.1:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==
xtend@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
y18n@^5.0.5: y18n@^5.0.5:
version "5.0.8" version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"