Skip to content

Commit 9cfca81

Browse files
authored
Remove DataStream, support binary sync lines over HTTP (#821)
1 parent 7e4c77a commit 9cfca81

File tree

19 files changed

+980
-698
lines changed

19 files changed

+980
-698
lines changed

.changeset/cool-lions-melt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@powersync/react-native': minor
3+
'@powersync/common': minor
4+
---
5+
6+
Simplify internal handling of HTTP streams.

packages/common/package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,15 @@
6464
"@rollup/plugin-node-resolve": "catalog:",
6565
"@types/node": "catalog:",
6666
"@types/uuid": "catalog:",
67+
"bson": "catalog:",
6768
"buffer": "^6.0.3",
68-
"rollup": "catalog:",
69-
"rollup-plugin-dts": "catalog:",
7069
"cross-fetch": "^4.1.0",
70+
"estree-walker": "^3.0.3",
7171
"js-logger": "catalog:",
72+
"magic-string": "^0.30.21",
73+
"rollup": "catalog:",
74+
"rollup-plugin-dts": "catalog:",
7275
"rsocket-core": "1.0.0-alpha.3",
73-
"rsocket-websocket-client": "1.0.0-alpha.3",
74-
"bson": "catalog:"
76+
"rsocket-websocket-client": "1.0.0-alpha.3"
7577
}
7678
}

packages/common/rollup.config.mjs

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,24 @@ import inject from '@rollup/plugin-inject';
33
import json from '@rollup/plugin-json';
44
import nodeResolve from '@rollup/plugin-node-resolve';
55
import { dts } from 'rollup-plugin-dts';
6+
import MagicString from 'magic-string';
7+
import { walk } from 'estree-walker';
68

79
function defineBuild(isNode) {
810
const suffix = isNode ? '.node' : '';
11+
const plugins = [
12+
[
13+
json(),
14+
nodeResolve({ preferBuiltins: false, browser: true }),
15+
commonjs({}),
16+
inject({
17+
Buffer: isNode ? ['node:buffer', 'Buffer'] : ['buffer/', 'Buffer']
18+
})
19+
]
20+
];
21+
if (!isNode) {
22+
plugins.push(applyAsyncIteratorPolyfill());
23+
}
924

1025
return {
1126
input: 'lib/index.js',
@@ -21,16 +36,7 @@ function defineBuild(isNode) {
2136
sourcemap: true
2237
}
2338
],
24-
plugins: [
25-
[
26-
json(),
27-
nodeResolve({ preferBuiltins: false, browser: true }),
28-
commonjs({}),
29-
inject({
30-
Buffer: isNode ? ['node:buffer', 'Buffer'] : ['buffer/', 'Buffer']
31-
})
32-
]
33-
],
39+
plugins,
3440
external: ['bson', isNode ? 'event-iterator' : undefined]
3541
};
3642
}
@@ -49,3 +55,71 @@ export default () => {
4955
}
5056
];
5157
};
58+
59+
/**
60+
* Replaces `Symbol.asyncIterator` with a polyfill in bundled dependencies (specifically `EventIterator`).
61+
*
62+
* This is derived from an example in Rollup docs: https://rollupjs.org/plugin-development/#resolveid
63+
*/
64+
function applyAsyncIteratorPolyfill() {
65+
// A fake file we import into every file using Symbol.asyncIterator
66+
const POLYFILL_ID = '\0symbol-async-iterator';
67+
68+
return {
69+
name: 'applyAsyncIteratorPonyfill',
70+
async resolveId(source) {
71+
if (source === POLYFILL_ID) {
72+
return { id: POLYFILL_ID };
73+
}
74+
return null;
75+
},
76+
load(id) {
77+
if (id === POLYFILL_ID) {
78+
// Outside of Node.JS, we replace Symbol.asyncIterator with an import to this symbol. This allows us to use
79+
// Symbol.asyncIterator internally. If users install the recommended polyfill, https://github.com/Azure/azure-sdk-for-js/blob/%40azure/core-asynciterator-polyfill_1.0.2/sdk/core/core-asynciterator-polyfill/src/index.ts#L4-L6
80+
// they can also use our async iterables on React Native.
81+
// Of course, we could also inline this definition with a simple replacement. But putting this in a fake module
82+
// de-duplicates it.
83+
return `
84+
export const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
85+
`;
86+
}
87+
},
88+
transform(code, id) {
89+
if (id === POLYFILL_ID) return null;
90+
if (!code.includes('Symbol.asyncIterator')) return null;
91+
92+
const ast = this.parse(code);
93+
const s = new MagicString(code);
94+
95+
let replaced = false;
96+
const symbolName = 'symbolAsyncIterator';
97+
98+
walk(ast, {
99+
enter(node) {
100+
// Replace Symbol.asyncIterator
101+
if (
102+
node.type === 'MemberExpression' &&
103+
!node.computed &&
104+
node.object.type === 'Identifier' &&
105+
node.object.name === 'Symbol' &&
106+
node.property.type === 'Identifier' &&
107+
node.property.name === 'asyncIterator'
108+
) {
109+
replaced = true;
110+
s.overwrite(node.start, node.end, symbolName);
111+
}
112+
}
113+
});
114+
115+
if (!replaced) return null;
116+
117+
s.prepend(`import { ${symbolName} } from ${JSON.stringify(POLYFILL_ID)};\n`);
118+
119+
return {
120+
code: s.toString(),
121+
map: s.generateMap({ hires: true })
122+
};
123+
}
124+
};
125+
}

packages/common/src/client/AbstractPowerSyncDatabase.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
1313
import { Schema } from '../db/schema/Schema.js';
1414
import { BaseObserver } from '../utils/BaseObserver.js';
1515
import { ControlledExecutor } from '../utils/ControlledExecutor.js';
16-
import { symbolAsyncIterator, throttleTrailing } from '../utils/async.js';
16+
import { throttleTrailing } from '../utils/async.js';
1717
import {
1818
ConnectionManager,
1919
CreateSyncImplementationOptions,
@@ -704,7 +704,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
704704
* @returns A transaction of CRUD operations to upload, or null if there are none
705705
*/
706706
async getNextCrudTransaction(): Promise<CrudTransaction | null> {
707-
const iterator = this.getCrudTransactions()[symbolAsyncIterator]();
707+
const iterator = this.getCrudTransactions()[Symbol.asyncIterator]();
708708
return (await iterator.next()).value;
709709
}
710710

@@ -741,7 +741,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
741741
*/
742742
getCrudTransactions(): AsyncIterable<CrudTransaction, null> {
743743
return {
744-
[symbolAsyncIterator]: () => {
744+
[Symbol.asyncIterator]: () => {
745745
let lastCrudItemId = -1;
746746
const sql = `
747747
WITH RECURSIVE crud_entries AS (

0 commit comments

Comments
 (0)