refactor(quartz): tighten projection package layout + relay flows from store
Three changes wrapped together:
1. relay moves up to IEventStore. The interface gains an abstract
`val relay: NormalizedRelayUrl?` and SQLiteEventStore, EventStore,
FsEventStore, InternedEventStore, and ObservableEventStore all
override it (decorators forward inner.relay). EventStoreProjection
loses its `relay` ctor param and uses `store.relay` for NIP-62
vanish scoping; ObservableEventStore.observe overloads lose their
relay arg. The relay was always a property of the store anyway —
threading it through projection construction was redundant.
2. StoreChange inlines into ObservableEventStore. The sealed type is
now ObservableEventStore.StoreChange (with Insert / DeleteByFilter /
DeleteExpired as nested cases) since it's strictly a contract of
the change stream, never used independently. StoreChange.kt deleted.
3. Package reshuffle:
store/projection/ObservableEventStore.kt → store/ObservableEventStore.kt
store/projection/EventStoreProjection.kt → cache/projection/EventStoreProjection.kt
store/projection/StoreChange.kt → inlined into ObservableEventStore
ObservableEventStore is a store decorator and lives next to
IEventStore. EventStoreProjection is a cache primitive (alongside
EventInterner / InternedEventStore) and lives under cache/. The
`observe(filters, scope)` convenience moved from a method on
ObservableEventStore to a top-level extension function in
cache/projection/, which avoids the otherwise-circular
store → cache/projection → store dependency.
7/7 interner + 17/17 projection + all other store tests pass.
https://claude.ai/code/session_01Jny85MTu1ynKgFBgysfWu5
This commit is contained in:
+12
-20
@@ -18,14 +18,14 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.store.projection
|
||||
package com.vitorpamplona.quartz.nip01Core.cache.projection
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip01Core.store.projection.ObservableEventStore
|
||||
import com.vitorpamplona.quartz.nip01Core.store.ObservableEventStore
|
||||
import com.vitorpamplona.quartz.nip01Core.store.sqlite.EventStore
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
@@ -96,7 +96,7 @@ class EventStoreProjectionTest {
|
||||
observable.insert(a)
|
||||
observable.insert(b)
|
||||
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
|
||||
val items = projection.items.value
|
||||
@@ -112,7 +112,7 @@ class EventStoreProjectionTest {
|
||||
val a = signer.sign(TextNoteEvent.build("a", createdAt = 100))
|
||||
observable.insert(a)
|
||||
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
val before = projection.items.value
|
||||
assertEquals(1, before.size)
|
||||
@@ -132,7 +132,7 @@ class EventStoreProjectionTest {
|
||||
val text = signer.sign(TextNoteEvent.build("a", createdAt = 100))
|
||||
observable.insert(text)
|
||||
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
val seed = projection.items.value
|
||||
|
||||
@@ -154,7 +154,6 @@ class EventStoreProjectionTest {
|
||||
val projection =
|
||||
observable.observe<MetadataEvent>(
|
||||
Filter(kinds = listOf(MetadataEvent.KIND), authors = listOf(v1.pubKey)),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -186,7 +185,6 @@ class EventStoreProjectionTest {
|
||||
authors = listOf(v1.pubKey),
|
||||
tags = mapOf("d" to listOf("blog")),
|
||||
),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -222,7 +220,6 @@ class EventStoreProjectionTest {
|
||||
val projection =
|
||||
observable.observe<MetadataEvent>(
|
||||
Filter(kinds = listOf(MetadataEvent.KIND), authors = listOf(v1.pubKey)),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -251,7 +248,7 @@ class EventStoreProjectionTest {
|
||||
observable.insert(a)
|
||||
observable.insert(b)
|
||||
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
assertEquals(2, projection.items.value.size)
|
||||
|
||||
@@ -273,7 +270,7 @@ class EventStoreProjectionTest {
|
||||
val a = signer.sign(TextNoteEvent.build("a", createdAt = 100))
|
||||
observable.insert(a)
|
||||
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
val seed = projection.items.value
|
||||
assertEquals(1, seed.size)
|
||||
@@ -301,7 +298,7 @@ class EventStoreProjectionTest {
|
||||
observable.insert(a)
|
||||
observable.insert(b)
|
||||
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
assertEquals(2, projection.items.value.size)
|
||||
|
||||
@@ -330,7 +327,7 @@ class EventStoreProjectionTest {
|
||||
val a = signer.sign(TextNoteEvent.build("a", createdAt = time))
|
||||
observable.insert(a)
|
||||
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<Event>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
val seed = projection.items.value
|
||||
|
||||
@@ -365,7 +362,6 @@ class EventStoreProjectionTest {
|
||||
val projection =
|
||||
observable.observe<Event>(
|
||||
Filter(kinds = listOf(TextNoteEvent.KIND)),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -400,7 +396,6 @@ class EventStoreProjectionTest {
|
||||
val projection =
|
||||
observable.observe<Event>(
|
||||
Filter(kinds = listOf(TextNoteEvent.KIND)),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -426,7 +421,6 @@ class EventStoreProjectionTest {
|
||||
val projection =
|
||||
observable.observe<TextNoteEvent>(
|
||||
Filter(kinds = listOf(TextNoteEvent.KIND), limit = 2),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -466,7 +460,6 @@ class EventStoreProjectionTest {
|
||||
val projection =
|
||||
observable.observe<TextNoteEvent>(
|
||||
listOf(filterA, filterB),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -490,7 +483,6 @@ class EventStoreProjectionTest {
|
||||
val projection =
|
||||
observable.observe<TextNoteEvent>(
|
||||
Filter(kinds = listOf(TextNoteEvent.KIND), limit = 2),
|
||||
store.relay,
|
||||
scope,
|
||||
)
|
||||
projection.ready.await()
|
||||
@@ -510,7 +502,7 @@ class EventStoreProjectionTest {
|
||||
val a = signer.sign(TextNoteEvent.build("a", createdAt = 100))
|
||||
observable.insert(a)
|
||||
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), store.relay, scope)
|
||||
val projection = observable.observe<TextNoteEvent>(Filter(kinds = listOf(TextNoteEvent.KIND)), scope)
|
||||
projection.ready.await()
|
||||
projection.close()
|
||||
|
||||
@@ -532,7 +524,7 @@ class EventStoreProjectionTest {
|
||||
runBlocking {
|
||||
val ephemeralKind = 22_000
|
||||
val projection =
|
||||
observable.observe<Event>(Filter(kinds = listOf(ephemeralKind)), store.relay, scope)
|
||||
observable.observe<Event>(Filter(kinds = listOf(ephemeralKind)), scope)
|
||||
projection.ready.await()
|
||||
assertTrue(projection.items.value.isEmpty())
|
||||
|
||||
@@ -555,7 +547,7 @@ class EventStoreProjectionTest {
|
||||
// A fresh projection on the same store gets nothing — the
|
||||
// event was only ever live, not durable.
|
||||
val freshProjection =
|
||||
observable.observe<Event>(Filter(kinds = listOf(ephemeralKind)), store.relay, scope)
|
||||
observable.observe<Event>(Filter(kinds = listOf(ephemeralKind)), scope)
|
||||
freshProjection.ready.await()
|
||||
assertTrue(freshProjection.items.value.isEmpty())
|
||||
|
||||
Reference in New Issue
Block a user