@@ -36,7 +36,7 @@ export type SQLiteModule = Parameters<typeof SQLite.Factory>[0];
3636/**
3737 * @internal
3838 */
39- export type WASQLiteModuleFactoryOptions = { dbFileName : string } ;
39+ export type WASQLiteModuleFactoryOptions = { dbFileName : string ; encryptionKey ?: string } ;
4040
4141/**
4242 * @internal
@@ -53,6 +53,14 @@ export const AsyncWASQLiteModuleFactory = async () => {
5353 return factory ( ) ;
5454} ;
5555
56+ /**
57+ * @internal
58+ */
59+ export const MultiCipherAsyncWASQLiteModuleFactory = async ( ) => {
60+ const { default : factory } = await import ( '@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs' ) ;
61+ return factory ( ) ;
62+ } ;
63+
5664/**
5765 * @internal
5866 */
@@ -61,12 +69,25 @@ export const SyncWASQLiteModuleFactory = async () => {
6169 return factory ( ) ;
6270} ;
6371
72+ /**
73+ * @internal
74+ */
75+ export const MultiCipherSyncWASQLiteModuleFactory = async ( ) => {
76+ const { default : factory } = await import ( '@journeyapps/wa-sqlite/dist/mc-wa-sqlite.mjs' ) ;
77+ return factory ( ) ;
78+ } ;
79+
6480/**
6581 * @internal
6682 */
6783export const DEFAULT_MODULE_FACTORIES = {
6884 [ WASQLiteVFS . IDBBatchAtomicVFS ] : async ( options : WASQLiteModuleFactoryOptions ) => {
69- const module = await AsyncWASQLiteModuleFactory ( ) ;
85+ let module ;
86+ if ( options . encryptionKey ) {
87+ module = await MultiCipherAsyncWASQLiteModuleFactory ( ) ;
88+ } else {
89+ module = await AsyncWASQLiteModuleFactory ( ) ;
90+ }
7091 const { IDBBatchAtomicVFS } = await import ( '@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js' ) ;
7192 return {
7293 module,
@@ -75,7 +96,12 @@ export const DEFAULT_MODULE_FACTORIES = {
7596 } ;
7697 } ,
7798 [ WASQLiteVFS . AccessHandlePoolVFS ] : async ( options : WASQLiteModuleFactoryOptions ) => {
78- const module = await SyncWASQLiteModuleFactory ( ) ;
99+ let module ;
100+ if ( options . encryptionKey ) {
101+ module = await MultiCipherSyncWASQLiteModuleFactory ( ) ;
102+ } else {
103+ module = await SyncWASQLiteModuleFactory ( ) ;
104+ }
79105 // @ts -expect-error The types for this static method are missing upstream
80106 const { AccessHandlePoolVFS } = await import ( '@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js' ) ;
81107 return {
@@ -84,7 +110,12 @@ export const DEFAULT_MODULE_FACTORIES = {
84110 } ;
85111 } ,
86112 [ WASQLiteVFS . OPFSCoopSyncVFS ] : async ( options : WASQLiteModuleFactoryOptions ) => {
87- const module = await SyncWASQLiteModuleFactory ( ) ;
113+ let module ;
114+ if ( options . encryptionKey ) {
115+ module = await MultiCipherSyncWASQLiteModuleFactory ( ) ;
116+ } else {
117+ module = await SyncWASQLiteModuleFactory ( ) ;
118+ }
88119 // @ts -expect-error The types for this static method are missing upstream
89120 const { OPFSCoopSyncVFS } = await import ( '@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js' ) ;
90121 return {
@@ -146,15 +177,35 @@ export class WASqliteConnection
146177 return this . _dbP ;
147178 }
148179
180+ protected async executeEncryptionPragma ( ) : Promise < void > {
181+ if ( this . options . encryptionKey && this . _dbP ) {
182+ await this . executeSingleStatement ( `PRAGMA key = "${ this . options . encryptionKey } "` ) ;
183+ }
184+ return ;
185+ }
186+
149187 protected async openSQLiteAPI ( ) : Promise < SQLiteAPI > {
150- const { module, vfs } = await this . _moduleFactory ( { dbFileName : this . options . dbFilename } ) ;
188+ const { module, vfs } = await this . _moduleFactory ( {
189+ dbFileName : this . options . dbFilename ,
190+ encryptionKey : this . options . encryptionKey
191+ } ) ;
151192 const sqlite3 = SQLite . Factory ( module ) ;
152193 sqlite3 . vfs_register ( vfs , true ) ;
153194 /**
154195 * Register the PowerSync core SQLite extension
155196 */
156197 module . ccall ( 'powersync_init_static' , 'int' , [ ] ) ;
157198
199+ /**
200+ * Create the multiple cipher vfs if an encryption key is provided
201+ */
202+ if ( this . options . encryptionKey ) {
203+ const createResult = module . ccall ( 'sqlite3mc_vfs_create' , 'int' , [ 'string' , 'int' ] , [ this . options . dbFilename , 1 ] ) ;
204+ if ( createResult !== 0 ) {
205+ throw new Error ( 'Failed to create multiple cipher vfs, Database encryption will not work' ) ;
206+ }
207+ }
208+
158209 return sqlite3 ;
159210 }
160211
@@ -182,6 +233,7 @@ export class WASqliteConnection
182233 await this . openDB ( ) ;
183234 this . registerBroadcastListeners ( ) ;
184235 await this . executeSingleStatement ( `PRAGMA temp_store = ${ this . options . temporaryStorage } ;` ) ;
236+ await this . executeEncryptionPragma ( ) ;
185237
186238 this . sqliteAPI . update_hook ( this . dbP , ( updateType : number , dbName : string | null , tableName : string | null ) => {
187239 if ( ! tableName ) {
0 commit comments