Skip to content

Commit 62e43aa

Browse files
[Fix] Improve bson usage (#172)
1 parent 6b01811 commit 62e43aa

File tree

22 files changed

+339
-742
lines changed

22 files changed

+339
-742
lines changed

.changeset/many-chefs-hug.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@powersync/react-native': minor
3+
'@powersync/common': minor
4+
'@powersync/web': minor
5+
---
6+
7+
Improved import and usage of BSON library.

demos/example-electron/vite.renderer.config.ts

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export default defineConfig((env) => {
3030
'@powersync/web > js-logger',
3131
'@powersync/web > lodash/throttle',
3232
'@powersync/web > can-ndjson-stream',
33-
'@powersync/web > bson',
3433
'@powersync/web > buffer',
3534
'@powersync/web > rsocket-core',
3635
'@powersync/web > rsocket-websocket-client',

demos/example-nextjs/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
"lowlight": "^2.9.0",
3131
"next": "14.1.0",
3232
"next-images": "1.8.5",
33-
"react": "^18.2.0",
34-
"react-dom": "^18.2.0",
33+
"react": "18.2.0",
34+
"react-dom": "18.2.0",
3535
"remixicon": "^2.5.0",
3636
"shiki": "^0.10.1",
3737
"simplify-js": "^1.2.4"

demos/example-vite/src/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { Column, ColumnType, WASQLitePowerSyncDatabaseOpenFactory, Schema, Table } from '@powersync/web';
2+
import Logger from 'js-logger';
3+
4+
Logger.useDefaults();
25

36
/**
47
* A placeholder connector which doesn't do anything.

demos/example-vite/vite.config.ts

-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import wasm from 'vite-plugin-wasm';
22
import topLevelAwait from 'vite-plugin-top-level-await';
3-
43
import { defineConfig } from 'vite';
54

65
// https://vitejs.dev/config/
@@ -13,12 +12,6 @@ export default defineConfig({
1312
},
1413
emptyOutDir: true
1514
},
16-
resolve: {
17-
alias: [
18-
// https://jira.mongodb.org/browse/NODE-5773
19-
{ find: 'bson', replacement: require.resolve('bson') }
20-
]
21-
},
2215
envDir: '..', // Use this dir for env vars, not 'src'.
2316
optimizeDeps: {
2417
// Don't optimize these packages as they contain web workers and WASM files.
@@ -29,7 +22,6 @@ export default defineConfig({
2922
'@powersync/web > js-logger',
3023
'@powersync/web > lodash/throttle',
3124
'@powersync/web > can-ndjson-stream',
32-
'@powersync/web > bson',
3325
'@powersync/web > buffer',
3426
'@powersync/web > rsocket-core',
3527
'@powersync/web > rsocket-websocket-client',

demos/example-webpack/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
"@powersync/web": "workspace:*"
1212
},
1313
"devDependencies": {
14-
"webpack-cli": "^5.1.4",
15-
"webpack": "^5.90.1",
1614
"@types/webpack": "^5.28.5",
15+
"html-webpack-plugin": "^5.6.0",
1716
"serve": "^14.2.1",
18-
"html-webpack-plugin": "^5.6.0"
17+
"webpack": "^5.90.1",
18+
"webpack-cli": "^5.1.4"
1919
}
2020
}

demos/example-webpack/src/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { Column, ColumnType, WASQLitePowerSyncDatabaseOpenFactory, Schema, Table } from '@powersync/web';
2+
import Logger from 'js-logger';
3+
4+
Logger.useDefaults();
25

36
/**
47
* A placeholder connector which doesn't do anything.

demos/example-webpack/webpack.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ module.exports = {
77
filename: 'index.js',
88
path: path.join(__dirname, 'dist')
99
},
10+
externals: {
11+
// BSON includes imports to these, but does not have a hard requirement for them to be present.
12+
crypto: 'Crypto',
13+
stream: 'Stream',
14+
vm: 'VM'
15+
},
1016
devtool: 'source-map',
1117
mode: 'development',
1218
resolve: {

demos/react-supabase-todolist/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
"dependencies": {
1212
"@powersync/react": "workspace:*",
1313
"@powersync/web": "workspace:*",
14+
"@emotion/react": "11.11.4",
15+
"@emotion/styled": "11.11.5",
1416
"@journeyapps/wa-sqlite": "~0.2.0",
1517
"@mui/icons-material": "^5.15.12",
1618
"@mui/material": "^5.15.12",
1719
"@mui/x-data-grid": "^6.19.6",
1820
"@supabase/supabase-js": "^2.39.7",
1921
"buffer": "^6.0.3",
22+
"formik": "^2.4.6",
2023
"js-logger": "^1.6.1",
2124
"lodash": "^4.17.21",
2225
"react": "^18.2.0",

demos/react-supabase-todolist/vite.config.mts

+1-8
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import { fileURLToPath, URL } from 'url';
55
import { defineConfig } from 'vite';
66
import react from '@vitejs/plugin-react';
77
import { VitePWA } from 'vite-plugin-pwa';
8-
import { createRequire } from 'node:module';
9-
const require = createRequire(import.meta.url); // Needed since the config file is also an ES module
108

119
// https://vitejs.dev/config/
1210
export default defineConfig({
@@ -19,11 +17,7 @@ export default defineConfig({
1917
emptyOutDir: true
2018
},
2119
resolve: {
22-
alias: [
23-
{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) },
24-
// https://jira.mongodb.org/browse/NODE-5773
25-
{ find: 'bson', replacement: require.resolve('bson') }
26-
]
20+
alias: [{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }]
2721
},
2822
publicDir: '../public',
2923
envDir: '..', // Use this dir for env vars, not 'src'.
@@ -36,7 +30,6 @@ export default defineConfig({
3630
'@powersync/web > js-logger',
3731
'@powersync/web > lodash/throttle',
3832
'@powersync/web > can-ndjson-stream',
39-
'@powersync/web > bson',
4033
'@powersync/web > buffer',
4134
'@powersync/web > rsocket-core',
4235
'@powersync/web > rsocket-websocket-client',

demos/vue-supabase-todolist/vite.config.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,7 @@ export default defineConfig({
7070
],
7171
define: { 'process.env': {} },
7272
resolve: {
73-
alias: [
74-
{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) },
75-
// https://jira.mongodb.org/browse/NODE-5773
76-
{ find: 'bson', replacement: require.resolve('bson') }
77-
],
73+
alias: [{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }],
7874
extensions: ['.js', '.json', '.jsx', '.mjs', '.ts', '.tsx', '.vue']
7975
},
8076
optimizeDeps: {
@@ -86,7 +82,6 @@ export default defineConfig({
8682
'@powersync/web > js-logger',
8783
'@powersync/web > lodash/throttle',
8884
'@powersync/web > can-ndjson-stream',
89-
'@powersync/web > bson',
9085
'@powersync/web > buffer',
9186
'@powersync/web > rsocket-core',
9287
'@powersync/web > rsocket-websocket-client',

demos/yjs-react-supabase-text-collab/vite.config.mts

-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export default defineConfig({
3838
'@powersync/web > js-logger',
3939
'@powersync/web > lodash/throttle',
4040
'@powersync/web > can-ndjson-stream',
41-
'@powersync/web > bson',
4241
'@powersync/web > buffer',
4342
'@powersync/web > rsocket-core',
4443
'@powersync/web > rsocket-websocket-client',

packages/common/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
},
2929
"dependencies": {
3030
"async-mutex": "^0.4.0",
31-
"bson": "^6.6.0",
3231
"buffer": "^6.0.3",
3332
"can-ndjson-stream": "^1.0.2",
3433
"cross-fetch": "^4.0.0",
@@ -43,6 +42,7 @@
4342
"@types/node": "^20.5.9",
4443
"@types/uuid": "^9.0.1",
4544
"typescript": "^5.1.3",
46-
"vitest": "^1.5.2"
45+
"vitest": "^1.5.2",
46+
"bson": "^6.6.0"
4747
}
4848
}

packages/common/src/client/sync/stream/AbstractRemote.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import { DataStream } from '../../../utils/DataStream';
66
import ndjsonStream from 'can-ndjson-stream';
77
import { RSocket, RSocketConnector, Requestable } from 'rsocket-core';
88
import { WebsocketClientTransport } from 'rsocket-websocket-client';
9-
import { serialize, deserialize } from 'bson';
9+
import type { BSON } from 'bson';
1010
import { AbortOperation } from '../../../utils/AbortOperation';
1111
import { Buffer } from 'buffer';
1212

13+
export type BSONImplementation = typeof BSON;
14+
1315
export type RemoteConnector = {
1416
fetchCredentials: () => Promise<PowerSyncCredentials | null>;
1517
};
@@ -171,13 +173,20 @@ export abstract class AbstractRemote {
171173
return res;
172174
}
173175

176+
/**
177+
* Provides a BSON implementation. The import nature of this varies depending on the platform
178+
*/
179+
abstract getBSON(): Promise<BSONImplementation>;
180+
174181
/**
175182
* Connects to the sync/stream websocket endpoint
176183
*/
177184
async socketStream(options: SyncStreamOptions): Promise<DataStream<StreamingSyncLine>> {
178185
const { path } = options;
179186
const request = await this.buildRequest(path);
180187

188+
const bson = await this.getBSON();
189+
181190
const connector = new RSocketConnector({
182191
transport: new WebsocketClientTransport({
183192
url: this.options.socketUrlTransformer(request.url)
@@ -190,7 +199,7 @@ export abstract class AbstractRemote {
190199
payload: {
191200
data: null,
192201
metadata: Buffer.from(
193-
serialize({
202+
bson.serialize({
194203
token: request.headers.Authorization
195204
})
196205
)
@@ -223,7 +232,7 @@ export abstract class AbstractRemote {
223232
}
224233
socketIsClosed = true;
225234
rsocket.close();
226-
}
235+
};
227236
// We initially request this amount and expect these to arrive eventually
228237
let pendingEventsCount = SYNC_QUEUE_REQUEST_N;
229238

@@ -232,9 +241,9 @@ export abstract class AbstractRemote {
232241

233242
const res = rsocket.requestStream(
234243
{
235-
data: Buffer.from(serialize(options.data)),
244+
data: Buffer.from(bson.serialize(options.data)),
236245
metadata: Buffer.from(
237-
serialize({
246+
bson.serialize({
238247
path
239248
})
240249
)
@@ -268,7 +277,7 @@ export abstract class AbstractRemote {
268277
return;
269278
}
270279

271-
const deserializedData = deserialize(data);
280+
const deserializedData = bson.deserialize(data);
272281
stream.enqueueData(deserializedData);
273282
},
274283
onComplete: () => {

packages/react-native/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
},
3636
"dependencies": {
3737
"@powersync/react": "workspace:*",
38-
"async-lock": "^1.4.0"
38+
"async-lock": "^1.4.0",
39+
"bson": "^6.6.0"
3940
},
4041
"devDependencies": {
4142
"@journeyapps/react-native-quick-sqlite": "^1.1.6",

packages/react-native/src/sync/stream/ReactNativeRemote.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
import { AbstractRemote, DataStream, StreamingSyncLine, SyncStreamOptions } from '@powersync/common';
1+
import {
2+
AbstractRemote,
3+
BSONImplementation,
4+
DataStream,
5+
StreamingSyncLine,
6+
SyncStreamOptions
7+
} from '@powersync/common';
28
import { Platform } from 'react-native';
3-
9+
// Note docs for React Native https://github.com/mongodb/js-bson?tab=readme-ov-file#react-native
10+
import { BSON } from 'bson';
411
export const STREAMING_POST_TIMEOUT_MS = 30_000;
512

613
type PolyfillTest = {
@@ -53,6 +60,10 @@ ${missingPolyfills.join('\n')}`
5360
};
5461

5562
export class ReactNativeRemote extends AbstractRemote {
63+
async getBSON(): Promise<BSONImplementation> {
64+
return BSON;
65+
}
66+
5667
async socketStream(options: SyncStreamOptions): Promise<DataStream<StreamingSyncLine>> {
5768
validatePolyfills(SocketPolyfillTests);
5869
return super.socketStream(options);

packages/web/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ if (typeof self.Buffer == 'undefined') {
5656
}
5757
```
5858

59+
## Webpack
60+
61+
See the [example Webpack config](https://github.com/powersync-ja/powersync-js/blob/main/demos/example-webpack/webpack.config.js) for details on polyfills and requirements.
62+
63+
## Vite
64+
65+
See the [example Vite config](https://github.com/powersync-ja/powersync-js/blob/main/demos/example-vite/vite.config.ts) for details on polyfills and requirements.
66+
5967
# Getting Started
6068

6169
Our [full SDK reference](https://docs.powersync.com/client-sdk-references/js-web) contains everything you need to know to get started implementing PowerSync in your project.

packages/web/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"dependencies": {
4040
"async-mutex": "^0.4.0",
4141
"buffer": "^6.0.3",
42+
"bson": "^6.6.0",
4243
"comlink": "^4.4.1",
4344
"js-logger": "^1.6.1",
4445
"lodash": "^4.17.21"

packages/web/src/db/sync/WebRemote.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1-
import { AbstractRemote } from '@powersync/common';
1+
import { AbstractRemote, BSONImplementation } from '@powersync/common';
22

3-
export class WebRemote extends AbstractRemote {}
3+
export class WebRemote extends AbstractRemote {
4+
private _bson: BSONImplementation | undefined;
5+
6+
async getBSON(): Promise<BSONImplementation> {
7+
if (this._bson) {
8+
return this._bson;
9+
}
10+
11+
/**
12+
* Dynamic import to be used only when needed.
13+
*/
14+
const { BSON } = await import('bson');
15+
this._bson = BSON;
16+
return this._bson;
17+
}
18+
}

packages/web/tests/utils/MockStreamOpenFactory.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
PowerSyncDatabaseOptions,
99
SyncStreamOptions,
1010
DataStream,
11-
StreamingSyncLine
11+
StreamingSyncLine,
12+
BSONImplementation
1213
} from '@powersync/common';
1314
import {
1415
PowerSyncDatabase,
@@ -41,6 +42,10 @@ export class MockRemote extends AbstractRemote {
4142
this.streamController = null;
4243
}
4344

45+
async getBSON(): Promise<BSONImplementation> {
46+
return import('bson');
47+
}
48+
4449
post(path: string, data: any, headers?: Record<string, string> | undefined): Promise<any> {
4550
throw new Error('Method not implemented.');
4651
}

0 commit comments

Comments
 (0)