Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ jobs:
lint:
name: Check lints
runs-on: ubuntu-latest
if: ${{ false }}
steps:
- uses: actions/checkout@v3
- name: Setup Flutter
Expand All @@ -45,7 +44,76 @@ jobs:
dart tool/generate_all_tests.dart
working-directory: packages/isar_test
- name: Lint
run: flutter analyze
run: flutter analyze --no-fatal-infos

dart_backends:
name: Dart memory and web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ secrets.FLUTTER_VERSION }}
- name: Resolve Isar
run: dart pub get
working-directory: packages/isar
- name: Test memory without native core
run: dart test test/memory_engine_test.dart
working-directory: packages/isar
- name: Resolve backend conformance tests
run: flutter pub get
working-directory: packages/isar_test
- name: Test full memory backend conformance
run: flutter test -j 1 --dart-define=ISAR_TEST_BACKEND=memory
working-directory: packages/isar_test
- name: Test IndexedDB in Chrome
run: dart test -p chrome test/web_indexeddb_test.dart
working-directory: packages/isar
- name: Test IndexedDB with WebAssembly
run: dart test -p chrome --compiler dart2wasm test/web_indexeddb_test.dart
working-directory: packages/isar
- name: Compile JavaScript boundary
run: dart compile js tool/compile_smoke.dart -o /tmp/isar-smoke.js
working-directory: packages/isar
- name: Compile WebAssembly boundary
run: dart compile wasm tool/compile_smoke.dart -o /tmp/isar-smoke.wasm
working-directory: packages/isar
- name: Build inspector JavaScript
run: flutter build web
working-directory: packages/isar_inspector
- name: Build inspector WebAssembly
run: flutter build web --wasm
working-directory: packages/isar_inspector
- name: Reject retired web bridge
run: |
! rg "dart:(html|indexed_db|js'|js_util)|package:js/|unpkg.com/isar" \
packages

web_conformance:
name: Web conformance (${{ matrix.compiler }}, shard ${{ matrix.shard }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
compiler: [dart2js, dart2wasm]
shard: [0, 1, 2, 3]
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ secrets.FLUTTER_VERSION }}
- name: Resolve test dependencies
run: flutter pub get
working-directory: packages/isar_test
- name: Generate test models
run: dart run build_runner build --delete-conflicting-outputs
working-directory: packages/isar_test
- name: Generate conformance aggregator
run: dart tool/generate_all_tests.dart
working-directory: packages/isar_test
- name: Run shared web conformance
run: dart test -p chrome --compiler ${{ matrix.compiler }} test/web_conformance_${{ matrix.shard }}_test.dart --reporter=failures-only
working-directory: packages/isar_test

test:
name: Dart Test
Expand Down
20 changes: 20 additions & 0 deletions packages/isar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,26 @@ final isar = await Isar.open(
);
```

On web, omit `directory`. `Isar.open()` automatically uses IndexedDB and is
asynchronous. The web backend is currently beta: it hydrates the database into
memory and executes scan-based queries, so avoid making large-database
performance claims. Browser quota and private-mode restrictions apply.
Synchronous APIs, inspector, isolates, compact/copy, and multi-tab writers are
not supported on web. Both JavaScript and WebAssembly builds are supported.
Records are stored individually in per-collection IndexedDB object stores.
Commits update records, links, counters, schema metadata, and a monotonic
revision in one atomic transaction. Adding or removing collections, properties,
indexes, or links is migrated automatically; changing an existing property's
type is rejected. A BroadcastChannel lease permits one owner per database name,
and revision checks reject stale writers.

For disposable databases in tests or tools, import `package:isar/isar_memory.dart`
and use `IsarMemory.open()` or `IsarMemory.openSync()`. Memory instances load no
native library, create no files, and discard their state when closed. Disk
persistence and schema migration, isolates, configured file-size limits, and
filesystem compact/copy operations are intentionally unavailable for memory
instances; CRUD, transactions, queries, links, JSON, and watchers are supported.

### 4. Query the database

```dart
Expand Down
14 changes: 8 additions & 6 deletions packages/isar/lib/isar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import 'dart:convert';
import 'dart:developer';
import 'dart:typed_data';

import 'package:isar/src/common/platform.dart'
if (dart.library.js_interop) 'package:isar/src/common/platform_web.dart'
as platform;
import 'package:isar/src/isar_connect_api.dart';
import 'package:isar/src/native/isar_core.dart'
if (dart.library.html) 'package:isar/src/web/isar_web.dart';
if (dart.library.js_interop) 'package:isar/src/web/isar_web.dart';
import 'package:isar/src/native/isar_link_impl.dart'
if (dart.library.html) 'package:isar/src/web/isar_link_impl.dart';
if (dart.library.js_interop) 'package:isar/src/dart_engine/link_impl.dart';
import 'package:isar/src/native/open.dart'
if (dart.library.html) 'package:isar/src/web/open.dart';
import 'package:isar/src/native/split_words.dart'
if (dart.library.html) 'package:isar/src/web/split_words.dart';
if (dart.library.js_interop) 'package:isar/src/web/indexeddb_open.dart';
import 'package:isar/src/web/split_words.dart';
import 'package:meta/meta.dart';
import 'package:meta/meta_meta.dart';

Expand Down Expand Up @@ -46,4 +48,4 @@ part 'src/schema/schema.dart';
@protected
typedef IsarUint8List = Uint8List;

const bool _kIsWeb = identical(0, 0.0);
const bool _kIsWeb = platform.isWeb;
51 changes: 51 additions & 0 deletions packages/isar/lib/isar_memory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
library isar_memory;

import 'package:isar/isar.dart';
import 'package:isar/src/dart_engine/engine.dart';

/// Opens disposable Isar databases backed entirely by Dart memory.
abstract final class IsarMemory {
/// Opens an in-memory database asynchronously.
static Future<Isar> open(
List<CollectionSchema<dynamic>> schemas, {
String name = Isar.defaultName,
}) async =>
openSync(schemas, name: name);

/// Opens an in-memory database synchronously.
static Isar openSync(
List<CollectionSchema<dynamic>> schemas, {
String name = Isar.defaultName,
}) {
_validate(name, schemas);
return DartEngineIsar(name, schemas);
}

static void _validate(
String name,
List<CollectionSchema<dynamic>> schemas,
) {
if (name.isEmpty || name.startsWith('_')) {
throw IsarError('Instance names must not be empty or start with "_".');
}
if (Isar.getInstance(name) != null) {
throw IsarError('Instance has already been opened.');
}
if (schemas.isEmpty) {
throw IsarError('At least one collection needs to be opened.');
}
final names = schemas.map((schema) => schema.name).toSet();
if (names.length != schemas.length) {
throw IsarError('Duplicate collection schema.');
}
for (final schema in schemas) {
for (final target in schema.links.values.map((link) => link.target)) {
if (!names.contains(target)) {
throw IsarError(
'Collection ${schema.name} depends on missing collection $target.',
);
}
}
}
}
}
4 changes: 2 additions & 2 deletions packages/isar/lib/src/common/isar_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ abstract class IsarCommon extends Isar {
await Future.wait(_activeAsyncTxns);
await super.close();

return performClose(deleteFromDisk);
return await performClose(deleteFromDisk);
}

/// @nodoc
bool performClose(bool deleteFromDisk);
FutureOr<bool> performClose(bool deleteFromDisk);
}

/// @nodoc
Expand Down
22 changes: 22 additions & 0 deletions packages/isar/lib/src/common/isar_link_backend.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:isar/isar.dart';

/// Internal backend-neutral dispatch used by generated link objects.
abstract interface class IsarLinkBackend {
Future<void> updateLinkBackend<OBJ>({
required IsarCollection<OBJ> targetCollection,
required String linkName,
required Id sourceId,
required Iterable<OBJ> link,
required Iterable<OBJ> unlink,
required bool reset,
});

void updateLinkBackendSync<OBJ>({
required IsarCollection<OBJ> targetCollection,
required String linkName,
required Id sourceId,
required Iterable<OBJ> link,
required Iterable<OBJ> unlink,
required bool reset,
});
}
9 changes: 5 additions & 4 deletions packages/isar/lib/src/common/isar_link_common.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:isar/isar.dart';
import 'package:isar/src/common/isar_link_base_impl.dart';

const bool _kIsWeb = identical(0, 0.0);
import 'package:isar/src/common/platform.dart'
if (dart.library.js_interop) 'package:isar/src/common/platform_web.dart'
as platform;

/// @nodoc
abstract class IsarLinkCommon<OBJ> extends IsarLinkBaseImpl<OBJ>
with IsarLink<OBJ> {
implements IsarLink<OBJ> {
OBJ? _value;

@override
Expand All @@ -16,7 +17,7 @@ abstract class IsarLinkCommon<OBJ> extends IsarLinkBaseImpl<OBJ>

@override
OBJ? get value {
if (isAttached && !isLoaded && !isChanged && !_kIsWeb) {
if (isAttached && !isLoaded && !isChanged && !platform.isWeb) {
loadSync();
}
return _value;
Expand Down
16 changes: 12 additions & 4 deletions packages/isar/lib/src/common/isar_links_common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import 'dart:collection';

import 'package:isar/isar.dart';
import 'package:isar/src/common/isar_link_base_impl.dart';

const bool _kIsWeb = identical(0, 0.0);
import 'package:isar/src/common/platform.dart'
if (dart.library.js_interop) 'package:isar/src/common/platform_web.dart'
as platform;

/// @nodoc
abstract class IsarLinksCommon<OBJ> extends IsarLinkBaseImpl<OBJ>
with IsarLinks<OBJ>, SetMixin<OBJ> {
with SetMixin<OBJ>
implements IsarLinks<OBJ> {
final _objects = <Id, OBJ>{};

/// @nodoc
Expand All @@ -22,8 +24,14 @@ abstract class IsarLinksCommon<OBJ> extends IsarLinkBaseImpl<OBJ>
@override
bool get isChanged => addedObjects.isNotEmpty || removedObjects.isNotEmpty;

@override
Future<int> count() => filter().count();

@override
int countSync() => filter().countSync();

Map<Id, OBJ> get _loadedObjects {
if (isAttached && !isLoaded && !_kIsWeb) {
if (isAttached && !isLoaded && !platform.isWeb) {
loadSync();
}
return _objects;
Expand Down
1 change: 1 addition & 0 deletions packages/isar/lib/src/common/platform.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const bool isWeb = false;
1 change: 1 addition & 0 deletions packages/isar/lib/src/common/platform_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const bool isWeb = true;
Loading