diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2cd34e0..6f6cad4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -35,19 +35,19 @@ jobs: include: - sqlite_version: "3440200" sqlite_url: "https://www.sqlite.org/2023/sqlite-autoconf-3440200.tar.gz" - dart_sdk: 3.4.0 + dart_sdk: 3.5.0 - sqlite_version: "3430200" sqlite_url: "https://www.sqlite.org/2023/sqlite-autoconf-3430200.tar.gz" - dart_sdk: 3.4.0 + dart_sdk: 3.5.0 - sqlite_version: "3420000" sqlite_url: "https://www.sqlite.org/2023/sqlite-autoconf-3420000.tar.gz" - dart_sdk: 3.4.0 + dart_sdk: 3.5.0 - sqlite_version: "3410100" sqlite_url: "https://www.sqlite.org/2023/sqlite-autoconf-3410100.tar.gz" - dart_sdk: 3.4.0 + dart_sdk: 3.5.0 - sqlite_version: "3380000" sqlite_url: "https://www.sqlite.org/2022/sqlite-autoconf-3380000.tar.gz" - dart_sdk: 3.4.0 + dart_sdk: 3.5.0 steps: - uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1 diff --git a/packages/sqlite_async/CHANGELOG.md b/packages/sqlite_async/CHANGELOG.md index ab91443..b49c6ab 100644 --- a/packages/sqlite_async/CHANGELOG.md +++ b/packages/sqlite_async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.11.1 + +- Remove remaining `dart:js_util` imports in favor of new interop APIs. +- Add `WebSqliteOpenFactory` with web-specific behavior for open factories. + ## 0.11.0 - Automatically flush IndexedDB storage to fix durability issues diff --git a/packages/sqlite_async/lib/src/impl/isolate_connection_factory_impl.dart b/packages/sqlite_async/lib/src/impl/isolate_connection_factory_impl.dart index 4812688..ec70cb0 100644 --- a/packages/sqlite_async/lib/src/impl/isolate_connection_factory_impl.dart +++ b/packages/sqlite_async/lib/src/impl/isolate_connection_factory_impl.dart @@ -2,4 +2,4 @@ export 'stub_isolate_connection_factory.dart' // ignore: uri_does_not_exist if (dart.library.io) '../native/native_isolate_connection_factory.dart' // ignore: uri_does_not_exist - if (dart.library.html) '../web/web_isolate_connection_factory.dart'; + if (dart.library.js_interop) '../web/web_isolate_connection_factory.dart'; diff --git a/packages/sqlite_async/lib/src/impl/mutex_impl.dart b/packages/sqlite_async/lib/src/impl/mutex_impl.dart index 6b77de7..0f45fe4 100644 --- a/packages/sqlite_async/lib/src/impl/mutex_impl.dart +++ b/packages/sqlite_async/lib/src/impl/mutex_impl.dart @@ -2,4 +2,4 @@ export 'stub_mutex.dart' // ignore: uri_does_not_exist if (dart.library.io) '../native/native_isolate_mutex.dart' // ignore: uri_does_not_exist - if (dart.library.html) '../web/web_mutex.dart'; + if (dart.library.js_interop) '../web/web_mutex.dart'; diff --git a/packages/sqlite_async/lib/src/impl/open_factory_impl.dart b/packages/sqlite_async/lib/src/impl/open_factory_impl.dart index e8ce4e1..a7a27ac 100644 --- a/packages/sqlite_async/lib/src/impl/open_factory_impl.dart +++ b/packages/sqlite_async/lib/src/impl/open_factory_impl.dart @@ -2,4 +2,4 @@ export 'stub_sqlite_open_factory.dart' // ignore: uri_does_not_exist if (dart.library.io) '../native/native_sqlite_open_factory.dart' // ignore: uri_does_not_exist - if (dart.library.html) '../web/web_sqlite_open_factory.dart'; + if (dart.library.js_interop) '../web/web_sqlite_open_factory.dart'; diff --git a/packages/sqlite_async/lib/src/impl/sqlite_database_impl.dart b/packages/sqlite_async/lib/src/impl/sqlite_database_impl.dart index a2bcd20..cf1dfbb 100644 --- a/packages/sqlite_async/lib/src/impl/sqlite_database_impl.dart +++ b/packages/sqlite_async/lib/src/impl/sqlite_database_impl.dart @@ -2,4 +2,4 @@ export 'stub_sqlite_database.dart' // ignore: uri_does_not_exist if (dart.library.io) '../native/database/native_sqlite_database.dart' // ignore: uri_does_not_exist - if (dart.library.html) '../web/database/web_sqlite_database.dart'; + if (dart.library.js_interop) '../web/database/web_sqlite_database.dart'; diff --git a/packages/sqlite_async/lib/src/web/web_mutex.dart b/packages/sqlite_async/lib/src/web/web_mutex.dart index 38f7ff5..8c2baa5 100644 --- a/packages/sqlite_async/lib/src/web/web_mutex.dart +++ b/packages/sqlite_async/lib/src/web/web_mutex.dart @@ -4,7 +4,6 @@ import 'dart:math'; import 'package:meta/meta.dart'; import 'package:mutex/mutex.dart' as mutex; import 'dart:js_interop'; -import 'dart:js_util' as js_util; // This allows for checking things like hasProperty without the need for depending on the `js` package import 'dart:js_interop_unsafe'; import 'package:web/web.dart'; @@ -128,7 +127,7 @@ class MutexImpl implements Mutex { .request(resolvedIdentifier, lockOptions, jsCallback.toJS); // A timeout abort will throw an exception which needs to be handled. // There should not be any other unhandled lock errors. - js_util.promiseToFuture(promise).catchError((error) {}); + promise.toDart.catchError((error) => null); return gotLock.future; } diff --git a/packages/sqlite_async/lib/src/web/web_sqlite_open_factory.dart b/packages/sqlite_async/lib/src/web/web_sqlite_open_factory.dart index 66000f9..247999d 100644 --- a/packages/sqlite_async/lib/src/web/web_sqlite_open_factory.dart +++ b/packages/sqlite_async/lib/src/web/web_sqlite_open_factory.dart @@ -5,33 +5,45 @@ import 'package:sqlite3_web/sqlite3_web.dart'; import 'package:sqlite_async/sqlite_async.dart'; import 'package:sqlite_async/src/web/database/broadcast_updates.dart'; import 'package:sqlite_async/src/web/web_mutex.dart'; +import 'package:sqlite_async/web.dart'; import 'database.dart'; +import 'worker/worker_utils.dart'; Map> webSQLiteImplementations = {}; /// Web implementation of [AbstractDefaultSqliteOpenFactory] class DefaultSqliteOpenFactory - extends AbstractDefaultSqliteOpenFactory { - final Future _initialized; + extends AbstractDefaultSqliteOpenFactory + implements WebSqliteOpenFactory { + late final Future _initialized = Future.sync(() { + final cacheKey = sqliteOptions.webSqliteOptions.wasmUri + + sqliteOptions.webSqliteOptions.workerUri; + + if (webSQLiteImplementations.containsKey(cacheKey)) { + return webSQLiteImplementations[cacheKey]!; + } + + webSQLiteImplementations[cacheKey] = + openWebSqlite(sqliteOptions.webSqliteOptions); + return webSQLiteImplementations[cacheKey]!; + }); DefaultSqliteOpenFactory( {required super.path, - super.sqliteOptions = const SqliteOptions.defaults()}) - : _initialized = Future.sync(() { - final cacheKey = sqliteOptions.webSqliteOptions.wasmUri + - sqliteOptions.webSqliteOptions.workerUri; - - if (webSQLiteImplementations.containsKey(cacheKey)) { - return webSQLiteImplementations[cacheKey]!; - } - - webSQLiteImplementations[cacheKey] = WebSqlite.open( - wasmModule: Uri.parse(sqliteOptions.webSqliteOptions.wasmUri), - worker: Uri.parse(sqliteOptions.webSqliteOptions.workerUri), - ); - return webSQLiteImplementations[cacheKey]!; - }); + super.sqliteOptions = const SqliteOptions.defaults()}) { + // Make sure initializer starts running immediately + _initialized; + } + + @override + Future openWebSqlite(WebSqliteOptions options) async { + return WebSqlite.open( + wasmModule: Uri.parse(sqliteOptions.webSqliteOptions.wasmUri), + worker: Uri.parse(sqliteOptions.webSqliteOptions.workerUri), + controller: AsyncSqliteController(), + ); + } @override diff --git a/packages/sqlite_async/lib/src/web/worker/throttled_common_database.dart b/packages/sqlite_async/lib/src/web/worker/throttled_common_database.dart index ea73bd6..07264bf 100644 --- a/packages/sqlite_async/lib/src/web/worker/throttled_common_database.dart +++ b/packages/sqlite_async/lib/src/web/worker/throttled_common_database.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:sqlite_async/sqlite3_common.dart'; +import 'package:sqlite_async/sqlite3_wasm.dart'; /// Wrap a CommonDatabase to throttle its updates stream. /// This is so that we can throttle the updates _within_ @@ -103,6 +103,17 @@ class ThrottledCommonDatabase extends CommonDatabase { Stream get updates { return throttledUpdates(_db, _transactionController.stream); } + + @override + VoidPredicate? get commitFilter => _db.commitFilter; + + set commitFilter(VoidPredicate? filter) => _db.commitFilter = filter; + + @override + Stream get commits => _db.commits; + + @override + Stream get rollbacks => _db.rollbacks; } /// This throttles the database update stream to: diff --git a/packages/sqlite_async/lib/src/web/worker/worker_utils.dart b/packages/sqlite_async/lib/src/web/worker/worker_utils.dart index 1d8fb5c..5a6e1b3 100644 --- a/packages/sqlite_async/lib/src/web/worker/worker_utils.dart +++ b/packages/sqlite_async/lib/src/web/worker/worker_utils.dart @@ -1,5 +1,4 @@ import 'dart:js_interop'; -import 'dart:js_util' as js_util; import 'package:mutex/mutex.dart'; import 'package:sqlite3/wasm.dart'; @@ -73,7 +72,7 @@ class AsyncSqliteDatabase extends WorkerDatabase { var dartMap = resultSetToMap(res); - var jsObject = js_util.jsify(dartMap); + var jsObject = dartMap.jsify(); return jsObject; case CustomDatabaseMessageKind.executeBatchInTransaction: diff --git a/packages/sqlite_async/lib/web.dart b/packages/sqlite_async/lib/web.dart index a4005e1..c7f9628 100644 --- a/packages/sqlite_async/lib/web.dart +++ b/packages/sqlite_async/lib/web.dart @@ -6,6 +6,8 @@ library sqlite_async.web; import 'package:sqlite3_web/sqlite3_web.dart'; import 'package:web/web.dart'; + +import 'sqlite3_common.dart'; import 'sqlite_async.dart'; import 'src/web/database.dart'; @@ -24,6 +26,22 @@ typedef WebDatabaseEndpoint = ({ String? lockName, }); +/// An additional interface for [SqliteOpenFactory] exposing additional +/// functionality that is only relevant when compiling to the web. +/// +/// The [DefaultSqliteOpenFactory] class implements this interface only when +/// compiling for the web. +abstract interface class WebSqliteOpenFactory + implements SqliteOpenFactory { + /// Opens a [WebSqlite] instance for the given [options]. + /// + /// This method can be overriden in scenarios where the way [WebSqlite] is + /// opened needs to be customized. Implementers should be aware that the + /// result of this method is cached and will be re-used by the open factory + /// when provided with the same [options] again. + Future openWebSqlite(WebSqliteOptions options); +} + /// A [SqliteConnection] interface implemented by opened connections when /// running on the web. /// diff --git a/packages/sqlite_async/pubspec.yaml b/packages/sqlite_async/pubspec.yaml index 09adc13..1ffa8b4 100644 --- a/packages/sqlite_async/pubspec.yaml +++ b/packages/sqlite_async/pubspec.yaml @@ -1,9 +1,9 @@ name: sqlite_async description: High-performance asynchronous interface for SQLite on Dart and Flutter. -version: 0.11.0 +version: 0.11.1 repository: https://github.com/powersync-ja/sqlite_async.dart environment: - sdk: ">=3.4.0 <4.0.0" + sdk: ">=3.5.0 <4.0.0" topics: - sqlite @@ -12,8 +12,8 @@ topics: - flutter dependencies: - sqlite3: "^2.4.7" - sqlite3_web: ^0.2.0 + sqlite3: ^2.7.2 + sqlite3_web: ^0.2.2 async: ^2.10.0 collection: ^1.17.0 mutex: ^3.1.0 @@ -22,7 +22,6 @@ dependencies: dev_dependencies: dcli: ^4.0.0 - js: ^0.6.7 lints: ^3.0.0 test: ^1.21.0 test_api: ^0.7.0 diff --git a/packages/sqlite_async/test/utils/test_utils_impl.dart b/packages/sqlite_async/test/utils/test_utils_impl.dart index 99a34d3..3406d1e 100644 --- a/packages/sqlite_async/test/utils/test_utils_impl.dart +++ b/packages/sqlite_async/test/utils/test_utils_impl.dart @@ -2,4 +2,4 @@ export 'stub_test_utils.dart' // ignore: uri_does_not_exist if (dart.library.io) 'native_test_utils.dart' // ignore: uri_does_not_exist - if (dart.library.html) 'web_test_utils.dart'; + if (dart.library.js_interop) 'web_test_utils.dart'; diff --git a/packages/sqlite_async/test/utils/web_test_utils.dart b/packages/sqlite_async/test/utils/web_test_utils.dart index 2aec683..32b7e05 100644 --- a/packages/sqlite_async/test/utils/web_test_utils.dart +++ b/packages/sqlite_async/test/utils/web_test_utils.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'dart:html'; +import 'dart:js_interop'; -import 'package:js/js.dart'; import 'package:sqlite_async/sqlite_async.dart'; import 'package:test/test.dart'; +import 'package:web/web.dart' show Blob, BlobPart, BlobPropertyBag; import 'abstract_test_utils.dart'; @JS('URL.createObjectURL') @@ -24,8 +24,8 @@ class TestUtils extends AbstractTestUtils { // Cross origin workers are not supported, but we can supply a Blob var sqliteUri = 'http://localhost:$port/db_worker.js'; - final blob = Blob( - ['importScripts("$sqliteUri");'], 'application/javascript'); + final blob = Blob(['importScripts("$sqliteUri");'.toJS].toJS, + BlobPropertyBag(type: 'application/javascript')); sqliteUri = _createObjectURL(blob); webOptions = SqliteOptions(