Skip to content

Commit 9297564

Browse files
Merge remote-tracking branch 'origin/main' into triggers
2 parents 8999b78 + 6b38551 commit 9297564

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+788
-172
lines changed

.changeset/bright-yaks-pump.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@powersync/common': patch
3+
'@powersync/react-native': patch
4+
'@powersync/web': patch
5+
---
6+
7+
Fixed bug where a WebSocket connection timeout could cause an uncaught exception.

.changeset/curly-bugs-teach.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/web': minor
3+
---
4+
5+
Export AsyncDatabaseConnection (and related) types for internal use

.changeset/mighty-doors-judge.md

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
---
2-
'@powersync/react-native': patch
32
'@powersync/common': patch
4-
'@powersync/web': patch
53
'@powersync/node': patch
4+
'@powersync/react': patch
5+
'@powersync/react-native': patch
6+
'@powersync/web': patch
67
---
78

8-
Fix sync stream delays during CRUD upload.
9+
Fix a warning about raw tables being used when they're not.

demos/react-supabase-todolist/src/app/views/layout.tsx

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { LOGIN_ROUTE, SQL_CONSOLE_ROUTE, TODO_LISTS_ROUTE } from '@/app/router';
2+
import { useNavigationPanel } from '@/components/navigation/NavigationPanelContext';
3+
import { useSupabase } from '@/components/providers/SystemProvider';
14
import ChecklistRtlIcon from '@mui/icons-material/ChecklistRtl';
25
import ExitToAppIcon from '@mui/icons-material/ExitToApp';
36
import MenuIcon from '@mui/icons-material/Menu';
@@ -17,16 +20,15 @@ import {
1720
ListItemButton,
1821
ListItemIcon,
1922
ListItemText,
23+
Menu,
24+
MenuItem,
2025
Toolbar,
2126
Typography,
2227
styled
2328
} from '@mui/material';
24-
import React from 'react';
2529
import { usePowerSync, useStatus } from '@powersync/react';
30+
import React from 'react';
2631
import { useNavigate } from 'react-router-dom';
27-
import { useSupabase } from '@/components/providers/SystemProvider';
28-
import { useNavigationPanel } from '@/components/navigation/NavigationPanelContext';
29-
import { LOGIN_ROUTE, SQL_CONSOLE_ROUTE, TODO_LISTS_ROUTE } from '@/app/router';
3032

3133
export default function ViewsLayout({ children }: { children: React.ReactNode }) {
3234
const powerSync = usePowerSync();
@@ -37,6 +39,8 @@ export default function ViewsLayout({ children }: { children: React.ReactNode })
3739
const [openDrawer, setOpenDrawer] = React.useState(false);
3840
const { title } = useNavigationPanel();
3941

42+
const [connectionAnchor, setConnectionAnchor] = React.useState<null | HTMLElement>(null);
43+
4044
const NAVIGATION_ITEMS = React.useMemo(
4145
() => [
4246
{
@@ -72,16 +76,45 @@ export default function ViewsLayout({ children }: { children: React.ReactNode })
7276
color="inherit"
7377
aria-label="menu"
7478
sx={{ mr: 2 }}
75-
onClick={() => setOpenDrawer(!openDrawer)}
76-
>
79+
onClick={() => setOpenDrawer(!openDrawer)}>
7780
<MenuIcon />
7881
</IconButton>
7982
<Box sx={{ flexGrow: 1 }}>
8083
<Typography>{title}</Typography>
8184
</Box>
8285
<NorthIcon sx={{ marginRight: '-10px' }} color={status?.dataFlowStatus.uploading ? 'primary' : 'inherit'} />
8386
<SouthIcon color={status?.dataFlowStatus.downloading ? 'primary' : 'inherit'} />
84-
{status?.connected ? <WifiIcon /> : <SignalWifiOffIcon />}
87+
<Box
88+
sx={{ cursor: 'pointer' }}
89+
onClick={(event) => {
90+
setConnectionAnchor(event.currentTarget);
91+
}}>
92+
{status?.connected ? <WifiIcon /> : <SignalWifiOffIcon />}
93+
</Box>
94+
{/* Allows for manual connection and disconnect for testing purposes */}
95+
<Menu
96+
id="connection-menu"
97+
anchorEl={connectionAnchor}
98+
open={Boolean(connectionAnchor)}
99+
onClose={() => setConnectionAnchor(null)}>
100+
{status?.connected || status?.connecting ? (
101+
<MenuItem
102+
onClick={(event) => {
103+
setConnectionAnchor(null);
104+
powerSync.disconnect();
105+
}}>
106+
Disconnect
107+
</MenuItem>
108+
) : supabase ? (
109+
<MenuItem
110+
onClick={(event) => {
111+
setConnectionAnchor(null);
112+
powerSync.connect(supabase);
113+
}}>
114+
Connect
115+
</MenuItem>
116+
) : null}
117+
</Menu>
85118
</Toolbar>
86119
</S.TopBar>
87120
<Drawer anchor={'left'} open={openDrawer} onClose={() => setOpenDrawer(false)}>
@@ -95,8 +128,7 @@ export default function ViewsLayout({ children }: { children: React.ReactNode })
95128
await item.beforeNavigate?.();
96129
navigate(item.path);
97130
setOpenDrawer(false);
98-
}}
99-
>
131+
}}>
100132
<ListItemIcon>{item.icon()}</ListItemIcon>
101133
<ListItemText primary={item.title} />
102134
</ListItemButton>

packages/attachments/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"test": "pnpm build && vitest"
3030
},
3131
"peerDependencies": {
32-
"@powersync/common": "workspace:^1.33.2"
32+
"@powersync/common": "workspace:^1.34.0"
3333
},
3434
"devDependencies": {
3535
"@powersync/common": "workspace:*",

packages/common/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# @powersync/common
22

3+
## 1.34.0
4+
5+
### Minor Changes
6+
7+
- ab33799: Add experimental support for raw tables, giving you full control over the table structure to sync into.
8+
While PowerSync manages tables as JSON views by default, raw tables have to be created by the application
9+
developer.
10+
11+
For more information about raw tables, see [the documentation](https://docs.powersync.com/usage/use-case-examples/raw-tables).
12+
13+
- 810c6ad: Propagate logger from PowerSyncDatabase to streaming sync and remote implementations, and tweak some log messages.
14+
15+
### Patch Changes
16+
17+
- a1aa18c: Fix sync stream delays during CRUD upload.
18+
- 9fb898d: [Internal] Removed shared mutex implementation of `readLock` and `writeLock`.
19+
320
## 1.33.2
421

522
### Patch Changes

packages/common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@powersync/common",
3-
"version": "1.33.2",
3+
"version": "1.34.0",
44
"publishConfig": {
55
"registry": "https://registry.npmjs.org/",
66
"access": "public"

packages/common/src/client/AbstractPowerSyncDatabase.ts

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { Schema } from '../db/schema/Schema.js';
1616
import { BaseObserver } from '../utils/BaseObserver.js';
1717
import { ControlledExecutor } from '../utils/ControlledExecutor.js';
1818
import { throttleTrailing } from '../utils/async.js';
19-
import { mutexRunExclusive } from '../utils/mutex.js';
2019
import { ConnectionManager } from './ConnectionManager.js';
2120
import { SQLOpenFactory, SQLOpenOptions, isDBAdapter, isSQLOpenFactory, isSQLOpenOptions } from './SQLOpenFactory.js';
2221
import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector.js';
@@ -28,6 +27,7 @@ import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
2827
import {
2928
DEFAULT_CRUD_UPLOAD_THROTTLE_MS,
3029
DEFAULT_RETRY_DELAY_MS,
30+
InternalConnectionOptions,
3131
StreamingSyncImplementation,
3232
StreamingSyncImplementationListener,
3333
type AdditionalConnectionOptions,
@@ -129,7 +129,6 @@ export const DEFAULT_WATCH_THROTTLE_MS = 30;
129129

130130
export const DEFAULT_POWERSYNC_DB_OPTIONS = {
131131
retryDelayMs: 5000,
132-
logger: Logger.get('PowerSyncDatabase'),
133132
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
134133
};
135134

@@ -151,12 +150,6 @@ export const isPowerSyncDatabaseOptionsWithSettings = (test: any): test is Power
151150
};
152151

153152
export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDBListener> {
154-
/**
155-
* Transactions should be queued in the DBAdapter, but we also want to prevent
156-
* calls to `.execute` while an async transaction is running.
157-
*/
158-
protected static transactionMutex: Mutex = new Mutex();
159-
160153
/**
161154
* Returns true if the connection is closed.
162155
*/
@@ -188,6 +181,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
188181
* Allows creating SQLite triggers which can be used to track various operations on SQLite tables.
189182
*/
190183
readonly triggers: TriggerManager;
184+
logger: ILogger;
191185

192186
constructor(options: PowerSyncDatabaseOptionsWithDBAdapter);
193187
constructor(options: PowerSyncDatabaseOptionsWithOpenFactory);
@@ -212,6 +206,8 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
212206
throw new Error('The provided `database` option is invalid.');
213207
}
214208

209+
this.logger = options.logger ?? Logger.get(`PowerSyncDatabase[${this._database.name}]`);
210+
215211
this.bucketStorageAdapter = this.generateBucketStorageAdapter();
216212
this.closed = false;
217213
this.currentStatus = new SyncStatus({});
@@ -439,7 +435,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
439435
try {
440436
schema.validate();
441437
} catch (ex) {
442-
this.options.logger?.warn('Schema validation failed. Unexpected behaviour could occur', ex);
438+
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
443439
}
444440
this._schema = schema;
445441

@@ -448,10 +444,6 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
448444
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
449445
}
450446

451-
get logger() {
452-
return this.options.logger!;
453-
}
454-
455447
/**
456448
* Wait for initialization to complete.
457449
* While initializing is automatic, this helps to catch and report initialization errors.
@@ -483,7 +475,10 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
483475
* Connects to stream of events from the PowerSync instance.
484476
*/
485477
async connect(connector: PowerSyncBackendConnector, options?: PowerSyncConnectionOptions) {
486-
return this.connectionManager.connect(connector, options);
478+
const resolvedOptions: InternalConnectionOptions = options ?? {};
479+
resolvedOptions.serializedSchema = this.schema.toJSON();
480+
481+
return this.connectionManager.connect(connector, resolvedOptions);
487482
}
488483

489484
/**
@@ -692,8 +687,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
692687
* @returns The query result as an object with structured key-value pairs
693688
*/
694689
async execute(sql: string, parameters?: any[]) {
695-
await this.waitForReady();
696-
return this.database.execute(sql, parameters);
690+
return this.writeLock((tx) => tx.execute(sql, parameters));
697691
}
698692

699693
/**
@@ -767,7 +761,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
767761
*/
768762
async readLock<T>(callback: (db: DBAdapter) => Promise<T>) {
769763
await this.waitForReady();
770-
return mutexRunExclusive(AbstractPowerSyncDatabase.transactionMutex, () => callback(this.database));
764+
return this.database.readLock(callback);
771765
}
772766

773767
/**
@@ -776,10 +770,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
776770
*/
777771
async writeLock<T>(callback: (db: DBAdapter) => Promise<T>) {
778772
await this.waitForReady();
779-
return mutexRunExclusive(AbstractPowerSyncDatabase.transactionMutex, async () => {
780-
const res = await callback(this.database);
781-
return res;
782-
});
773+
return this.database.writeLock(callback);
783774
}
784775

785776
/**
@@ -897,7 +888,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
897888
* @param options Options for configuring watch behavior
898889
*/
899890
watchWithCallback(sql: string, parameters?: any[], handler?: WatchHandler, options?: SQLWatchOptions): void {
900-
const { onResult, onError = (e: Error) => this.options.logger?.error(e) } = handler ?? {};
891+
const { onResult, onError = (e: Error) => this.logger.error(e) } = handler ?? {};
901892
if (!onResult) {
902893
throw new Error('onResult is required');
903894
}
@@ -1052,7 +1043,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
10521043
* @returns A dispose function to stop watching for changes
10531044
*/
10541045
onChangeWithCallback(handler?: WatchOnChangeHandler, options?: SQLWatchOptions): () => void {
1055-
const { onChange, onError = (e: Error) => this.options.logger?.error(e) } = handler ?? {};
1046+
const { onChange, onError = (e: Error) => this.logger.error(e) } = handler ?? {};
10561047
if (!onChange) {
10571048
throw new Error('onChange is required');
10581049
}

packages/common/src/client/ConnectionManager.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ILogger } from 'js-logger';
22
import { BaseListener, BaseObserver } from '../utils/BaseObserver.js';
33
import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector.js';
44
import {
5-
PowerSyncConnectionOptions,
5+
InternalConnectionOptions,
66
StreamingSyncImplementation
77
} from './sync/stream/AbstractStreamingSyncImplementation.js';
88

@@ -24,14 +24,14 @@ export interface ConnectionManagerSyncImplementationResult {
2424
export interface ConnectionManagerOptions {
2525
createSyncImplementation(
2626
connector: PowerSyncBackendConnector,
27-
options: PowerSyncConnectionOptions
27+
options: InternalConnectionOptions
2828
): Promise<ConnectionManagerSyncImplementationResult>;
2929
logger: ILogger;
3030
}
3131

3232
type StoredConnectionOptions = {
3333
connector: PowerSyncBackendConnector;
34-
options: PowerSyncConnectionOptions;
34+
options: InternalConnectionOptions;
3535
};
3636

3737
/**
@@ -95,7 +95,7 @@ export class ConnectionManager extends BaseObserver<ConnectionManagerListener> {
9595
await this.syncDisposer?.();
9696
}
9797

98-
async connect(connector: PowerSyncBackendConnector, options?: PowerSyncConnectionOptions) {
98+
async connect(connector: PowerSyncBackendConnector, options: InternalConnectionOptions) {
9999
// Keep track if there were pending operations before this call
100100
const hadPendingOptions = !!this.pendingConnectionOptions;
101101

@@ -140,7 +140,7 @@ export class ConnectionManager extends BaseObserver<ConnectionManagerListener> {
140140
}
141141

142142
protected async connectInternal() {
143-
let appliedOptions: PowerSyncConnectionOptions | null = null;
143+
let appliedOptions: InternalConnectionOptions | null = null;
144144

145145
// This method ensures a disconnect before any connection attempt
146146
await this.disconnectInternal();

0 commit comments

Comments
 (0)