diff --git a/.github/workflows/publish-embedded-packages.yaml b/.github/workflows/publish-embedded-packages.yaml index 41dd8f1f..4182d488 100644 --- a/.github/workflows/publish-embedded-packages.yaml +++ b/.github/workflows/publish-embedded-packages.yaml @@ -165,6 +165,10 @@ jobs: - name: Copy files run: rsync -a --delete --exclude .git element-call/embedded/ios/ element-call-swift + - name: Test build + working-directory: element-call-swift + run: swift build + - name: Get artifact version run: echo "ARTIFACT_VERSION=${VERSION:1}" >> "$GITHUB_ENV" diff --git a/README.md b/README.md index e1f56275..31684a21 100644 --- a/README.md +++ b/README.md @@ -97,14 +97,14 @@ deployment for three different sites A, B and C is depicted below. MatrixRTC backend (according to [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)) -is announced by the homeserver's `.well-known/matrix/client` file and discovered +is announced by the Matrix site's `.well-known/matrix/client` file and discovered via the `org.matrix.msc4143.rtc_foci` key, e.g.: ```json "org.matrix.msc4143.rtc_foci": [ { "type": "livekit", - "livekit_service_url": "https://someurl.com" + "livekit_service_url": "https://matrix-rtc.example.com/livekit/jwt" }, ] ``` @@ -232,8 +232,8 @@ The easiest way to develop new test is to use the codegen feature of Playwright: npx playwright codegen ``` -This will record your action and write the test code for you. Use the tool bar to test visibility, text content, -clicking. +This will record your action and write the test code for you. Use the tool bar +to test visibility, text content and clicking. ##### Investigate a failed test from the CI @@ -251,8 +251,8 @@ Unzip the report then use this command to open the report in your browser: npx playwright show-report ~/Downloads/playwright-report/ ``` -Under the failed test there is a small icon looking like "3 columns" (next to test name file name), -click on it to see the live screenshots/console output. +Under the failed test there is a small icon looking like "3 columns" (next to +the test name file name), click on it to see the live screenshots/console output. ### Test Coverage diff --git a/config/config_netlify_preview.json b/config/config_netlify_preview.json index ec1688d2..dde58267 100644 --- a/config/config_netlify_preview.json +++ b/config/config_netlify_preview.json @@ -5,12 +5,6 @@ "server_name": "call-unstable.ems.host" } }, - "livekit": { - "livekit_service_url": "https://livekit-jwt.call.element.dev" - }, - "features": { - "feature_use_device_session_member_events": true - }, "posthog": { "api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU", "api_host": "https://posthog-element-call.element.io" diff --git a/docs/MSC4195_setup.drawio.png b/docs/MSC4195_setup.drawio.png index 42b4d1aa..18566448 100644 Binary files a/docs/MSC4195_setup.drawio.png and b/docs/MSC4195_setup.drawio.png differ diff --git a/docs/element_call_standalone.drawio.png b/docs/element_call_standalone.drawio.png index 7667d77c..1e105ef4 100644 Binary files a/docs/element_call_standalone.drawio.png and b/docs/element_call_standalone.drawio.png differ diff --git a/docs/element_call_widget.drawio.png b/docs/element_call_widget.drawio.png index 97d5ace3..72a4e1de 100644 Binary files a/docs/element_call_widget.drawio.png and b/docs/element_call_widget.drawio.png differ diff --git a/docs/self-hosting.md b/docs/self-hosting.md index fecd4773..c085be3a 100644 --- a/docs/self-hosting.md +++ b/docs/self-hosting.md @@ -30,8 +30,9 @@ required for Element Call to work properly: sync v2 API that allows them to correctly track the state of the room. This is required by Element Call to track room state reliably. -If you're using [Synapse](https://github.com/element-hq/synapse/) as your homeserver, you'll need -to additionally add the following config items to `homeserver.yaml` to comply with Element Call: +If you're using [Synapse](https://github.com/element-hq/synapse/) as your +homeserver, you'll need to additionally add the following config items to +`homeserver.yaml` to comply with Element Call: ```yaml experimental_features: @@ -64,35 +65,88 @@ required for each site deployment. ![MSC4195 compatible setup](MSC4195_setup.drawio.png) -As depicted above, Element Call requires a +As depicted above in the `example.com` site deployment, Element Call requires a [Livekit SFU](https://github.com/livekit/livekit) alongside a [Matrix Livekit JWT auth service](https://github.com/element-hq/lk-jwt-service) to implement [MSC4195: MatrixRTC using LiveKit backend](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md). +#### Matrix site endpoint routing + +In the context of MatrixRTC, we suggest using a single hostname for backend +communication by implementing endpoint routing within a reverse proxy setup. For +the example above, this results in: +| Service | Endpoint | Example | +| -------- | ------- | ------- | +| [Livekit SFU](https://github.com/livekit/livekit) WebSocket signalling connection | `/livekit/sfu` | `matrix-rtc.example.com/livekit/sfu` | +| [Matrix Livekit JWT auth service](https://github.com/element-hq/lk-jwt-service) | `/livekit/jwt` | `matrix-rtc.example.com/livekit/jwt` | + +Using Nginx, you can achieve this by: + +```jsonc +server { + ... + location ^~ /livekit/jwt/ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # JWT Service running at port 8080 + proxy_pass http://localhost:8080/; + } + + location ^~ /livekit/sfu/ { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_send_timeout 120; + proxy_read_timeout 120; + proxy_buffering off; + + proxy_set_header Accept-Encoding gzip; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # LiveKit SFU websocket connection running at port 7880 + proxy_pass http://localhost:7880/; + } +} +``` + +#### MatrixRTC backend announcement + > [!IMPORTANT] > As defined in > [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143) -> MatrixRTC backend must be announced to the client via your **homeserver's -> `.well-known/matrix/client`**. The configuration is a list of Foci configs: +> MatrixRTC backend must be announced to the client via your **Matrix site's +> `.well-known/matrix/client`** file (e.g. +> `example.com/.well-known/matrix/client` matching the site deployment example +> from above). The configuration is a list of Foci configs: ```json "org.matrix.msc4143.rtc_foci": [ { "type": "livekit", - "livekit_service_url": "https://someurl.com" - }, - { - "type": "livekit", - "livekit_service_url": "https://livekit2.com" + "livekit_service_url": "https://matrix-rtc.example.com" }, { - "type": "another_foci", - "props_for_another_foci": "val" + "type": "livekit", + "livekit_service_url": "https://matrix-rtc-2.example.com" + }, + { + "type": "nextgen_new_foci_type", + "props_for_nextgen_foci": "val" } ] ``` +> [!NOTE] +> Most `org.matrix.msc4143.rtc_foci` configurations will only have one entry in +> the array + ## Building Element Call > [!NOTE] diff --git a/embedded/ios/Package.swift b/embedded/ios/Package.swift index 53195216..08b562a1 100644 --- a/embedded/ios/Package.swift +++ b/embedded/ios/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "EmbeddedElementCall", - platforms: [.iOS(.v17_6)], + platforms: [.iOS(.v17)], products: [ .library( name: "EmbeddedElementCall", diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx index 0fbff564..f964dada 100644 --- a/src/ClientContext.tsx +++ b/src/ClientContext.tsx @@ -50,7 +50,7 @@ export type ValidClientState = { reactions: boolean; thumbnails: boolean; }; - setClient: (params?: SetClientParams) => void; + setClient: (client: MatrixClient, session: Session) => void; }; export type AuthenticatedClient = { @@ -65,11 +65,6 @@ export type ErrorState = { error: Error; }; -export type SetClientParams = { - client: MatrixClient; - session: Session; -}; - const ClientContext = createContext(undefined); export const ClientContextProvider = ClientContext.Provider; @@ -79,7 +74,7 @@ export const useClientState = (): ClientState | undefined => export function useClient(): { client?: MatrixClient; - setClient?: (params?: SetClientParams) => void; + setClient?: (client: MatrixClient, session: Session) => void; } { let client; let setClient; @@ -96,7 +91,7 @@ export function useClient(): { // Plain representation of the `ClientContext` as a helper for old components that expected an object with multiple fields. export function useClientLegacy(): { client?: MatrixClient; - setClient?: (params?: SetClientParams) => void; + setClient?: (client: MatrixClient, session: Session) => void; passwordlessUser: boolean; loading: boolean; authenticated: boolean; @@ -160,7 +155,11 @@ export const ClientProvider: FC = ({ children }) => { initializing.current = true; loadClient() - .then(setInitClientState) + .then((initResult) => { + setInitClientState(initResult); + if (PosthogAnalytics.instance.isEnabled()) + PosthogAnalytics.instance.startListeningToSettingsChanges(); + }) .catch((err) => logger.error(err)) .finally(() => (initializing.current = false)); }, []); @@ -196,24 +195,20 @@ export const ClientProvider: FC = ({ children }) => { ); const setClient = useCallback( - (clientParams?: SetClientParams) => { + (client: MatrixClient, session: Session) => { const oldClient = initClientState?.client; - const newClient = clientParams?.client; - if (oldClient && oldClient !== newClient) { + if (oldClient && oldClient !== client) { oldClient.stopClient(); } - if (clientParams) { - saveSession(clientParams.session); - setInitClientState({ - widgetApi: null, - client: clientParams.client, - passwordlessUser: clientParams.session.passwordlessUser, - }); - } else { - clearSession(); - setInitClientState(null); - } + saveSession(session); + setInitClientState({ + widgetApi: null, + client, + passwordlessUser: session.passwordlessUser, + }); + if (PosthogAnalytics.instance.isEnabled()) + PosthogAnalytics.instance.startListeningToSettingsChanges(); }, [initClientState?.client], ); @@ -229,6 +224,7 @@ export const ClientProvider: FC = ({ children }) => { clearSession(); setInitClientState(null); await navigate("/"); + PosthogAnalytics.instance.logout(); PosthogAnalytics.instance.setRegistrationType(RegistrationType.Guest); }, [navigate, initClientState?.client]); diff --git a/src/analytics/PosthogAnalytics.ts b/src/analytics/PosthogAnalytics.ts index fda78b39..581b967e 100644 --- a/src/analytics/PosthogAnalytics.ts +++ b/src/analytics/PosthogAnalytics.ts @@ -12,6 +12,7 @@ import posthog, { } from "posthog-js"; import { logger } from "matrix-js-sdk/src/logger"; import { type MatrixClient } from "matrix-js-sdk/src/matrix"; +import { type Subscription } from "rxjs"; import { widget } from "../widget"; import { @@ -95,6 +96,7 @@ export class PosthogAnalytics { private anonymity = Anonymity.Disabled; private platformSuperProperties = {}; private registrationType: RegistrationType = RegistrationType.Guest; + private optInListener: Subscription | null = null; public static hasInstance(): boolean { return Boolean(this.internalInstance); @@ -143,7 +145,6 @@ export class PosthogAnalytics { ); this.enabled = false; } - this.startListeningToSettingsChanges(); // Triggers maybeIdentifyUser } private sanitizeProperties = ( @@ -325,6 +326,8 @@ export class PosthogAnalytics { if (this.enabled) { this.posthog.reset(); } + this.optInListener?.unsubscribe(); + this.optInListener = null; this.setAnonymity(Anonymity.Disabled); } @@ -403,7 +406,7 @@ export class PosthogAnalytics { } } - private startListeningToSettingsChanges(): void { + public startListeningToSettingsChanges(): void { // Listen to account data changes from sync so we can observe changes to relevant flags and update. // This is called - // * On page load, when the account data is first received by sync @@ -412,7 +415,7 @@ export class PosthogAnalytics { // * When the user changes their preferences on this device // Note that for new accounts, pseudonymousAnalyticsOptIn won't be set, so updateAnonymityFromSettings // won't be called (i.e. this.anonymity will be left as the default, until the setting changes) - optInAnalytics.value$.subscribe((optIn) => { + this.optInListener ??= optInAnalytics.value$.subscribe((optIn) => { this.setAnonymity(optIn ? Anonymity.Pseudonymous : Anonymity.Disabled); this.maybeIdentifyUser().catch(() => logger.log("Could not identify user"), diff --git a/src/auth/LoginPage.tsx b/src/auth/LoginPage.tsx index b3805ef7..da20a86b 100644 --- a/src/auth/LoginPage.tsx +++ b/src/auth/LoginPage.tsx @@ -53,7 +53,7 @@ export const LoginPage: FC = () => { return; } - setClient({ client, session }); + setClient(client, session); const locationState = location.state; // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx index bb2c09a4..b82afd5a 100644 --- a/src/auth/RegisterPage.tsx +++ b/src/auth/RegisterPage.tsx @@ -95,7 +95,7 @@ export const RegisterPage: FC = () => { } } - setClient?.({ client: newClient, session }); + setClient?.(newClient, session); PosthogAnalytics.instance.eventSignup.cacheSignupEnd(new Date()); }; diff --git a/src/auth/useRegisterPasswordlessUser.ts b/src/auth/useRegisterPasswordlessUser.ts index 6dad6ebd..728fe131 100644 --- a/src/auth/useRegisterPasswordlessUser.ts +++ b/src/auth/useRegisterPasswordlessUser.ts @@ -47,7 +47,7 @@ export function useRegisterPasswordlessUser(): UseRegisterPasswordlessUserType { recaptchaResponse, true, ); - setClient({ client, session }); + setClient(client, session); } catch (e) { reset(); throw e; diff --git a/src/home/UnauthenticatedView.tsx b/src/home/UnauthenticatedView.tsx index ced13985..d84d2309 100644 --- a/src/home/UnauthenticatedView.tsx +++ b/src/home/UnauthenticatedView.tsx @@ -89,7 +89,7 @@ export const UnauthenticatedView: FC = () => { // @ts-ignore if (error.errcode === "M_ROOM_IN_USE") { setOnFinished(() => { - setClient({ client, session }); + setClient(client, session); const aliasLocalpart = roomAliasLocalpartFromRoomName(roomName); navigate(`/${aliasLocalpart}`)?.catch((error) => { logger.error("Failed to navigate to alias localpart", error); @@ -111,7 +111,7 @@ export const UnauthenticatedView: FC = () => { if (!createRoomResult.password) throw new Error("Failed to create room with shared secret"); - setClient({ client, session }); + setClient(client, session); await navigate( getRelativeRoomUrl( createRoomResult.roomId, diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 4a82de23..ac44db9b 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -12,9 +12,9 @@ import { useEffect, useState } from "react"; import { type LivekitFocus } from "matrix-js-sdk/src/matrixrtc/LivekitFocus"; import { useActiveLivekitFocus } from "../room/useActiveFocus"; -import { useGroupCallErrorBoundary } from "../room/useCallErrorBoundary.ts"; -import { FailToGetOpenIdToken } from "../utils/errors.ts"; -import { doNetworkOperationWithRetry } from "../utils/matrix.ts"; +import { useErrorBoundary } from "../useErrorBoundary"; +import { FailToGetOpenIdToken } from "../utils/errors"; +import { doNetworkOperationWithRetry } from "../utils/matrix"; export interface SFUConfig { url: string; @@ -41,7 +41,7 @@ export function useOpenIDSFU( const [sfuConfig, setSFUConfig] = useState(undefined); const activeFocus = useActiveLivekitFocus(rtcSession); - const { showGroupCallErrorBoundary } = useGroupCallErrorBoundary(); + const { showErrorBoundary } = useErrorBoundary(); useEffect(() => { if (activeFocus) { @@ -50,14 +50,14 @@ export function useOpenIDSFU( setSFUConfig(sfuConfig); }, (e) => { - showGroupCallErrorBoundary(new FailToGetOpenIdToken(e)); + showErrorBoundary(new FailToGetOpenIdToken(e)); logger.error("Failed to get SFU config", e); }, ); } else { setSFUConfig(undefined); } - }, [client, activeFocus, showGroupCallErrorBoundary]); + }, [client, activeFocus, showErrorBoundary]); return sfuConfig; } diff --git a/src/room/GroupCallView.test.tsx b/src/room/GroupCallView.test.tsx index 0a57d081..f0023b22 100644 --- a/src/room/GroupCallView.test.tsx +++ b/src/room/GroupCallView.test.tsx @@ -5,7 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { beforeEach, expect, type MockedFunction, test, vitest } from "vitest"; +import { + beforeEach, + expect, + type MockedFunction, + onTestFinished, + test, + vi, +} from "vitest"; import { render, waitFor, screen } from "@testing-library/react"; import { type MatrixClient } from "matrix-js-sdk/src/client"; import { type MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc"; @@ -15,6 +22,7 @@ import { BrowserRouter } from "react-router-dom"; import userEvent from "@testing-library/user-event"; import { type RelationsContainer } from "matrix-js-sdk/src/models/relations-container"; import { useState } from "react"; +import { TooltipProvider } from "@vector-im/compound-web"; import { type MuteStates } from "./MuteStates"; import { prefetchSounds } from "../soundUtils"; @@ -28,20 +36,33 @@ import { MockRTCSession, } from "../utils/test"; import { GroupCallView } from "./GroupCallView"; -import { leaveRTCSession } from "../rtcSessionHelpers"; import { type WidgetHelpers } from "../widget"; import { LazyEventEmitter } from "../LazyEventEmitter"; +import { MatrixRTCFocusMissingError } from "../utils/errors"; -vitest.mock("../soundUtils"); -vitest.mock("../useAudioContext"); -vitest.mock("./InCallView"); +vi.mock("../soundUtils"); +vi.mock("../useAudioContext"); +vi.mock("./InCallView"); +vi.mock("react-use-measure", () => ({ + default: (): [() => void, object] => [(): void => {}, {}], +})); -vitest.mock("../rtcSessionHelpers", async (importOriginal) => { +const enterRTCSession = vi.hoisted(() => vi.fn(async () => Promise.resolve())); +const leaveRTCSession = vi.hoisted(() => + vi.fn( + async ( + rtcSession: unknown, + cause: unknown, + promiseBeforeHangup = Promise.resolve(), + ) => await promiseBeforeHangup, + ), +); + +vi.mock("../rtcSessionHelpers", async (importOriginal) => { // TODO: perhaps there is a more elegant way to manage the type import here? // eslint-disable-next-line @typescript-eslint/consistent-type-imports const orig = await importOriginal(); - vitest.spyOn(orig, "leaveRTCSession"); - return orig; + return { ...orig, enterRTCSession, leaveRTCSession }; }); let playSound: MockedFunction< @@ -55,11 +76,11 @@ const roomMembers = new Map([carol].map((p) => [p.userId, p])); const roomId = "!foo:bar"; beforeEach(() => { - vitest.clearAllMocks(); + vi.clearAllMocks(); (prefetchSounds as MockedFunction).mockResolvedValue({ sound: new ArrayBuffer(0), }); - playSound = vitest.fn(); + playSound = vi.fn(); (useAudioContext as MockedFunction).mockReturnValue({ playSound, }); @@ -75,7 +96,10 @@ beforeEach(() => { ); }); -function createGroupCallView(widget: WidgetHelpers | null): { +function createGroupCallView( + widget: WidgetHelpers | null, + joined = true, +): { rtcSession: MockRTCSession; getByText: ReturnType["getByText"]; } { @@ -88,7 +112,7 @@ function createGroupCallView(widget: WidgetHelpers | null): { const room = mockMatrixRoom({ relations: { getChildEventsForEvent: () => - vitest.mocked({ + vi.mocked({ getRelations: () => [], }), } as unknown as RelationsContainer, @@ -106,24 +130,27 @@ function createGroupCallView(widget: WidgetHelpers | null): { localRtcMember, [], ).withMemberships(of([])); + rtcSession.joined = joined; const muteState = { audio: { enabled: false }, video: { enabled: false }, } as MuteStates; const { getByText } = render( - + + + , ); return { @@ -132,7 +159,7 @@ function createGroupCallView(widget: WidgetHelpers | null): { }; } -test("will play a leave sound asynchronously in SPA mode", async () => { +test("GroupCallView plays a leave sound asynchronously in SPA mode", async () => { const user = userEvent.setup(); const { getByText, rtcSession } = createGroupCallView(null); const leaveButton = getByText("Leave"); @@ -143,13 +170,13 @@ test("will play a leave sound asynchronously in SPA mode", async () => { "user", expect.any(Promise), ); - expect(rtcSession.leaveRoomSession).toHaveBeenCalledOnce(); + expect(leaveRTCSession).toHaveBeenCalledOnce(); // Ensure that the playSound promise resolves within this test to avoid // impacting the results of other tests await waitFor(() => expect(leaveRTCSession).toHaveResolved()); }); -test("will play a leave sound synchronously in widget mode", async () => { +test("GroupCallView plays a leave sound synchronously in widget mode", async () => { const user = userEvent.setup(); const widget = { api: { @@ -158,7 +185,7 @@ test("will play a leave sound synchronously in widget mode", async () => { lazyActions: new LazyEventEmitter(), }; let resolvePlaySound: () => void; - playSound = vitest + playSound = vi .fn() .mockReturnValue( new Promise((resolve) => (resolvePlaySound = resolve)), @@ -183,7 +210,7 @@ test("will play a leave sound synchronously in widget mode", async () => { "user", expect.any(Promise), ); - expect(rtcSession.leaveRoomSession).toHaveBeenCalledOnce(); + expect(leaveRTCSession).toHaveBeenCalledOnce(); }); test("GroupCallView leaves the session when an error occurs", async () => { @@ -205,8 +232,15 @@ test("GroupCallView leaves the session when an error occurs", async () => { "error", expect.any(Promise), ); - expect(rtcSession.leaveRoomSession).toHaveBeenCalledOnce(); - // Ensure that the playSound promise resolves within this test to avoid - // impacting the results of other tests - await waitFor(() => expect(leaveRTCSession).toHaveResolved()); +}); + +test("GroupCallView shows errors that occur during joining", async () => { + const user = userEvent.setup(); + enterRTCSession.mockRejectedValue(new MatrixRTCFocusMissingError("")); + onTestFinished(() => { + enterRTCSession.mockReset(); + }); + createGroupCallView(null, false); + await user.click(screen.getByRole("button", { name: "Join call" })); + screen.getByText("Call is not supported"); }); diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 1b3d0f20..5e307273 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -67,7 +67,6 @@ import { useSetting, } from "../settings/settings"; import { useTypedEventEmitter } from "../useEvents"; -import { useGroupCallErrorBoundary } from "./useCallErrorBoundary.ts"; declare global { interface Window { @@ -100,6 +99,11 @@ export const GroupCallView: FC = ({ muteStates, widget, }) => { + // Used to thread through any errors that occur outside the error boundary + const [externalError, setExternalError] = useState( + null, + ); + const memberships = useMatrixRTCSessionMemberships(rtcSession); const leaveSoundContext = useLatest( useAudioContext({ @@ -121,13 +125,11 @@ export const GroupCallView: FC = ({ }; }, [rtcSession]); - const { showGroupCallErrorBoundary } = useGroupCallErrorBoundary(); - useTypedEventEmitter( rtcSession, MatrixRTCSessionEvent.MembershipManagerError, (error) => { - showGroupCallErrorBoundary( + setExternalError( new RTCSessionError( ErrorCode.MEMBERSHIP_MANAGER_UNRECOVERABLE, error.message ?? error, @@ -190,17 +192,17 @@ export const GroupCallView: FC = ({ ); } catch (e) { if (e instanceof ElementCallError) { - showGroupCallErrorBoundary(e); + setExternalError(e); } else { logger.error(`Unknown Error while entering RTC session`, e); const error = new UnknownCallError( e instanceof Error ? e : new Error("Unknown error", { cause: e }), ); - showGroupCallErrorBoundary(error); + setExternalError(error); } } }, - [showGroupCallErrorBoundary], + [setExternalError], ); useEffect(() => { @@ -422,7 +424,15 @@ export const GroupCallView: FC = ({ ); let body: ReactNode; - if (isJoined) { + if (externalError) { + // If an error was recorded within this component but outside + // GroupCallErrorBoundary, create a component that rethrows the error from + // within the error boundary, so it can be handled uniformly + const ErrorComponent = (): ReactNode => { + throw externalError; + }; + body = ; + } else if (isJoined) { body = ( <> {shareModal} diff --git a/src/rtcSessionHelpers.ts b/src/rtcSessionHelpers.ts index 0f43fd90..1d9ec6f6 100644 --- a/src/rtcSessionHelpers.ts +++ b/src/rtcSessionHelpers.ts @@ -166,7 +166,6 @@ const widgetPostHangupProcedure = async ( logger.error("Failed to send close action", e); } widget.api.transport.stop(); - PosthogAnalytics.instance.logout(); } }; diff --git a/src/room/useCallErrorBoundary.test.tsx b/src/useErrorBoundary.test.tsx similarity index 75% rename from src/room/useCallErrorBoundary.test.tsx rename to src/useErrorBoundary.test.tsx index eccb8039..13fa43bb 100644 --- a/src/room/useCallErrorBoundary.test.tsx +++ b/src/useErrorBoundary.test.tsx @@ -11,19 +11,19 @@ import { type ReactElement, useCallback } from "react"; import userEvent from "@testing-library/user-event"; import { BrowserRouter } from "react-router-dom"; -import { GroupCallErrorBoundary } from "./GroupCallErrorBoundary.tsx"; -import { useGroupCallErrorBoundary } from "./useCallErrorBoundary.ts"; -import { ConnectionLostError } from "../utils/errors.ts"; +import { GroupCallErrorBoundary } from "./room/GroupCallErrorBoundary"; +import { useErrorBoundary } from "./useErrorBoundary"; +import { ConnectionLostError } from "./utils/errors"; it("should show async error", async () => { const user = userEvent.setup(); const TestComponent = (): ReactElement => { - const { showGroupCallErrorBoundary } = useGroupCallErrorBoundary(); + const { showErrorBoundary } = useErrorBoundary(); const onClick = useCallback((): void => { - showGroupCallErrorBoundary(new ConnectionLostError()); - }, [showGroupCallErrorBoundary]); + showErrorBoundary(new ConnectionLostError()); + }, [showErrorBoundary]); return (
diff --git a/src/room/useCallErrorBoundary.ts b/src/useErrorBoundary.ts similarity index 54% rename from src/room/useCallErrorBoundary.ts rename to src/useErrorBoundary.ts index f89abf77..4430394e 100644 --- a/src/room/useCallErrorBoundary.ts +++ b/src/useErrorBoundary.ts @@ -7,18 +7,16 @@ Please see LICENSE in the repository root for full details. import { useMemo, useState } from "react"; -import type { ElementCallError } from "../utils/errors.ts"; - export type UseErrorBoundaryApi = { - showGroupCallErrorBoundary: (error: ElementCallError) => void; + showErrorBoundary: (error: Error) => void; }; -export function useGroupCallErrorBoundary(): UseErrorBoundaryApi { - const [error, setError] = useState(null); +export function useErrorBoundary(): UseErrorBoundaryApi { + const [error, setError] = useState(null); const memoized: UseErrorBoundaryApi = useMemo( () => ({ - showGroupCallErrorBoundary: (error: ElementCallError) => setError(error), + showErrorBoundary: (error: Error) => setError(error), }), [], ); diff --git a/src/utils/test.ts b/src/utils/test.ts index 72c49d7a..9845d51d 100644 --- a/src/utils/test.ts +++ b/src/utils/test.ts @@ -286,8 +286,9 @@ export class MockRTCSession extends TypedEventEmitter< super(); } - public isJoined(): true { - return true; + public joined = true; + public isJoined(): boolean { + return this.joined; } public withMemberships( diff --git a/yarn.lock b/yarn.lock index 01ca0fef..ffc3ceca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2000,177 +2000,177 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/aix-ppc64@npm:0.25.0" +"@esbuild/aix-ppc64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/aix-ppc64@npm:0.25.1" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/android-arm64@npm:0.25.0" +"@esbuild/android-arm64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/android-arm64@npm:0.25.1" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/android-arm@npm:0.25.0" +"@esbuild/android-arm@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/android-arm@npm:0.25.1" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/android-x64@npm:0.25.0" +"@esbuild/android-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/android-x64@npm:0.25.1" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/darwin-arm64@npm:0.25.0" +"@esbuild/darwin-arm64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/darwin-arm64@npm:0.25.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/darwin-x64@npm:0.25.0" +"@esbuild/darwin-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/darwin-x64@npm:0.25.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/freebsd-arm64@npm:0.25.0" +"@esbuild/freebsd-arm64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/freebsd-arm64@npm:0.25.1" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/freebsd-x64@npm:0.25.0" +"@esbuild/freebsd-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/freebsd-x64@npm:0.25.1" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-arm64@npm:0.25.0" +"@esbuild/linux-arm64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-arm64@npm:0.25.1" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-arm@npm:0.25.0" +"@esbuild/linux-arm@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-arm@npm:0.25.1" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-ia32@npm:0.25.0" +"@esbuild/linux-ia32@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-ia32@npm:0.25.1" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-loong64@npm:0.25.0" +"@esbuild/linux-loong64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-loong64@npm:0.25.1" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-mips64el@npm:0.25.0" +"@esbuild/linux-mips64el@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-mips64el@npm:0.25.1" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-ppc64@npm:0.25.0" +"@esbuild/linux-ppc64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-ppc64@npm:0.25.1" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-riscv64@npm:0.25.0" +"@esbuild/linux-riscv64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-riscv64@npm:0.25.1" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-s390x@npm:0.25.0" +"@esbuild/linux-s390x@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-s390x@npm:0.25.1" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-x64@npm:0.25.0" +"@esbuild/linux-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/linux-x64@npm:0.25.1" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/netbsd-arm64@npm:0.25.0" +"@esbuild/netbsd-arm64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/netbsd-arm64@npm:0.25.1" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/netbsd-x64@npm:0.25.0" +"@esbuild/netbsd-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/netbsd-x64@npm:0.25.1" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/openbsd-arm64@npm:0.25.0" +"@esbuild/openbsd-arm64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/openbsd-arm64@npm:0.25.1" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/openbsd-x64@npm:0.25.0" +"@esbuild/openbsd-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/openbsd-x64@npm:0.25.1" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/sunos-x64@npm:0.25.0" +"@esbuild/sunos-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/sunos-x64@npm:0.25.1" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/win32-arm64@npm:0.25.0" +"@esbuild/win32-arm64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/win32-arm64@npm:0.25.1" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/win32-ia32@npm:0.25.0" +"@esbuild/win32-ia32@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/win32-ia32@npm:0.25.1" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/win32-x64@npm:0.25.0" +"@esbuild/win32-x64@npm:0.25.1": + version: 0.25.1 + resolution: "@esbuild/win32-x64@npm:0.25.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -3998,135 +3998,142 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.34.9" +"@rollup/rollup-android-arm-eabi@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.37.0" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-android-arm64@npm:4.34.9" +"@rollup/rollup-android-arm64@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-android-arm64@npm:4.37.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-darwin-arm64@npm:4.34.9" +"@rollup/rollup-darwin-arm64@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.37.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-darwin-x64@npm:4.34.9" +"@rollup/rollup-darwin-x64@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.37.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.34.9" +"@rollup/rollup-freebsd-arm64@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.37.0" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-freebsd-x64@npm:4.34.9" +"@rollup/rollup-freebsd-x64@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-freebsd-x64@npm:4.37.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.34.9" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.37.0" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.34.9" +"@rollup/rollup-linux-arm-musleabihf@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.37.0" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.34.9" +"@rollup/rollup-linux-arm64-gnu@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.37.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.34.9" +"@rollup/rollup-linux-arm64-musl@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.37.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-loongarch64-gnu@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.34.9" +"@rollup/rollup-linux-loongarch64-gnu@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.37.0" conditions: os=linux & cpu=loong64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.34.9" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.37.0" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.34.9" +"@rollup/rollup-linux-riscv64-gnu@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.37.0" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.34.9" +"@rollup/rollup-linux-riscv64-musl@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.37.0" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.37.0" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.34.9" +"@rollup/rollup-linux-x64-gnu@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.37.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.34.9" +"@rollup/rollup-linux-x64-musl@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.37.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.34.9" +"@rollup/rollup-win32-arm64-msvc@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.37.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.34.9" +"@rollup/rollup-win32-ia32-msvc@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.37.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.34.9": - version: 4.34.9 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.34.9" +"@rollup/rollup-win32-x64-msvc@npm:4.37.0": + version: 4.37.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.37.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -7186,34 +7193,34 @@ __metadata: linkType: hard "esbuild@npm:^0.25.0": - version: 0.25.0 - resolution: "esbuild@npm:0.25.0" + version: 0.25.1 + resolution: "esbuild@npm:0.25.1" dependencies: - "@esbuild/aix-ppc64": "npm:0.25.0" - "@esbuild/android-arm": "npm:0.25.0" - "@esbuild/android-arm64": "npm:0.25.0" - "@esbuild/android-x64": "npm:0.25.0" - "@esbuild/darwin-arm64": "npm:0.25.0" - "@esbuild/darwin-x64": "npm:0.25.0" - "@esbuild/freebsd-arm64": "npm:0.25.0" - "@esbuild/freebsd-x64": "npm:0.25.0" - "@esbuild/linux-arm": "npm:0.25.0" - "@esbuild/linux-arm64": "npm:0.25.0" - "@esbuild/linux-ia32": "npm:0.25.0" - "@esbuild/linux-loong64": "npm:0.25.0" - "@esbuild/linux-mips64el": "npm:0.25.0" - "@esbuild/linux-ppc64": "npm:0.25.0" - "@esbuild/linux-riscv64": "npm:0.25.0" - "@esbuild/linux-s390x": "npm:0.25.0" - "@esbuild/linux-x64": "npm:0.25.0" - "@esbuild/netbsd-arm64": "npm:0.25.0" - "@esbuild/netbsd-x64": "npm:0.25.0" - "@esbuild/openbsd-arm64": "npm:0.25.0" - "@esbuild/openbsd-x64": "npm:0.25.0" - "@esbuild/sunos-x64": "npm:0.25.0" - "@esbuild/win32-arm64": "npm:0.25.0" - "@esbuild/win32-ia32": "npm:0.25.0" - "@esbuild/win32-x64": "npm:0.25.0" + "@esbuild/aix-ppc64": "npm:0.25.1" + "@esbuild/android-arm": "npm:0.25.1" + "@esbuild/android-arm64": "npm:0.25.1" + "@esbuild/android-x64": "npm:0.25.1" + "@esbuild/darwin-arm64": "npm:0.25.1" + "@esbuild/darwin-x64": "npm:0.25.1" + "@esbuild/freebsd-arm64": "npm:0.25.1" + "@esbuild/freebsd-x64": "npm:0.25.1" + "@esbuild/linux-arm": "npm:0.25.1" + "@esbuild/linux-arm64": "npm:0.25.1" + "@esbuild/linux-ia32": "npm:0.25.1" + "@esbuild/linux-loong64": "npm:0.25.1" + "@esbuild/linux-mips64el": "npm:0.25.1" + "@esbuild/linux-ppc64": "npm:0.25.1" + "@esbuild/linux-riscv64": "npm:0.25.1" + "@esbuild/linux-s390x": "npm:0.25.1" + "@esbuild/linux-x64": "npm:0.25.1" + "@esbuild/netbsd-arm64": "npm:0.25.1" + "@esbuild/netbsd-x64": "npm:0.25.1" + "@esbuild/openbsd-arm64": "npm:0.25.1" + "@esbuild/openbsd-x64": "npm:0.25.1" + "@esbuild/sunos-x64": "npm:0.25.1" + "@esbuild/win32-arm64": "npm:0.25.1" + "@esbuild/win32-ia32": "npm:0.25.1" + "@esbuild/win32-x64": "npm:0.25.1" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -7267,7 +7274,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10c0/5767b72da46da3cfec51661647ec850ddbf8a8d0662771139f10ef0692a8831396a0004b2be7966cecdb08264fb16bdc16290dcecd92396fac5f12d722fa013d + checksum: 10c0/80fca30dd0f21aec23fdfab34f0a8d5f55df5097dd7f475f2ab561d45662c32ee306f5649071cd1a0ba0614b164c48ca3dc3ee1551a4daf204b8af90e4d893f5 languageName: node linkType: hard @@ -9737,11 +9744,11 @@ __metadata: linkType: hard "nanoid@npm:^3.3.8": - version: 3.3.8 - resolution: "nanoid@npm:3.3.8" + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" bin: nanoid: bin/nanoid.cjs - checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120 + checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b languageName: node linkType: hard @@ -11483,28 +11490,29 @@ __metadata: linkType: hard "rollup@npm:^4.30.1": - version: 4.34.9 - resolution: "rollup@npm:4.34.9" + version: 4.37.0 + resolution: "rollup@npm:4.37.0" dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.34.9" - "@rollup/rollup-android-arm64": "npm:4.34.9" - "@rollup/rollup-darwin-arm64": "npm:4.34.9" - "@rollup/rollup-darwin-x64": "npm:4.34.9" - "@rollup/rollup-freebsd-arm64": "npm:4.34.9" - "@rollup/rollup-freebsd-x64": "npm:4.34.9" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.34.9" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.34.9" - "@rollup/rollup-linux-arm64-gnu": "npm:4.34.9" - "@rollup/rollup-linux-arm64-musl": "npm:4.34.9" - "@rollup/rollup-linux-loongarch64-gnu": "npm:4.34.9" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.34.9" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.34.9" - "@rollup/rollup-linux-s390x-gnu": "npm:4.34.9" - "@rollup/rollup-linux-x64-gnu": "npm:4.34.9" - "@rollup/rollup-linux-x64-musl": "npm:4.34.9" - "@rollup/rollup-win32-arm64-msvc": "npm:4.34.9" - "@rollup/rollup-win32-ia32-msvc": "npm:4.34.9" - "@rollup/rollup-win32-x64-msvc": "npm:4.34.9" + "@rollup/rollup-android-arm-eabi": "npm:4.37.0" + "@rollup/rollup-android-arm64": "npm:4.37.0" + "@rollup/rollup-darwin-arm64": "npm:4.37.0" + "@rollup/rollup-darwin-x64": "npm:4.37.0" + "@rollup/rollup-freebsd-arm64": "npm:4.37.0" + "@rollup/rollup-freebsd-x64": "npm:4.37.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.37.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.37.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.37.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.37.0" + "@rollup/rollup-linux-loongarch64-gnu": "npm:4.37.0" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.37.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.37.0" + "@rollup/rollup-linux-riscv64-musl": "npm:4.37.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.37.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.37.0" + "@rollup/rollup-linux-x64-musl": "npm:4.37.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.37.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.37.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.37.0" "@types/estree": "npm:1.0.6" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -11534,6 +11542,8 @@ __metadata: optional: true "@rollup/rollup-linux-riscv64-gnu": optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true "@rollup/rollup-linux-s390x-gnu": optional: true "@rollup/rollup-linux-x64-gnu": @@ -11550,7 +11560,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/dd0be1f7c4f8a93040026be13ecc39259fb55313db0dac7eafd97a3ac01ab4584e6b1a8afd86b0259dcf391699d5560a678abe6c0729af0aa4f2d5df70f05c8c + checksum: 10c0/2e00382e08938636edfe0a7547ea2eaa027205dc0b6ff85d8b82be0fbe55a4ef88a1995fee2a5059e33dbccf12d1376c236825353afb89c96298cc95c5160a46 languageName: node linkType: hard @@ -13091,8 +13101,8 @@ __metadata: linkType: hard "vite@npm:^5.0.0 || ^6.0.0, vite@npm:^6.0.0": - version: 6.2.0 - resolution: "vite@npm:6.2.0" + version: 6.2.3 + resolution: "vite@npm:6.2.3" dependencies: esbuild: "npm:^0.25.0" fsevents: "npm:~2.3.3" @@ -13138,7 +13148,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/db62c93d4a823e805c6f8429de035528b3c35cc7f6de4948b41e0528f94ed2ac55047d90f8534f626ef3a04e682883b570fe5ec9ee92f51bf0c3c210dbec5ac1 + checksum: 10c0/ba6ad7e83e5a63fb0b6f62d3a3963624b8784bdc1bfa2a83e16cf268fb58c76bd9f8e69f39ed34bf8711cdb8fd7702916f878781da53c232c34ef7a85e0600cf languageName: node linkType: hard