diff --git a/demos/example-vite-custom-sql/CHANGELOG.md b/demos/example-vite-custom-sql/CHANGELOG.md new file mode 100644 index 000000000..b245f3300 --- /dev/null +++ b/demos/example-vite-custom-sql/CHANGELOG.md @@ -0,0 +1,3 @@ +# example-vite-custom-sql + +## 0.0.1 diff --git a/demos/example-vite-custom-sql/README.md b/demos/example-vite-custom-sql/README.md new file mode 100644 index 000000000..82a2c8fbb --- /dev/null +++ b/demos/example-vite-custom-sql/README.md @@ -0,0 +1,9 @@ +# PowerSync Vite custom SQL demo + +This is a minimal example demonstrating how to use custom SQL with PowerSync. + +To see it in action: + +1. Make sure to run `pnpm install` and `pnpm build:packages` in the root directory of this repo. +2. `cd` into this directory, and run `pnpm start`. +3. Open the localhost URL displayed in the terminal output in your browser. diff --git a/demos/example-vite-custom-sql/package.json b/demos/example-vite-custom-sql/package.json new file mode 100644 index 000000000..bd579d15c --- /dev/null +++ b/demos/example-vite-custom-sql/package.json @@ -0,0 +1,22 @@ +{ + "name": "example-vite-custom-sql", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "start": "pnpm build && pnpm preview", + "test:build": "pnpm build" + }, + "dependencies": { + "@powersync/web": "workspace:*" + }, + "devDependencies": { + "@swc/core": "~1.6.0", + "vite": "^5.0.12", + "vite-plugin-top-level-await": "^1.4.1", + "vite-plugin-wasm": "^3.3.0" + } +} diff --git a/demos/example-vite-custom-sql/src/index.html b/demos/example-vite-custom-sql/src/index.html new file mode 100644 index 000000000..c7c4ba9aa --- /dev/null +++ b/demos/example-vite-custom-sql/src/index.html @@ -0,0 +1,9 @@ + + +
+ + + + Custom SQL demo: Check the console to see it in action! + + diff --git a/demos/example-vite-custom-sql/src/index.js b/demos/example-vite-custom-sql/src/index.js new file mode 100644 index 000000000..27d2a100d --- /dev/null +++ b/demos/example-vite-custom-sql/src/index.js @@ -0,0 +1,88 @@ +import { column, Schema, Table, PowerSyncDatabase } from '@powersync/web'; +import Logger from 'js-logger'; + +Logger.useDefaults(); + +/** + * A placeholder connector which doesn't do anything. + * This is just used to verify that the sync workers can be loaded + * when connecting. + */ +class DummyConnector { + async fetchCredentials() { + return { + endpoint: '', + token: '' + }; + } + + async uploadData(database) {} +} + +const customers = new Table({ first_name: column.text, last_name: column.text, full_name: column.text }); + +export const AppSchema = new Schema({ customers }, ({ getInternalName }) => [ + `DROP TRIGGER IF EXISTS compute_full_name`, + `DROP TRIGGER IF EXISTS update_full_name`, + + ` + CREATE TRIGGER compute_full_name + AFTER INSERT ON ${getInternalName('customers')} + BEGIN + UPDATE customers + SET full_name = first_name || ' ' || last_name + WHERE id = NEW.id; + END; + `, + ` + CREATE TRIGGER update_full_name + AFTER UPDATE OF data ON ${getInternalName('customers')} + BEGIN + UPDATE customers + SET full_name = first_name || ' ' || last_name + WHERE id = NEW.id AND full_name != (first_name || ' ' || last_name); + END; + ` +]); + +let PowerSync; + +const openDatabase = async () => { + PowerSync = new PowerSyncDatabase({ + schema: AppSchema, + database: { dbFilename: 'test.sqlite' } + }); + + await PowerSync.init(); + + await PowerSync.execute('DELETE FROM customers'); + + await PowerSync.execute('INSERT INTO customers(id, first_name, last_name) VALUES(uuid(), ?, ?)', ['John', 'Doe']); + + const result = await PowerSync.getAll('SELECT * FROM customers'); + + console.log('Contents of customers after insert: ', result); + + await PowerSync.execute('UPDATE customers SET first_name = ?', ['Jane']); + + const result2 = await PowerSync.getAll('SELECT * FROM customers'); + + console.log('Contents of customers after update: ', result2); + + console.log( + `Attempting to connect in order to verify web workers are correctly loaded. + This doesn't use any actual network credentials. + Network errors will be shown: these can be ignored.` + ); + + /** + * Try and connect, this will setup shared sync workers + * This will fail due to not having a valid endpoint, + * but it will try - which is all that matters. + */ + await PowerSync.connect(new DummyConnector()); +}; + +document.addEventListener('DOMContentLoaded', (event) => { + openDatabase(); +}); diff --git a/demos/example-vite-custom-sql/vite.config.ts b/demos/example-vite-custom-sql/vite.config.ts new file mode 100644 index 000000000..a1f909b03 --- /dev/null +++ b/demos/example-vite-custom-sql/vite.config.ts @@ -0,0 +1,27 @@ +import wasm from 'vite-plugin-wasm'; +import topLevelAwait from 'vite-plugin-top-level-await'; +import { defineConfig } from 'vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + root: 'src', + build: { + outDir: '../dist', + rollupOptions: { + input: 'src/index.html' + }, + emptyOutDir: true + }, + envDir: '..', // Use this dir for env vars, not 'src'. + optimizeDeps: { + // Don't optimize these packages as they contain web workers and WASM files. + // https://github.com/vitejs/vite/issues/11672#issuecomment-1415820673 + exclude: ['@journeyapps/wa-sqlite', '@powersync/web'], + include: ['@powersync/web > js-logger'] + }, + plugins: [wasm(), topLevelAwait()], + worker: { + format: 'es', + plugins: () => [wasm(), topLevelAwait()] + } +}); diff --git a/packages/common/src/client/AbstractPowerSyncDatabase.ts b/packages/common/src/client/AbstractPowerSyncDatabase.ts index 2e71083e7..2c2b78c8b 100644 --- a/packages/common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/common/src/client/AbstractPowerSyncDatabase.ts @@ -402,6 +402,15 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver