diff --git a/demos/react-supabase-todolist/package.json b/demos/react-supabase-todolist/package.json
index 33342be5..976d380e 100644
--- a/demos/react-supabase-todolist/package.json
+++ b/demos/react-supabase-todolist/package.json
@@ -13,7 +13,7 @@
"@powersync/web": "workspace:*",
"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.5",
- "@journeyapps/wa-sqlite": "0.0.0-dev-20241126093237",
+ "@journeyapps/wa-sqlite": "0.0.0-dev-20241126145151",
"@mui/icons-material": "^5.15.12",
"@mui/material": "^5.15.12",
"@mui/x-data-grid": "^6.19.6",
diff --git a/packages/web/package.json b/packages/web/package.json
index f615377f..33404b95 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -60,7 +60,7 @@
"author": "JOURNEYAPPS",
"license": "Apache-2.0",
"peerDependencies": {
- "@journeyapps/wa-sqlite": "0.0.0-dev-20241126093237",
+ "@journeyapps/wa-sqlite": "0.0.0-dev-20241126145151",
"@powersync/common": "workspace:^1.21.0"
},
"dependencies": {
@@ -72,7 +72,7 @@
"js-logger": "^1.6.1"
},
"devDependencies": {
- "@journeyapps/wa-sqlite": "0.0.0-dev-20241126093237",
+ "@journeyapps/wa-sqlite": "0.0.0-dev-20241126145151",
"@types/uuid": "^9.0.6",
"@vitest/browser": "^2.1.4",
"crypto-browserify": "^3.12.0",
diff --git a/packages/web/src/db/PowerSyncDatabase.ts b/packages/web/src/db/PowerSyncDatabase.ts
index 56a3c6dd..24e61300 100644
--- a/packages/web/src/db/PowerSyncDatabase.ts
+++ b/packages/web/src/db/PowerSyncDatabase.ts
@@ -55,17 +55,31 @@ type WithWebSyncOptions = Base & {
sync?: WebSyncOptions;
};
+export interface WebEncryptionOptions {
+ /**
+ * Encryption key for the database.
+ * If set, the database will be encrypted using Multiple Ciphers.
+ */
+ encryptionKey?: string;
+}
+
+type WithWebEncryptionOptions = Base & {
+ encryptionKey?: string;
+};
+
export type WebPowerSyncDatabaseOptionsWithAdapter = WithWebSyncOptions<
- WithWebFlags
+ WithWebFlags>
>;
export type WebPowerSyncDatabaseOptionsWithOpenFactory = WithWebSyncOptions<
- WithWebFlags
+ WithWebFlags>
>;
export type WebPowerSyncDatabaseOptionsWithSettings = WithWebSyncOptions<
- WithWebFlags
+ WithWebFlags>
>;
-export type WebPowerSyncDatabaseOptions = WithWebSyncOptions>;
+export type WebPowerSyncDatabaseOptions = WithWebSyncOptions<
+ WithWebFlags>
+>;
export const DEFAULT_POWERSYNC_FLAGS: Required = {
...DEFAULT_WEB_SQL_FLAGS,
@@ -120,7 +134,8 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
protected openDBAdapter(options: WebPowerSyncDatabaseOptionsWithSettings): DBAdapter {
const defaultFactory = new WASQLiteOpenFactory({
...options.database,
- flags: resolveWebPowerSyncFlags(options.flags)
+ flags: resolveWebPowerSyncFlags(options.flags),
+ encryptionKey: options.encryptionKey
});
return defaultFactory.openDB();
}
diff --git a/packages/web/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts b/packages/web/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts
index d2d39e0b..b7104ea2 100644
--- a/packages/web/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts
+++ b/packages/web/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts
@@ -32,6 +32,12 @@ export interface WASQLiteDBAdapterOptions extends Omit Worker | SharedWorker);
+
+ /**
+ * Encryption key for the database.
+ * If set, the database will be encrypted using SQLCipher.
+ */
+ encryptionKey?: string;
}
/**
@@ -111,7 +117,7 @@ export class WASQLiteDBAdapter extends BaseObserver implement
return;
}
- this.methods = await _openDB(this.options.dbFilename, { useWebWorker: false });
+ this.methods = await _openDB(this.options.dbFilename, this.options.encryptionKey, { useWebWorker: false });
this.methods.registerOnTableChange((event) => {
this.iterateListeners((cb) => cb.tablesUpdated?.(event));
});
diff --git a/packages/web/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts b/packages/web/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts
index 039e0f83..98b8eadf 100644
--- a/packages/web/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts
+++ b/packages/web/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts
@@ -9,7 +9,8 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
protected openAdapter(): DBAdapter {
return new WASQLiteDBAdapter({
...this.options,
- flags: this.resolvedFlags
+ flags: this.resolvedFlags,
+ encryptionKey: this.options.encryptionKey
});
}
}
diff --git a/packages/web/src/db/adapters/web-sql-flags.ts b/packages/web/src/db/adapters/web-sql-flags.ts
index dcc62c6b..8b36dae7 100644
--- a/packages/web/src/db/adapters/web-sql-flags.ts
+++ b/packages/web/src/db/adapters/web-sql-flags.ts
@@ -55,6 +55,12 @@ export interface WebSQLOpenFactoryOptions extends SQLOpenOptions {
* or a factory method that returns a worker.
*/
worker?: string | URL | ((options: ResolvedWebSQLOpenOptions) => Worker | SharedWorker);
+
+ /**
+ * Encryption key for the database.
+ * If set, the database will be encrypted using Multiple Ciphers.
+ */
+ encryptionKey?: string;
}
export function isServerSide() {
diff --git a/packages/web/src/shared/open-db.ts b/packages/web/src/shared/open-db.ts
index efd31c16..0e494834 100644
--- a/packages/web/src/shared/open-db.ts
+++ b/packages/web/src/shared/open-db.ts
@@ -9,45 +9,48 @@ let nextId = 1;
export async function _openDB(
dbFileName: string,
+ encryptionKey?: string,
options: { useWebWorker: boolean } = { useWebWorker: true }
): Promise {
- const { default: moduleFactory } = await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs');
+ let moduleFactory;
+ if (encryptionKey) {
+ console.log('Using encrypted database');
+ moduleFactory = (await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs')).default;
+ } else {
+ console.log('Using unencrypted database');
+ moduleFactory = (await import('@journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs')).default;
+ }
+ // const { default: moduleFactory } = await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs');
const module = await moduleFactory();
const sqlite3 = SQLite.Factory(module);
- console.log('sqlite3', sqlite3);
/**
* Register the PowerSync core SQLite extension
*/
module.ccall('powersync_init_static', 'int', []);
- console.log('powerync init');
-
const { IDBBatchAtomicVFS } = await import('@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js');
- console.log('DbFileName', dbFileName);
// @ts-expect-error The types for this static method are missing upstream
const vfs = await IDBBatchAtomicVFS.create(dbFileName, module, {
lockPolicy: 'exclusive'
});
-
- console.log('vfs before', vfs);
sqlite3.vfs_register(vfs, true);
- const createResult = module.ccall('sqlite3mc_vfs_create', 'int', ['string', 'int'], [dbFileName, 1]);
- console.log('result from creation', createResult);
- if (createResult !== 0) {
- throw new Error('Failed to create multiple cipher vfs');
+ if (encryptionKey) {
+ const createResult = module.ccall('sqlite3mc_vfs_create', 'int', ['string', 'int'], [dbFileName, 1]);
+ if (createResult !== 0) {
+ throw new Error('Failed to create multiple cipher vfs');
+ }
}
- console.log('vfs after', vfs);
const db = await sqlite3.open_v2(dbFileName);
- console.log('db', db);
- // const pragma = await sqlite3.exec(db, 'PRAGMA key = "key"');
- // console.log('pragma', pragma);
- // if (pragma !== SQLite.SQLITE_OK) {
- // throw new Error('Failed to set key');
- // }
+ if (encryptionKey) {
+ const pragma = await sqlite3.exec(db, `PRAGMA key = "${encryptionKey}"`);
+ if (pragma !== SQLite.SQLITE_OK) {
+ throw new Error('Failed to set encryption key');
+ }
+ }
const statementMutex = new Mutex();
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 748f8ad4..83b9e175 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1067,8 +1067,8 @@ importers:
specifier: 11.11.5
version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.11)(react@18.2.0)
'@journeyapps/wa-sqlite':
- specifier: 0.0.0-dev-20241126093237
- version: 0.0.0-dev-20241126093237
+ specifier: 0.0.0-dev-20241126145151
+ version: 0.0.0-dev-20241126145151
'@mui/icons-material':
specifier: ^5.15.12
version: 5.16.7(@mui/material@5.16.7)(@types/react@18.3.11)(react@18.2.0)
@@ -1900,8 +1900,8 @@ importers:
version: 1.6.1
devDependencies:
'@journeyapps/wa-sqlite':
- specifier: 0.0.0-dev-20241126093237
- version: 0.0.0-dev-20241126093237
+ specifier: 0.0.0-dev-20241126145151
+ version: 0.0.0-dev-20241126145151
'@types/uuid':
specifier: ^9.0.6
version: 9.0.8
@@ -10697,8 +10697,8 @@ packages:
react-native: 0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.7)(@types/react@18.2.79)(react@18.2.0)
dev: false
- /@journeyapps/wa-sqlite@0.0.0-dev-20241126093237:
- resolution: {integrity: sha512-hLAEGlzXmfapXxnWUDTT0ClHuz+oxmAoR5pBESplB4EEAUMnyNeV595/IufvFQq3WqNZ+P/RXsaJYZqY0Ji2HA==}
+ /@journeyapps/wa-sqlite@0.0.0-dev-20241126145151:
+ resolution: {integrity: sha512-FwlF1Q/pMIHUXKjcDBkO3MGSimCIA21PwFmz29xII61+Ga0sRpLEx28WQYD7YTG6/8r68f5vLgB0SkD8nXAIag==}
requiresBuild: true
/@journeyapps/wa-sqlite@0.4.2:
@@ -15367,6 +15367,7 @@ packages:
cpu: [arm64]
os: [darwin]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-darwin-arm64@1.7.26:
@@ -15384,6 +15385,7 @@ packages:
cpu: [x64]
os: [darwin]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-darwin-x64@1.7.26:
@@ -15401,6 +15403,7 @@ packages:
cpu: [arm]
os: [linux]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-linux-arm-gnueabihf@1.7.26:
@@ -15418,6 +15421,7 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-linux-arm64-gnu@1.7.26:
@@ -15435,6 +15439,7 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-linux-arm64-musl@1.7.26:
@@ -15452,6 +15457,7 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-linux-x64-gnu@1.7.26:
@@ -15469,6 +15475,7 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-linux-x64-musl@1.7.26:
@@ -15486,6 +15493,7 @@ packages:
cpu: [arm64]
os: [win32]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-win32-arm64-msvc@1.7.26:
@@ -15503,6 +15511,7 @@ packages:
cpu: [ia32]
os: [win32]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-win32-ia32-msvc@1.7.26:
@@ -15520,6 +15529,7 @@ packages:
cpu: [x64]
os: [win32]
requiresBuild: true
+ dev: true
optional: true
/@swc/core-win32-x64-msvc@1.7.26:
@@ -15554,6 +15564,7 @@ packages:
'@swc/core-win32-arm64-msvc': 1.6.13
'@swc/core-win32-ia32-msvc': 1.6.13
'@swc/core-win32-x64-msvc': 1.6.13
+ dev: true
/@swc/core@1.7.26:
resolution: {integrity: sha512-f5uYFf+TmMQyYIoxkn/evWhNGuUzC730dFwAKGwBVHHVoPyak1/GvJUm6i1SKl+2Hrj9oN0i3WSoWWZ4pgI8lw==}
@@ -15594,6 +15605,7 @@ packages:
resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==}
dependencies:
'@swc/counter': 0.1.3
+ dev: true
/@szmarczak/http-timer@4.0.6:
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
@@ -19745,7 +19757,7 @@ packages:
'@babel/core': 7.24.5
find-cache-dir: 4.0.0
schema-utils: 4.2.0
- webpack: 5.95.0(@swc/core@1.6.13)
+ webpack: 5.95.0(webpack-cli@5.1.4)
/babel-plugin-dynamic-import-node@2.3.3:
resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==}
@@ -36166,6 +36178,7 @@ packages:
serialize-javascript: 6.0.2
terser: 5.34.1
webpack: 5.95.0(@swc/core@1.6.13)
+ dev: true
/terser-webpack-plugin@5.3.10(esbuild@0.23.0)(webpack@5.94.0):
resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
@@ -38737,6 +38750,7 @@ packages:
- '@swc/core'
- esbuild
- uglify-js
+ dev: true
/webpack@5.95.0(webpack-cli@5.1.4):
resolution: {integrity: sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==}