Repository navigation
Add read-only subtype views - #45
Open
danReynolds wants to merge 7 commits into
Open
danReynolds wants to merge 7 commits into
danReynolds wants to merge 7 commits into
Conversation
`collection.view<S>()` and `document.view<S>()` read the documents whose data is an S. A view is the collection's ordinary query or document, filtered by type and given a narrower static type with extension types. Its snapshots, lists and streams are the store's own, so views need no projection objects, caches or invalidation, and no changes to existing classes: the query engine already reports a document that changes type as removed or added. QueryViewStreamBuilder and DocumentViewStreamBuilder build from views by delegating to the existing builders. Extension types require Dart 3.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`collection.view<S>()` returns a CollectionView again: a query view that
also reads documents by ID, as in `view<Dog>().doc('rex').get()`. It
replaces `document.view<S>()`, so views have one entry point.
A view's queries carry its type test, so a document read through a view is
narrowed by the view's own type even when the view's static type is widened.
`where` and `sortBy` share one helper that keeps the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Views no longer have their own stream builders, so the view layer depends only on Loon's core library and the existing builders are the only ones. Also tightens the views' documentation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The advertised stream-builder APIs are missing, and the documentation overstates the runtime allocation benefits.
Review effort: Balanced
Findings: 1
Open (3)
What changed in this PR
Adds read-only subtype views over collections using Dart extension types.
Changes:
- Introduces subtype-filtered collection, query, document, and snapshot views.
- Raises the minimum Dart SDK to 3.3 and documents the API.
- Adds extensive subtype-view tests and supporting test models.
| File | Description |
|---|---|
lib/src/views.dart |
Implements subtype view APIs. |
lib/loon.dart |
Exports the view API. |
pubspec.yaml |
Requires Dart 3.3. |
README.md |
Documents subtype views. |
CHANGELOG.md |
Records the feature and SDK requirement. |
test/core/collection_view_test.dart |
Tests view reads, streams, hydration, and caching. |
test/models/test_animal_model.dart |
Adds a polymorphic test model. |
test/models/test_persistor.dart |
Generalizes persistence seed snapshots. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| export 'src/utils/id.dart' show generateFastId, generateSecureId; | ||
| export 'src/widgets/document_stream_builder.dart'; | ||
| export 'src/widgets/query_stream_builder.dart'; | ||
| export 'src/views.dart'; |
| @@ -1,3 +1,8 @@ | |||
| ## Unreleased | |||
|
|
|||
| * Add `collection.view<S>()`: a live, read-only view of the documents whose data is an `S`, read by ID with `doc(id)` or through queries. Views are extension types over the collection's queries and documents, so they add no runtime objects. | |||
Comment on lines
+228
to
+230
| A view is the collection's own query, given a narrower type at compile time, so it | ||
| costs nothing extra at runtime: its snapshots are the store's own, and its lists and | ||
| streams are its query's. |
`view<S>()` on collections and queries puts the type test in the query, and a view's `where()` adds its filter there too. The query then selects the view's documents, so its change stream reports documents that enter or leave the view as added or removed, and each write rechecks only the documents it changed. - `sortBy` sorts the wrapped snapshots. - `where()` callbacks are typed by the view. - A document view's stream emits a single null while its document is missing or another type. - The view types are exported, and views are created only through `view()` and `doc()`. - Queries without filters return their results without copying them. - The README, changelog and SDK constraint describe class-based views, which need no newer Dart than main. - `.rk/` is ignored, and its lock file is no longer tracked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A view knows its type at runtime, so it adds the type test to the query it is given. `Collection.view` and `Query.view` pass a plain query, and views derived through `where`, `sortBy` and `observe` reuse their already selected query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Adds read-only subtype views: a collection of
Animalcan be read as itsDogs, by ID or through queries, with snapshots typed asDog.How it works
collection.view<S>()andquery.view<S>()add a type test to the underlying query, and a view'swhere()adds its filter to that query too. The query therefore selects the view's documents and handles hydration, dependency invalidation and incremental updates as it does for any query. A document that enters or leaves the view, by changing type or by starting or stopping to pass a filter, appears in the view's change stream as added or removed. The view wraps each result in aDocumentSnapshotView<S>whosedatais typed asS, and appliessortByto the wrapped snapshots.CollectionView.doc(id)returns aDocumentView<S>, which reads its document as absent while it is missing or not anS; its stream emits a single null for that time. Views are created only throughview()anddoc(), so every view's query includes its type test.The views live in
lib/src/views.dart, a part of the core library. Besidesview()onCollectionandQuery, the only engine change is that a query without filters returns its results without copying them.Notes
animals.doc(snap.id).update(...).wherefilter wraps each snapshot it checks. Against filtering after the query,get()measured the same, and the time from a write to its stream emission was 20–30% shorter, because only the changed document is rechecked.QueryStreamBuilderorDocumentStreamBuilder. To build from a view, observe it and use aStreamBuilder.reduceandorElsethrew) and views had no runtime type of their own.Validation
test/core/collection_view_test.darthas 20 tests, passing on the VM and in Chrome. They cover reads by type and ID, typed filters and sorting, membership changes in values and change streams (including filter transitions), hydration through the parent codec, dependency writes, ancestor deletion, multicast observers, widening a view's static type, and list operations that take view-typed values.flutter analyzereports no issues for the package and the example.🤖 Generated with Claude Code