Skip to content

Commit 326f33d

Browse files
bundle powersync core with package
1 parent 86f3c1d commit 326f33d

File tree

4 files changed

+113
-41
lines changed

4 files changed

+113
-41
lines changed

.changeset/seven-fireants-boil.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/node': minor
3+
---
4+
5+
Bundle PowerSync extension with NPM package

packages/node/download_core.js

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
// TODO: Make this a pre-publish hook and just bundle everything
21
import { createHash } from 'node:crypto';
3-
import * as OS from 'node:os';
42
import * as fs from 'node:fs/promises';
53
import * as path from 'node:path';
64
import { Readable } from 'node:stream';
75
import { finished } from 'node:stream/promises';
8-
import { exit } from 'node:process';
96

107
// When changing this version, run node download_core.js update_hashes
118
const version = '0.4.6';
@@ -17,33 +14,24 @@ const versionHashes = {
1714
'libpowersync_aarch64.dylib': 'bfb4f1ec207b298aff560f1825f8123d24316edaa27b6df3a17dd49466576b92'
1815
};
1916

20-
const platform = OS.platform();
21-
let destination;
22-
let asset;
23-
24-
if (platform === 'win32') {
25-
asset = 'powersync_x64.dll';
26-
destination = 'powersync.dll';
27-
} else if (platform === 'linux') {
28-
asset = OS.arch() === 'x64' ? 'libpowersync_x64.so' : 'libpowersync_aarch64.so';
29-
destination = 'libpowersync.so';
30-
} else if (platform === 'darwin') {
31-
asset = OS.arch() === 'x64' ? 'libpowersync_x64.dylib' : 'libpowersync_aarch64.dylib';
32-
destination = 'libpowersync.dylib';
33-
}
34-
35-
const expectedHash = versionHashes[asset];
36-
const destinationPath = path.resolve('lib', destination);
17+
// Map of all assets to their destinations
18+
const assetMap = {
19+
'powersync_x64.dll': 'powersync.dll',
20+
'libpowersync_x64.so': 'libpowersync.so',
21+
'libpowersync_aarch64.so': 'libpowersync-aarch64.so',
22+
'libpowersync_x64.dylib': 'libpowersync.dylib',
23+
'libpowersync_aarch64.dylib': 'libpowersync-aarch64.dylib'
24+
};
3725

3826
const hashStream = async (input) => {
3927
for await (const chunk of input.pipe(createHash('sha256')).setEncoding('hex')) {
4028
return chunk;
4129
}
4230
};
4331

44-
const hashLocal = async () => {
32+
const hashLocal = async (filePath) => {
4533
try {
46-
const handle = await fs.open(destinationPath, 'r');
34+
const handle = await fs.open(filePath, 'r');
4735
const input = handle.createReadStream();
4836

4937
const result = await hashStream(input);
@@ -54,31 +42,92 @@ const hashLocal = async () => {
5442
}
5543
};
5644

57-
const download = async () => {
58-
if ((await hashLocal()) == expectedHash) {
59-
console.debug('Local copy is up-to-date, skipping download');
60-
exit(0);
45+
const downloadAsset = async (asset, destination) => {
46+
const destinationPath = path.resolve('lib', destination);
47+
const expectedHash = versionHashes[asset];
48+
49+
// Check if file exists and has correct hash
50+
const currentHash = await hashLocal(destinationPath);
51+
if (currentHash === expectedHash) {
52+
console.debug(`${destination} is up-to-date, skipping download`);
53+
return;
6154
}
6255

6356
const url = `https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v${version}/${asset}`;
57+
console.log(`Downloading ${url}`);
6458
const response = await fetch(url);
6559
if (response.status != 200) {
6660
throw `Could not download ${url}`;
6761
}
6862

63+
const file = await fs.open(destinationPath, 'w');
64+
await finished(Readable.fromWeb(response.body).pipe(file.createWriteStream()));
65+
await file.close();
66+
67+
const hashAfterDownloading = await hashLocal(destinationPath);
68+
if (hashAfterDownloading != expectedHash) {
69+
throw `Unexpected hash after downloading ${asset} (got ${hashAfterDownloading}, expected ${expectedHash})`;
70+
}
71+
console.log(`Successfully downloaded ${destination}`);
72+
};
73+
74+
const checkAsset = async (asset, destination) => {
75+
const destinationPath = path.resolve('lib', destination);
76+
const expectedHash = versionHashes[asset];
77+
const currentHash = await hashLocal(destinationPath);
78+
79+
return {
80+
asset,
81+
destination,
82+
destinationPath,
83+
expectedHash,
84+
currentHash,
85+
exists: currentHash !== null,
86+
isValid: currentHash === expectedHash
87+
};
88+
};
89+
90+
const download = async () => {
6991
try {
7092
await fs.access('lib');
7193
} catch {
7294
await fs.mkdir('lib');
7395
}
7496

75-
const file = await fs.open(destinationPath, 'w');
76-
await finished(Readable.fromWeb(response.body).pipe(file.createWriteStream()));
77-
await file.close();
97+
// First check all assets
98+
console.log('Checking existing files...');
99+
const checks = await Promise.all(
100+
Object.entries(assetMap).map(([asset, destination]) => checkAsset(asset, destination))
101+
);
78102

79-
const hashAfterDownloading = await hashLocal();
80-
if (hashAfterDownloading != expectedHash) {
81-
throw `Unexpected hash after downloading (got ${hashAfterDownloading}, expected ${expectedHash})`;
103+
const toDownload = checks.filter((check) => !check.isValid);
104+
const upToDate = checks.filter((check) => check.isValid);
105+
106+
// Print summary
107+
if (upToDate.length > 0) {
108+
console.log('\nUp-to-date files:');
109+
for (const check of upToDate) {
110+
console.log(` ✓ ${check.destination}`);
111+
}
112+
}
113+
114+
if (toDownload.length > 0) {
115+
console.log('\nFiles to download:');
116+
for (const check of toDownload) {
117+
if (!check.exists) {
118+
console.log(` • ${check.destination} (missing)`);
119+
} else {
120+
console.log(` • ${check.destination} (hash mismatch)`);
121+
}
122+
}
123+
124+
console.log('\nStarting downloads...');
125+
// Download required assets in parallel
126+
await Promise.all(toDownload.map((check) => downloadAsset(check.asset, check.destination)));
127+
128+
console.log('\nAll downloads completed successfully!');
129+
} else {
130+
console.log('\nAll files are up-to-date, nothing to download.');
82131
}
83132
};
84133

packages/node/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
"download_core.js"
1414
],
1515
"scripts": {
16-
"install": "node download_core.js",
17-
"build": "tsc -b && rollup --config",
18-
"build:prod": "tsc -b --sourceMap false && rollup --config",
16+
"prepare:core": "node download_core.js",
17+
"build": " pnpm prepare:core && tsc -b && rollup --config",
18+
"build:prod": "pnpm prepare:core && tsc -b --sourceMap false && rollup --config",
1919
"clean": "rm -rf lib dist tsconfig.tsbuildinfo",
2020
"watch": "tsc -b -w",
21-
"test": "vitest",
21+
"test": " pnpm prepare:core && vitest",
2222
"test:exports": "attw --pack . --ignore-rules no-resolution"
2323
},
2424
"type": "module",

packages/node/src/db/SqliteWorker.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import * as path from 'node:path';
21
import BetterSQLite3Database, { Database } from '@powersync/better-sqlite3';
32
import * as Comlink from 'comlink';
4-
import { parentPort, threadId } from 'node:worker_threads';
53
import OS from 'node:os';
4+
import * as path from 'node:path';
65
import url from 'node:url';
6+
import { parentPort, threadId } from 'node:worker_threads';
77
import { AsyncDatabase, AsyncDatabaseOpener } from './AsyncDatabase.js';
88

99
class BlockingAsyncDatabase implements AsyncDatabase {
@@ -129,13 +129,31 @@ export function startPowerSyncWorker(options?: Partial<PowerSyncWorkerOptions>)
129129
const isCommonJsModule = import.meta.isBundlingToCommonJs ?? false;
130130

131131
const platform = OS.platform();
132+
const arch = OS.arch();
132133
let extensionPath: string;
134+
133135
if (platform === 'win32') {
134-
extensionPath = 'powersync.dll';
136+
if (arch === 'x64') {
137+
extensionPath = 'powersync.dll';
138+
} else {
139+
throw 'Windows platform only supports x64 architecture.';
140+
}
135141
} else if (platform === 'linux') {
136-
extensionPath = 'libpowersync.so';
142+
if (arch === 'x64') {
143+
extensionPath = 'libpowersync.so';
144+
} else if (arch === 'arm64') {
145+
extensionPath = 'libpowersync-aarch64.so';
146+
} else {
147+
throw 'Linux platform only supports x64 and arm64 architectures.';
148+
}
137149
} else if (platform === 'darwin') {
138-
extensionPath = 'libpowersync.dylib';
150+
if (arch === 'x64') {
151+
extensionPath = 'libpowersync.dylib';
152+
} else if (arch === 'arm64') {
153+
extensionPath = 'libpowersync-aarch64.dylib';
154+
} else {
155+
throw 'macOS platform only supports x64 and arm64 architectures.';
156+
}
139157
} else {
140158
throw 'Unknown platform, PowerSync for Node.js currently supports Windows, Linux and macOS.';
141159
}

0 commit comments

Comments
 (0)