From 8c4a2b52ca98a5fa0c048ed94f1d9d1dc12605a4 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 9 Jun 2025 12:17:52 +0200 Subject: [PATCH 1/3] Fix diagnostics app reconnection. --- .../library/powersync/ConnectionManager.ts | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tools/diagnostics-app/src/library/powersync/ConnectionManager.ts b/tools/diagnostics-app/src/library/powersync/ConnectionManager.ts index 8a9a4a0a..2db1926f 100644 --- a/tools/diagnostics-app/src/library/powersync/ConnectionManager.ts +++ b/tools/diagnostics-app/src/library/powersync/ConnectionManager.ts @@ -44,18 +44,9 @@ export const db = new PowerSyncDatabase({ export const connector = new TokenConnector(); -const remote = new WebRemote(connector); const adapter = new RecordingStorageAdapter(db.database, schemaManager); -const syncOptions: WebStreamingSyncImplementationOptions = { - adapter, - remote, - uploadCrud: async () => { - // No-op - }, - identifier: 'diagnostics' -}; -export const sync = new WebStreamingSyncImplementation(syncOptions); +export let sync: WebStreamingSyncImplementation | undefined; export interface SyncErrorListener extends BaseListener { lastErrorUpdated?: ((error: Error) => void) | undefined; @@ -67,28 +58,39 @@ if (connector.hasCredentials()) { export async function connect() { const params = getParams(); + await sync?.disconnect(); + const remote = new WebRemote(connector); + const syncOptions: WebStreamingSyncImplementationOptions = { + adapter, + remote, + uploadCrud: async () => { + // No-op + }, + identifier: 'diagnostics' + }; + sync = new WebStreamingSyncImplementation(syncOptions); await sync.connect({ params }); if (!sync.syncStatus.connected) { const error = sync.syncStatus.dataFlowStatus.downloadError ?? new Error('Failed to connect'); // Disconnect but don't wait for it - sync.disconnect(); + await sync.disconnect(); throw error; } } export async function clearData() { - await sync.disconnect(); + await sync?.disconnect(); await db.disconnectAndClear(); await schemaManager.clear(); await schemaManager.refreshSchema(db.database); if (connector.hasCredentials()) { const params = getParams(); - await sync.connect({ params }); + await sync?.connect({ params }); } } export async function disconnect() { - await sync.disconnect(); + await sync?.disconnect(); } export async function signOut() { From c5c7a3fe4ef98101fe22f73e8aa163adb9d77ac0 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 9 Jun 2025 12:18:26 +0200 Subject: [PATCH 2/3] Add changeset. --- .changeset/silent-lions-cough.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silent-lions-cough.md diff --git a/.changeset/silent-lions-cough.md b/.changeset/silent-lions-cough.md new file mode 100644 index 00000000..f71a0704 --- /dev/null +++ b/.changeset/silent-lions-cough.md @@ -0,0 +1,5 @@ +--- +'@powersync/diagnostics-app': patch +--- + +Fix reconnecting after changing credentials. From fa59f26850d4f242d59d1c3aec91dcae6cd974dd Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 9 Jun 2025 12:23:09 +0200 Subject: [PATCH 3/3] Fix type errors. --- tools/diagnostics-app/src/app/views/layout.tsx | 6 +++--- tools/diagnostics-app/src/app/views/sync-diagnostics.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/diagnostics-app/src/app/views/layout.tsx b/tools/diagnostics-app/src/app/views/layout.tsx index 64531a11..f018cffd 100644 --- a/tools/diagnostics-app/src/app/views/layout.tsx +++ b/tools/diagnostics-app/src/app/views/layout.tsx @@ -43,7 +43,7 @@ export default function ViewsLayout({ children }: { children: React.ReactNode }) const powerSync = usePowerSync(); const navigate = useNavigate(); - const [syncStatus, setSyncStatus] = React.useState(sync.syncStatus); + const [syncStatus, setSyncStatus] = React.useState(sync?.syncStatus); const [syncError, setSyncError] = React.useState(null); const { title } = useNavigationPanel(); @@ -101,13 +101,13 @@ export default function ViewsLayout({ children }: { children: React.ReactNode }) // Cannot use `useStatus()`, since we're not using the default sync implementation. React.useEffect(() => { - const l = sync.registerListener({ + const l = sync?.registerListener({ statusChanged: (status) => { setSyncStatus(status); setSyncError(status.dataFlowStatus.downloadError ?? null); } }); - return () => l(); + return () => l?.(); }, []); const drawerWidth = 320; diff --git a/tools/diagnostics-app/src/app/views/sync-diagnostics.tsx b/tools/diagnostics-app/src/app/views/sync-diagnostics.tsx index 7772f533..abe82d19 100644 --- a/tools/diagnostics-app/src/app/views/sync-diagnostics.tsx +++ b/tools/diagnostics-app/src/app/views/sync-diagnostics.tsx @@ -82,7 +82,7 @@ export default function SyncDiagnosticsPage() { // Similar to db.currentState.hasSynced, but synchronized to the onChange events const { synced_at } = await db.get<{ synced_at: string | null }>('SELECT powersync_last_synced_at() as synced_at'); setlastSyncedAt(synced_at ? new Date(synced_at + 'Z') : null); - if (synced_at != null && !sync.syncStatus.dataFlowStatus.downloading) { + if (synced_at != null && !sync?.syncStatus.dataFlowStatus.downloading) { // These are potentially expensive queries - do not run during initial sync const bucketRows = await db.getAll(BUCKETS_QUERY); const tableRows = await db.getAll(TABLES_QUERY);