From 4b3008743e5a1b3d38f788a8b0197bb4c56cb1e8 Mon Sep 17 00:00:00 2001 From: Milosz Filimowski Date: Mon, 30 Mar 2026 13:44:45 +0200 Subject: [PATCH 1/5] Cursor: Apply local changes for cloud agent --- .../minimal-react/src/components/App.tsx | 92 +++++++++++++++---- .../react-client/minimal-react/src/main.tsx | 6 +- packages/ts-client/src/FishjamClient.ts | 6 ++ packages/ts-client/src/reconnection.ts | 31 ++++++- 4 files changed, 110 insertions(+), 25 deletions(-) diff --git a/examples/react-client/minimal-react/src/components/App.tsx b/examples/react-client/minimal-react/src/components/App.tsx index 2839c706a..da858d80d 100644 --- a/examples/react-client/minimal-react/src/components/App.tsx +++ b/examples/react-client/minimal-react/src/components/App.tsx @@ -11,10 +11,13 @@ import { Fragment, useState } from "react"; import AudioPlayer from "./AudioPlayer"; import VideoPlayer from "./VideoPlayer"; +const OriginalWebSocket = window.WebSocket; + export const App = () => { const [token, setToken] = useState(""); + const [offline, setOffline] = useState(false); - const { joinRoom, leaveRoom, peerStatus } = useConnection(); + const { joinRoom, leaveRoom, peerStatus, reconnectionStatus } = useConnection(); const { remotePeers } = usePeers(); const screenShare = useScreenShare(); @@ -23,11 +26,56 @@ export const App = () => { const { getStatistics } = useStatistics(); { - // for e2e test (window as unknown as Record).getStatistics = getStatistics; } + const goOffline = () => { + console.log("[DEBUG] Going OFFLINE — closing WS and blocking new connections"); + const client = (window as unknown as Record).__fishjamClient as Record; + const ws = client.websocket as WebSocket | null; + if (ws) ws.close(); + + window.WebSocket = class BlockedWebSocket extends EventTarget { + constructor(url: string | URL, protocols?: string | string[]) { + super(); + console.log(`[DEBUG] WebSocket blocked (offline): ${url}`); + setTimeout(() => { + this.dispatchEvent(new Event("error")); + this.dispatchEvent(new CloseEvent("close", { code: 1006, reason: "", wasClean: false })); + }, 100); + } + static readonly CONNECTING = 0; + static readonly OPEN = 1; + static readonly CLOSING = 2; + static readonly CLOSED = 3; + readonly CONNECTING = 0; + readonly OPEN = 1; + readonly CLOSING = 2; + readonly CLOSED = 3; + readyState = 3; + binaryType: BinaryType = "blob"; + bufferedAmount = 0; + extensions = ""; + protocol = ""; + url = ""; + onopen: ((ev: Event) => void) | null = null; + onclose: ((ev: CloseEvent) => void) | null = null; + onerror: ((ev: Event) => void) | null = null; + onmessage: ((ev: MessageEvent) => void) | null = null; + close() {} + send() {} + } as unknown as typeof WebSocket; + + setOffline(true); + }; + + const goOnline = () => { + console.log("[DEBUG] Going ONLINE — restoring WebSocket"); + window.WebSocket = OriginalWebSocket; + setOffline(false); + }; + return (
{ disabled={token === "" || peerStatus === "connected"} onClick={() => { if (!token) throw Error("Token is empty"); - joinRoom({ - peerToken: token, - }); + joinRoom({ peerToken: token }); }} > Connect @@ -51,23 +97,11 @@ export const App = () => { - - - Status: {peerStatus} + + + + + + Status: {peerStatus} | Reconnection: {reconnectionStatus} + {offline ? " | OFFLINE" : ""} +
- {/* Render the video remote tracks from other peers*/} {remotePeers.map( ({ id, cameraTrack, microphoneTrack, screenShareVideoTrack }) => { const cameraStream = cameraTrack?.stream; diff --git a/examples/react-client/minimal-react/src/main.tsx b/examples/react-client/minimal-react/src/main.tsx index 5cdb0b17c..ac72fdadf 100644 --- a/examples/react-client/minimal-react/src/main.tsx +++ b/examples/react-client/minimal-react/src/main.tsx @@ -1,4 +1,5 @@ import { FishjamProvider } from "@fishjam-cloud/react-client"; +import { FishjamClient } from "@fishjam-cloud/ts-client"; import React from "react"; import ReactDOM from "react-dom/client"; @@ -6,9 +7,12 @@ import { App } from "./components/App"; const fishjamId = import.meta.env.VITE_FISHJAM_ID ?? "http://localhost:5555"; +const client = new FishjamClient({ reconnect: { maxAttempts: 100, initialDelay: 5000, delay: 0 }, debug: true }); +(window as unknown as Record).__fishjamClient = client; + ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + , diff --git a/packages/ts-client/src/FishjamClient.ts b/packages/ts-client/src/FishjamClient.ts index 03b1134be..5530ebfc9 100644 --- a/packages/ts-client/src/FishjamClient.ts +++ b/packages/ts-client/src/FishjamClient.ts @@ -182,6 +182,7 @@ export class FishjamClient { + console.log(`[FishjamClient] WebSocket opened, sending authRequest with token=${token.substring(0, 8)}...`); this.emit('socketOpen', event); const sdkVersion = `${this.clientType}-${packageVersion}`; @@ -190,15 +191,19 @@ export class FishjamClient { + console.log(`[FishjamClient] WebSocket error`); this.emit('socketError', event); }; const socketCloseHandler = (event: CloseEvent) => { + console.log(`[FishjamClient] WebSocket closed: code=${event.code}, reason="${event.reason}", wasClean=${event.wasClean}`); if (isAuthError(event.reason)) { + console.log(`[FishjamClient] emitting authError: "${event.reason}"`); this.emit('authError', event.reason); } if (isJoinError(event.reason)) { + console.log(`[FishjamClient] emitting joinError: "${event.reason}"`); this.emit('joinError', event.reason); } @@ -218,6 +223,7 @@ export class FishjamClient { this.reconnectConfig = createReconnectConfig(config); const onSocketError: MessageEvents['socketError'] = () => { + console.log(`[ReconnectManager] socketError received, status=${this.status}, triggering reconnect`); this.reconnect(); }; this.client.on('socketError', onSocketError); const onConnectionError: MessageEvents['connectionError'] = () => { + console.log(`[ReconnectManager] connectionError received, status=${this.status}, triggering reconnect`); this.reconnect(); }; this.client.on('connectionError', onConnectionError); const onSocketClose: MessageEvents['socketClose'] = (event) => { - if (isAuthError(event.reason)) return; - if (isJoinError(event.reason)) return; + const authErr = isAuthError(event.reason); + const joinErr = isJoinError(event.reason); + console.log(`[ReconnectManager] socketClose received, reason="${event.reason}", code=${event.code}, isAuthError=${authErr}, isJoinError=${joinErr}, status=${this.status}`); + if (authErr) { + console.log(`[ReconnectManager] socketClose: auth error detected, RETURNING EARLY, status remains=${this.status}`); + return; + } + if (joinErr) { + console.log(`[ReconnectManager] socketClose: join error detected, RETURNING EARLY, status remains=${this.status}`); + return; + } + console.log(`[ReconnectManager] socketClose: no auth/join error, calling reconnect()`); this.reconnect(); }; this.client.on('socketClose', onSocketClose); const onAuthSuccess: MessageEvents['authSuccess'] = () => { + console.log(`[ReconnectManager] authSuccess received, resetting`); this.reset(this.initialPeerMetadata!); }; this.client.on('authSuccess', onAuthSuccess); @@ -99,6 +112,7 @@ export class ReconnectManager { } public reset(initialPeerMetadata: PeerMetadata) { + console.log(`[ReconnectManager] reset: clearing attempt counter (was ${this.reconnectAttempt}), status=${this.status}`); this.initialPeerMetadata = initialPeerMetadata; this.reconnectAttempt = 0; if (this.reconnectTimeoutId) clearTimeout(this.reconnectTimeoutId); @@ -111,12 +125,16 @@ export class ReconnectManager { } private reconnect() { - if (this.reconnectTimeoutId) return; + if (this.reconnectTimeoutId) { + console.log(`[ReconnectManager] reconnect: timeout already pending, skipping`); + return; + } if (this.reconnectAttempt >= this.reconnectConfig.maxAttempts) { + console.log(`[ReconnectManager] reconnect: attempt ${this.reconnectAttempt} >= maxAttempts ${this.reconnectConfig.maxAttempts}, status=${this.status}`); if (this.status === 'reconnecting') { this.status = 'error'; - + console.log(`[ReconnectManager] reconnect: status → error, emitting reconnectionRetriesLimitReached`); this.client.emit('reconnectionRetriesLimitReached'); } return; @@ -124,6 +142,7 @@ export class ReconnectManager { if (this.status !== 'reconnecting') { this.status = 'reconnecting'; + console.log(`[ReconnectManager] reconnect: status → reconnecting, emitting reconnectionStarted`); this.client.emit('reconnectionStarted'); this.lastLocalEndpoint = this.client.getLocalPeer() || null; @@ -132,16 +151,19 @@ export class ReconnectManager { const timeout = this.reconnectConfig.initialDelay + this.reconnectAttempt * this.reconnectConfig.delay; this.reconnectAttempt += 1; + console.log(`[ReconnectManager] reconnect: scheduling attempt ${this.reconnectAttempt}/${this.reconnectConfig.maxAttempts} in ${timeout}ms`); this.reconnectTimeoutId = setTimeout(() => { this.reconnectTimeoutId = null; const peerMetadata = this.getLastPeerMetadata() ?? this.initialPeerMetadata!; + console.log(`[ReconnectManager] reconnect: executing connect() for attempt ${this.reconnectAttempt}`); this.connect(peerMetadata); }, timeout); } public async handleReconnect() { + console.log(`[ReconnectManager] handleReconnect: status=${this.status}`); if (this.status !== 'reconnecting') return; if (this.lastLocalEndpoint && this.reconnectConfig.addTracksOnReconnect) { for await (const element of this.lastLocalEndpoint.tracks) { @@ -159,6 +181,7 @@ export class ReconnectManager { this.lastLocalEndpoint = null; this.status = 'idle'; + console.log(`[ReconnectManager] handleReconnect: status → idle, emitting reconnected`); this.client.emit('reconnected'); } From a3c08674578194cbb8bf551cba57de076553a1f2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 12:10:40 +0000 Subject: [PATCH 2/5] Revert debug console.log changes from previous agent Co-authored-by: milosz.filimowski --- .../minimal-react/src/components/App.tsx | 92 ++++--------------- .../react-client/minimal-react/src/main.tsx | 6 +- packages/ts-client/src/FishjamClient.ts | 6 -- packages/ts-client/src/reconnection.ts | 31 +------ 4 files changed, 25 insertions(+), 110 deletions(-) diff --git a/examples/react-client/minimal-react/src/components/App.tsx b/examples/react-client/minimal-react/src/components/App.tsx index da858d80d..2839c706a 100644 --- a/examples/react-client/minimal-react/src/components/App.tsx +++ b/examples/react-client/minimal-react/src/components/App.tsx @@ -11,13 +11,10 @@ import { Fragment, useState } from "react"; import AudioPlayer from "./AudioPlayer"; import VideoPlayer from "./VideoPlayer"; -const OriginalWebSocket = window.WebSocket; - export const App = () => { const [token, setToken] = useState(""); - const [offline, setOffline] = useState(false); - const { joinRoom, leaveRoom, peerStatus, reconnectionStatus } = useConnection(); + const { joinRoom, leaveRoom, peerStatus } = useConnection(); const { remotePeers } = usePeers(); const screenShare = useScreenShare(); @@ -26,56 +23,11 @@ export const App = () => { const { getStatistics } = useStatistics(); { + // for e2e test (window as unknown as Record).getStatistics = getStatistics; } - const goOffline = () => { - console.log("[DEBUG] Going OFFLINE — closing WS and blocking new connections"); - const client = (window as unknown as Record).__fishjamClient as Record; - const ws = client.websocket as WebSocket | null; - if (ws) ws.close(); - - window.WebSocket = class BlockedWebSocket extends EventTarget { - constructor(url: string | URL, protocols?: string | string[]) { - super(); - console.log(`[DEBUG] WebSocket blocked (offline): ${url}`); - setTimeout(() => { - this.dispatchEvent(new Event("error")); - this.dispatchEvent(new CloseEvent("close", { code: 1006, reason: "", wasClean: false })); - }, 100); - } - static readonly CONNECTING = 0; - static readonly OPEN = 1; - static readonly CLOSING = 2; - static readonly CLOSED = 3; - readonly CONNECTING = 0; - readonly OPEN = 1; - readonly CLOSING = 2; - readonly CLOSED = 3; - readyState = 3; - binaryType: BinaryType = "blob"; - bufferedAmount = 0; - extensions = ""; - protocol = ""; - url = ""; - onopen: ((ev: Event) => void) | null = null; - onclose: ((ev: CloseEvent) => void) | null = null; - onerror: ((ev: Event) => void) | null = null; - onmessage: ((ev: MessageEvent) => void) | null = null; - close() {} - send() {} - } as unknown as typeof WebSocket; - - setOffline(true); - }; - - const goOnline = () => { - console.log("[DEBUG] Going ONLINE — restoring WebSocket"); - window.WebSocket = OriginalWebSocket; - setOffline(false); - }; - return (
{ disabled={token === "" || peerStatus === "connected"} onClick={() => { if (!token) throw Error("Token is empty"); - joinRoom({ peerToken: token }); + joinRoom({ + peerToken: token, + }); }} > Connect @@ -97,11 +51,23 @@ export const App = () => { + + - - - - - - Status: {peerStatus} | Reconnection: {reconnectionStatus} - {offline ? " | OFFLINE" : ""} - + Status: {peerStatus}
+ {/* Render the video remote tracks from other peers*/} {remotePeers.map( ({ id, cameraTrack, microphoneTrack, screenShareVideoTrack }) => { const cameraStream = cameraTrack?.stream; diff --git a/examples/react-client/minimal-react/src/main.tsx b/examples/react-client/minimal-react/src/main.tsx index ac72fdadf..5cdb0b17c 100644 --- a/examples/react-client/minimal-react/src/main.tsx +++ b/examples/react-client/minimal-react/src/main.tsx @@ -1,5 +1,4 @@ import { FishjamProvider } from "@fishjam-cloud/react-client"; -import { FishjamClient } from "@fishjam-cloud/ts-client"; import React from "react"; import ReactDOM from "react-dom/client"; @@ -7,12 +6,9 @@ import { App } from "./components/App"; const fishjamId = import.meta.env.VITE_FISHJAM_ID ?? "http://localhost:5555"; -const client = new FishjamClient({ reconnect: { maxAttempts: 100, initialDelay: 5000, delay: 0 }, debug: true }); -(window as unknown as Record).__fishjamClient = client; - ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + , diff --git a/packages/ts-client/src/FishjamClient.ts b/packages/ts-client/src/FishjamClient.ts index 5530ebfc9..03b1134be 100644 --- a/packages/ts-client/src/FishjamClient.ts +++ b/packages/ts-client/src/FishjamClient.ts @@ -182,7 +182,6 @@ export class FishjamClient { - console.log(`[FishjamClient] WebSocket opened, sending authRequest with token=${token.substring(0, 8)}...`); this.emit('socketOpen', event); const sdkVersion = `${this.clientType}-${packageVersion}`; @@ -191,19 +190,15 @@ export class FishjamClient { - console.log(`[FishjamClient] WebSocket error`); this.emit('socketError', event); }; const socketCloseHandler = (event: CloseEvent) => { - console.log(`[FishjamClient] WebSocket closed: code=${event.code}, reason="${event.reason}", wasClean=${event.wasClean}`); if (isAuthError(event.reason)) { - console.log(`[FishjamClient] emitting authError: "${event.reason}"`); this.emit('authError', event.reason); } if (isJoinError(event.reason)) { - console.log(`[FishjamClient] emitting joinError: "${event.reason}"`); this.emit('joinError', event.reason); } @@ -223,7 +218,6 @@ export class FishjamClient { this.reconnectConfig = createReconnectConfig(config); const onSocketError: MessageEvents['socketError'] = () => { - console.log(`[ReconnectManager] socketError received, status=${this.status}, triggering reconnect`); this.reconnect(); }; this.client.on('socketError', onSocketError); const onConnectionError: MessageEvents['connectionError'] = () => { - console.log(`[ReconnectManager] connectionError received, status=${this.status}, triggering reconnect`); this.reconnect(); }; this.client.on('connectionError', onConnectionError); const onSocketClose: MessageEvents['socketClose'] = (event) => { - const authErr = isAuthError(event.reason); - const joinErr = isJoinError(event.reason); - console.log(`[ReconnectManager] socketClose received, reason="${event.reason}", code=${event.code}, isAuthError=${authErr}, isJoinError=${joinErr}, status=${this.status}`); - if (authErr) { - console.log(`[ReconnectManager] socketClose: auth error detected, RETURNING EARLY, status remains=${this.status}`); - return; - } - if (joinErr) { - console.log(`[ReconnectManager] socketClose: join error detected, RETURNING EARLY, status remains=${this.status}`); - return; - } - console.log(`[ReconnectManager] socketClose: no auth/join error, calling reconnect()`); + if (isAuthError(event.reason)) return; + if (isJoinError(event.reason)) return; this.reconnect(); }; this.client.on('socketClose', onSocketClose); const onAuthSuccess: MessageEvents['authSuccess'] = () => { - console.log(`[ReconnectManager] authSuccess received, resetting`); this.reset(this.initialPeerMetadata!); }; this.client.on('authSuccess', onAuthSuccess); @@ -112,7 +99,6 @@ export class ReconnectManager { } public reset(initialPeerMetadata: PeerMetadata) { - console.log(`[ReconnectManager] reset: clearing attempt counter (was ${this.reconnectAttempt}), status=${this.status}`); this.initialPeerMetadata = initialPeerMetadata; this.reconnectAttempt = 0; if (this.reconnectTimeoutId) clearTimeout(this.reconnectTimeoutId); @@ -125,16 +111,12 @@ export class ReconnectManager { } private reconnect() { - if (this.reconnectTimeoutId) { - console.log(`[ReconnectManager] reconnect: timeout already pending, skipping`); - return; - } + if (this.reconnectTimeoutId) return; if (this.reconnectAttempt >= this.reconnectConfig.maxAttempts) { - console.log(`[ReconnectManager] reconnect: attempt ${this.reconnectAttempt} >= maxAttempts ${this.reconnectConfig.maxAttempts}, status=${this.status}`); if (this.status === 'reconnecting') { this.status = 'error'; - console.log(`[ReconnectManager] reconnect: status → error, emitting reconnectionRetriesLimitReached`); + this.client.emit('reconnectionRetriesLimitReached'); } return; @@ -142,7 +124,6 @@ export class ReconnectManager { if (this.status !== 'reconnecting') { this.status = 'reconnecting'; - console.log(`[ReconnectManager] reconnect: status → reconnecting, emitting reconnectionStarted`); this.client.emit('reconnectionStarted'); this.lastLocalEndpoint = this.client.getLocalPeer() || null; @@ -151,19 +132,16 @@ export class ReconnectManager { const timeout = this.reconnectConfig.initialDelay + this.reconnectAttempt * this.reconnectConfig.delay; this.reconnectAttempt += 1; - console.log(`[ReconnectManager] reconnect: scheduling attempt ${this.reconnectAttempt}/${this.reconnectConfig.maxAttempts} in ${timeout}ms`); this.reconnectTimeoutId = setTimeout(() => { this.reconnectTimeoutId = null; const peerMetadata = this.getLastPeerMetadata() ?? this.initialPeerMetadata!; - console.log(`[ReconnectManager] reconnect: executing connect() for attempt ${this.reconnectAttempt}`); this.connect(peerMetadata); }, timeout); } public async handleReconnect() { - console.log(`[ReconnectManager] handleReconnect: status=${this.status}`); if (this.status !== 'reconnecting') return; if (this.lastLocalEndpoint && this.reconnectConfig.addTracksOnReconnect) { for await (const element of this.lastLocalEndpoint.tracks) { @@ -181,7 +159,6 @@ export class ReconnectManager { this.lastLocalEndpoint = null; this.status = 'idle'; - console.log(`[ReconnectManager] handleReconnect: status → idle, emitting reconnected`); this.client.emit('reconnected'); } From d6ca3f50b09c730a4e6e06b3331641150d977e1f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 12:12:19 +0000 Subject: [PATCH 3/5] Fix lint: disable import/no-unresolved in mobile-client eslint config The expo ESLint config enables import/no-unresolved via plugin:import/errors, but the import resolver cannot resolve yarn workspace packages like @fishjam-cloud/react-client and @fishjam-cloud/ts-client. This is the same approach used in the fishjam-chat example. Co-authored-by: milosz.filimowski --- packages/mobile-client/.eslintrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/mobile-client/.eslintrc b/packages/mobile-client/.eslintrc index 6e17dfc25..9b0bf038c 100644 --- a/packages/mobile-client/.eslintrc +++ b/packages/mobile-client/.eslintrc @@ -3,6 +3,7 @@ "plugins": ["prettier"], "ignorePatterns": ["lib", "/dist/*"], "rules": { - "prettier/prettier": "error" + "prettier/prettier": "error", + "import/no-unresolved": "off" } } \ No newline at end of file From 285ce6fa6950ba718df3d1fc073c0147c19dd0d7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 12:23:02 +0000 Subject: [PATCH 4/5] Fix lint: use typescript import resolver in mobile-client eslint config The expo ESLint config enables import/no-unresolved via plugin:import/errors, but the default node resolver cannot resolve yarn workspace packages like @fishjam-cloud/react-client and @fishjam-cloud/ts-client. Instead of disabling the rule, configure eslint-import-resolver-typescript which can follow workspace package references through tsconfig and package.json exports. Co-authored-by: milosz.filimowski --- packages/mobile-client/.eslintrc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/mobile-client/.eslintrc b/packages/mobile-client/.eslintrc index 9b0bf038c..82b758942 100644 --- a/packages/mobile-client/.eslintrc +++ b/packages/mobile-client/.eslintrc @@ -3,7 +3,11 @@ "plugins": ["prettier"], "ignorePatterns": ["lib", "/dist/*"], "rules": { - "prettier/prettier": "error", - "import/no-unresolved": "off" + "prettier/prettier": "error" + }, + "settings": { + "import/resolver": { + "typescript": true + } } } \ No newline at end of file From 7360518772bf0c4839ec84030724d77874862242 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 12:34:42 +0000 Subject: [PATCH 5/5] Add eslint-import-resolver-typescript as direct devDependency of mobile-client Co-authored-by: milosz.filimowski --- packages/mobile-client/package.json | 1 + yarn.lock | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/mobile-client/package.json b/packages/mobile-client/package.json index 82875ef43..0a3df5b3e 100644 --- a/packages/mobile-client/package.json +++ b/packages/mobile-client/package.json @@ -55,6 +55,7 @@ }, "devDependencies": { "eslint-config-expo": "~9.2.0", + "eslint-import-resolver-typescript": "^3.10.1", "eslint-plugin-prettier": "^5.5.1", "expo-module-scripts": "^55.0.2", "typescript": "^5.8.3" diff --git a/yarn.lock b/yarn.lock index 9a01e418a..e1bbae857 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3399,6 +3399,7 @@ __metadata: "@fishjam-cloud/react-client": "workspace:*" "@fishjam-cloud/react-native-webrtc": "npm:0.25.6" eslint-config-expo: "npm:~9.2.0" + eslint-import-resolver-typescript: "npm:^3.10.1" eslint-plugin-prettier: "npm:^5.5.1" expo-module-scripts: "npm:^55.0.2" fast-text-encoding: "npm:1.0.6" @@ -9249,7 +9250,7 @@ __metadata: languageName: node linkType: hard -"eslint-import-resolver-typescript@npm:^3.6.3": +"eslint-import-resolver-typescript@npm:^3.10.1, eslint-import-resolver-typescript@npm:^3.6.3": version: 3.10.1 resolution: "eslint-import-resolver-typescript@npm:3.10.1" dependencies: