diff --git a/docs/lib/sample/app.zap b/docs/lib/sample/app.zap
new file mode 100644
index 000000000..dbab86e5e
--- /dev/null
+++ b/docs/lib/sample/app.zap
@@ -0,0 +1,74 @@
+
+
+
+
+ Todo list
+ This offline todo-list app is implemented with drift on the web.
+
+
+
+{#await database from dbFuture}
+ {#if database.hasData}
+
+ {#await each snapshot from database.data.items}
+ {#if snapshot.hasData}
+ {#for entry, i in snapshot.data}
+
+ {#if i != snapshot.data.length - 1}
+
+ {/if}
+ {/for}
+ {/if}
+ {/await}
+ {:else if database.hasError}
+ Sorry, we could not open the database on this browser!
+ {:else}
+
+ {/if}
+{/await}
+
+
+
+
diff --git a/docs/lib/sample/database.dart b/docs/lib/sample/database.dart
new file mode 100644
index 000000000..e12b26a81
--- /dev/null
+++ b/docs/lib/sample/database.dart
@@ -0,0 +1,105 @@
+import 'dart:async';
+
+import 'package:drift/drift.dart';
+import 'package:drift/wasm.dart';
+import 'package:rxdart/rxdart.dart';
+
+part 'database.g.dart';
+
+enum TodoListFilter { all, active, completed }
+
+class TodoItems extends Table {
+ IntColumn get id => integer().autoIncrement()();
+ TextColumn get description => text()();
+ BoolColumn get completed => boolean().withDefault(const Constant(false))();
+}
+
+@DriftDatabase(tables: [TodoItems])
+class Database extends _$Database {
+ final BehaviorSubject _filterChanges =
+ BehaviorSubject.seeded(TodoListFilter.all);
+
+ Database(super.e);
+
+ @override
+ int get schemaVersion => 1;
+
+ @override
+ MigrationStrategy get migration {
+ return MigrationStrategy(
+ onCreate: (m) async {
+ await m.createAll();
+
+ // Create some entries by default
+ await batch((b) {
+ b.insertAll(todoItems, [
+ TodoItemsCompanion.insert(description: 'Migrate to drift'),
+ TodoItemsCompanion.insert(description: 'Support all platforms'),
+ TodoItemsCompanion.insert(
+ description: 'Solve local persistence issues'),
+ ]);
+ });
+ },
+ );
+ }
+
+ TodoListFilter get currentFilter => _filterChanges.value;
+
+ set currentFilter(TodoListFilter value) {
+ _filterChanges.add(value);
+ }
+
+ Stream> get items => _filterChanges.stream.switchMap(_items);
+
+ Stream get uncompletedItems {
+ final all = countAll();
+ final query = selectOnly(todoItems)
+ ..addColumns([all])
+ ..where(todoItems.completed.not());
+
+ return query.map((row) => row.read(all)!).watchSingle();
+ }
+
+ Stream> _items(TodoListFilter filter) {
+ final query = todoItems.select();
+
+ switch (filter) {
+ case TodoListFilter.completed:
+ query.where((row) => row.completed);
+ case TodoListFilter.active:
+ query.where((row) => row.completed.not());
+ case TodoListFilter.all:
+ break;
+ }
+
+ return query.watch();
+ }
+
+ Future toggleCompleted(TodoItem item) async {
+ final statement = update(todoItems)..whereSamePrimaryKey(item);
+ await statement.write(TodoItemsCompanion.custom(
+ completed: todoItems.completed.not(),
+ ));
+ }
+}
+
+Future connect() async {
+ final result = await WasmDatabase.open(
+ databaseName: 'todo_example',
+ sqlite3Uri: Uri.parse('/sqlite3.wasm'),
+ driftWorkerUri: Uri.parse('/drift_worker.dart.js'),
+ );
+
+ if (!result.chosenImplementation.fullySupported) {
+ print('Using ${result.chosenImplementation} due to unsupported browser '
+ 'features: ${result.missingFeatures}');
+ }
+
+ return result;
+}
+
+extension CompatibilityUI on WasmStorageImplementation {
+ bool get fullySupported =>
+ this != WasmStorageImplementation.inMemory &&
+ this != WasmStorageImplementation.unsafeIndexedDb;
+}
diff --git a/docs/lib/sample/entry.zap b/docs/lib/sample/entry.zap
new file mode 100644
index 000000000..8dabb046b
--- /dev/null
+++ b/docs/lib/sample/entry.zap
@@ -0,0 +1,18 @@
+
+
+
diff --git a/docs/lib/sample/toolbar.zap b/docs/lib/sample/toolbar.zap
new file mode 100644
index 000000000..e19998436
--- /dev/null
+++ b/docs/lib/sample/toolbar.zap
@@ -0,0 +1,41 @@
+
+
+
+
+{#await each entry from database.uncompletedItems}
+ {#if entry.hasData}
+ {entry.data} {entry.data == 1 ? 'item' : 'items'} left
+ {/if}
+{/await}
+
+
diff --git a/docs/pubspec.yaml b/docs/pubspec.yaml
index 8f38b9645..45d2a7ddd 100644
--- a/docs/pubspec.yaml
+++ b/docs/pubspec.yaml
@@ -25,6 +25,10 @@ dependencies:
rxdart: ^0.27.3
yaml: ^3.1.1
drift_dev: any
+ zap: ^0.2.0
+ picocss:
+ hosted: https://simonbinder.eu
+ version: ^1.5.10
dev_dependencies:
lints: ^2.0.0
@@ -42,6 +46,7 @@ dev_dependencies:
source_span: ^1.9.1
test: ^1.18.0
sqlparser:
+ zap_dev: ^0.2.2
dependency_overrides:
diff --git a/docs/web/web/compatibility.dart b/docs/web/web/compatibility.dart
index 846527cc2..e5c7845e9 100644
--- a/docs/web/web/compatibility.dart
+++ b/docs/web/web/compatibility.dart
@@ -1,8 +1,13 @@
import 'dart:html';
import 'package:drift/wasm.dart';
+import 'package:drift_docs/site.dart' as i0;
+import 'package:docsy/main.dart' as i1;
void main() async {
+ i0.built_site_main();
+ i1.built_site_main();
+
final btn = querySelector('#drift-compat-btn')!;
final results = querySelector('#drift-compat-results')!;
diff --git a/docs/web/web/example/index.html b/docs/web/web/example/index.html
new file mode 100644
index 000000000..52c1be82a
--- /dev/null
+++ b/docs/web/web/example/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Drift web example
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/web/web/example/main.dart b/docs/web/web/example/main.dart
new file mode 100644
index 000000000..ac6223056
--- /dev/null
+++ b/docs/web/web/example/main.dart
@@ -0,0 +1,9 @@
+import 'dart:html';
+
+import 'package:drift_docs/sample/app.zap.dart';
+
+void main() {
+ final main = document.querySelector('main.container')!;
+
+ App().create(main);
+}
diff --git a/docs/web/web/example/style.scss b/docs/web/web/example/style.scss
new file mode 100644
index 000000000..e7b258775
--- /dev/null
+++ b/docs/web/web/example/style.scss
@@ -0,0 +1,2 @@
+@use "package:picocss/pico";
+@use "package:drift_docs/sample/app.zap";
diff --git a/netlify.toml b/netlify.toml
index 94d0e429c..e2de9174c 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -21,6 +21,12 @@
status = 301
force = true
+[[headers]]
+ for = "/*"
+ [headers.values]
+ Cross-Origin-Opener-Policy = "same-origin"
+ Cross-Origin-Embedder-Policy = "require-corp"
+
[context.production]
environment = { BUILD_RELEASE="release" }