Skip to content

Commit aadb5ea

Browse files
authored
Merge pull request #753 from slashv/fix-513-event-streaming
2 parents a857645 + 561887b commit aadb5ea

52 files changed

Lines changed: 2842 additions & 109 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { Store } from '@livestore/livestore'
2+
3+
declare const store: Store
4+
5+
for await (const event of store.events()) {
6+
console.log('event from leader', event)
7+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Store } from '@livestore/livestore'
2+
3+
declare const store: Store
4+
5+
// Run once
6+
for await (const event of store.events()) {
7+
console.log('event from leader', event)
8+
}
9+
10+
// Continuos stream
11+
const iterator = store.events()[Symbol.asyncIterator]()
12+
try {
13+
while (true) {
14+
const { value, done } = await iterator.next()
15+
if (done) break
16+
console.log('event from stream:', value)
17+
}
18+
} finally {
19+
await iterator.return?.()
20+
}

docs/src/content/docs/building-with-livestore/events.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { EventsVisualizer } from '../../../components/EventsVisualizer'
1111
import EventNodesDiagram from '../../_assets/diagrams/event-nodes.tldr?tldraw'
1212
import UnknownEventHandlingSnippet from '../../_assets/code/reference/events/unknown-event-handling.ts?snippet'
1313
import EventTypeNamespacesSnippet from '../../_assets/code/reference/events/event-type-namespaces.ts?snippet'
14+
import StreamEventsSnippet from '../../_assets/code/reference/events/stream.ts?snippet'
1415

1516
## Event definitions
1617

@@ -31,6 +32,12 @@ Events will be synced across clients and materialized into state (i.e. SQLite ta
3132

3233
<CommitEventSnippet />
3334

35+
### Streaming events
36+
37+
Currently only events confirmed by the sync backend are supported.
38+
39+
<StreamEventsSnippet />
40+
3441
### Best practices
3542

3643
- It's strongly recommended to use past-tense event names (e.g. `todoCreated`/`createdTodo` instead of `todoCreate`/`createTodo`) to indicate something already occurred.

docs/src/content/docs/building-with-livestore/store.mdx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import CreateStoreSnippet from '../../_assets/code/reference/store/create-store.
99
import QueryDataSnippet from '../../_assets/code/reference/store/query-data.ts?snippet'
1010
import ShutdownSnippet from '../../_assets/code/reference/store/shutdown.ts?snippet'
1111
import SubscribeSnippet from '../../_assets/code/reference/store/subscribe.ts?snippet'
12+
import StreamEventsSnippet from '../../_assets/code/reference/store/stream-events.ts?snippet'
1213

1314
The `Store` is the most common way to interact with LiveStore from your application code. It provides a way to query data, commit events, and subscribe to data changes.
1415

@@ -32,6 +33,12 @@ For how to create a store in React, see the [React integration docs](/framework-
3233

3334
<CommitEventSnippet />
3435

36+
### Streaming events
37+
38+
Currently only events confirmed by the sync backend are supported.
39+
40+
<StreamEventsSnippet />
41+
3542
### Shutting down a store
3643

3744
LiveStore provides two APIs for shutting down a store:
@@ -58,4 +65,4 @@ __debugLiveStore.default._dev.hardReset()
5865

5966
// See the current sync state
6067
__debugLiveStore.default._dev.syncStates()
61-
```
68+
```

packages/@livestore/adapter-cloudflare/src/make-adapter.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ import {
77
type SyncOptions,
88
UnknownError,
99
} from '@livestore/common'
10-
import { type DevtoolsOptions, Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
10+
import {
11+
type DevtoolsOptions,
12+
Eventlog,
13+
LeaderThreadCtx,
14+
makeLeaderThreadLayer,
15+
streamEventsWithSyncState,
16+
} from '@livestore/common/leader-thread'
1117
import type { CfTypes } from '@livestore/common-cf'
1218
import { LiveStoreEvent } from '@livestore/livestore'
1319
import { sqliteDbFactory } from '@livestore/sqlite-wasm/cf'
@@ -112,6 +118,12 @@ export const makeAdapter =
112118
batch.map((item) => new LiveStoreEvent.Client.EncodedWithMeta(item)),
113119
{ waitForProcessing: true },
114120
),
121+
stream: (options) =>
122+
streamEventsWithSyncState({
123+
dbEventlog,
124+
syncState: syncProcessor.syncState,
125+
options,
126+
}),
115127
},
116128
initialState: { leaderHead: initialLeaderHead, migrationsReport: initialState.migrationsReport },
117129
export: Effect.sync(() => dbState.export()),

packages/@livestore/adapter-expo/src/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ import {
1313
UnknownError,
1414
} from '@livestore/common'
1515
import type { DevtoolsOptions, LeaderSqliteDb } from '@livestore/common/leader-thread'
16-
import { Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
16+
import {
17+
Eventlog,
18+
LeaderThreadCtx,
19+
makeLeaderThreadLayer,
20+
streamEventsWithSyncState,
21+
} from '@livestore/common/leader-thread'
1722
import type { LiveStoreSchema } from '@livestore/common/schema'
1823
import { LiveStoreEvent } from '@livestore/common/schema'
1924
import { shouldNeverHappen } from '@livestore/utils'
@@ -300,6 +305,12 @@ const makeLeaderThread = ({
300305
{ waitForProcessing: true },
301306
)
302307
.pipe(Effect.provide(layer), Effect.scoped),
308+
stream: (options) =>
309+
streamEventsWithSyncState({
310+
dbEventlog,
311+
syncState: syncProcessor.syncState,
312+
options,
313+
}),
303314
},
304315
initialState: { leaderHead: initialLeaderHead, migrationsReport: initialState.migrationsReport },
305316
export: Effect.sync(() => db.export()),

packages/@livestore/adapter-node/src/client-session/adapter.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
type SyncOptions,
1414
UnknownError,
1515
} from '@livestore/common'
16-
import { Eventlog, LeaderThreadCtx } from '@livestore/common/leader-thread'
16+
import { Eventlog, LeaderThreadCtx, streamEventsWithSyncState } from '@livestore/common/leader-thread'
1717
import type { LiveStoreSchema } from '@livestore/common/schema'
1818
import { LiveStoreEvent } from '@livestore/common/schema'
1919
import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
@@ -406,6 +406,12 @@ const makeLocalLeaderThread = ({
406406
batch.map((item) => new LiveStoreEvent.Client.EncodedWithMeta(item)),
407407
{ waitForProcessing: true },
408408
),
409+
stream: (options) =>
410+
streamEventsWithSyncState({
411+
dbEventlog,
412+
syncState: syncProcessor.syncState,
413+
options,
414+
}),
409415
},
410416
initialState: { leaderHead: initialLeaderHead, migrationsReport: initialState.migrationsReport },
411417
export: Effect.sync(() => dbState.export()),
@@ -546,6 +552,11 @@ const makeWorkerLeaderThread = ({
546552
attributes: { batchSize: batch.length },
547553
}),
548554
),
555+
stream: (options) =>
556+
runInWorkerStream(new WorkerSchema.LeaderWorkerInnerStreamEvents(options)).pipe(
557+
Stream.withSpan('@livestore/adapter-node:client-session:streamEvents'),
558+
Stream.orDie,
559+
),
549560
},
550561
initialState: {
551562
leaderHead: initialLeaderHead,

packages/@livestore/adapter-node/src/make-leader-worker.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ if (process.execArgv.includes('--inspect')) {
99

1010
import type { SyncOptions } from '@livestore/common'
1111
import { LogConfig, UnknownError } from '@livestore/common'
12-
import { Eventlog, LeaderThreadCtx } from '@livestore/common/leader-thread'
12+
import type { StreamEventsOptions } from '@livestore/common/leader-thread'
13+
import { Eventlog, LeaderThreadCtx, streamEventsWithSyncState } from '@livestore/common/leader-thread'
1314
import type { LiveStoreSchema } from '@livestore/common/schema'
1415
import { LiveStoreEvent } from '@livestore/common/schema'
1516
import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
@@ -86,6 +87,20 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
8687
const { syncProcessor } = yield* LeaderThreadCtx
8788
return syncProcessor.pull({ cursor })
8889
}).pipe(Stream.unwrapScoped),
90+
StreamEvents: (options: WorkerSchema.LeaderWorkerInnerStreamEvents) =>
91+
LeaderThreadCtx.pipe(
92+
Effect.map(({ dbEventlog, syncProcessor }) => {
93+
const { _tag: _ignored, ...payload } = options
94+
const streamOptions = payload as StreamEventsOptions
95+
return streamEventsWithSyncState({
96+
dbEventlog,
97+
syncState: syncProcessor.syncState,
98+
options: streamOptions,
99+
})
100+
}),
101+
Stream.unwrapScoped,
102+
Stream.withSpan('@livestore/adapter-node:worker:StreamEvents'),
103+
),
89104
Export: () =>
90105
Effect.andThen(LeaderThreadCtx, (_) => _.dbState.export()).pipe(
91106
UnknownError.mapToUnknownError,

packages/@livestore/adapter-node/src/worker-schema.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
SyncState,
88
UnknownError,
99
} from '@livestore/common'
10+
import { StreamEventsOptionsFields } from '@livestore/common/leader-thread'
1011
import { EventSequenceNumber, LiveStoreEvent } from '@livestore/common/schema'
1112
import { Schema, Transferable } from '@livestore/utils/effect'
1213

@@ -111,6 +112,15 @@ export class LeaderWorkerInnerPullStream extends Schema.TaggedRequest<LeaderWork
111112
failure: UnknownError,
112113
}) {}
113114

115+
export class LeaderWorkerInnerStreamEvents extends Schema.TaggedRequest<LeaderWorkerInnerStreamEvents>()(
116+
'StreamEvents',
117+
{
118+
payload: StreamEventsOptionsFields,
119+
success: LiveStoreEvent.Client.Encoded,
120+
failure: UnknownError,
121+
},
122+
) {}
123+
114124
export class LeaderWorkerInnerPushToLeader extends Schema.TaggedRequest<LeaderWorkerInnerPushToLeader>()(
115125
'PushToLeader',
116126
{
@@ -215,6 +225,7 @@ export const LeaderWorkerInnerRequest = Schema.Union(
215225
LeaderWorkerInnerInitialMessage,
216226
LeaderWorkerInnerBootStatusStream,
217227
LeaderWorkerInnerPullStream,
228+
LeaderWorkerInnerStreamEvents,
218229
LeaderWorkerInnerPushToLeader,
219230
LeaderWorkerInnerExport,
220231
LeaderWorkerInnerGetRecreateSnapshot,

packages/@livestore/adapter-web/src/in-memory/in-memory-adapter.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ import {
99
UnknownError,
1010
} from '@livestore/common'
1111
import type { DevtoolsOptions, LeaderSqliteDb } from '@livestore/common/leader-thread'
12-
import { configureConnection, Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
12+
import {
13+
configureConnection,
14+
Eventlog,
15+
LeaderThreadCtx,
16+
makeLeaderThreadLayer,
17+
streamEventsWithSyncState,
18+
} from '@livestore/common/leader-thread'
1319
import type { LiveStoreSchema } from '@livestore/common/schema'
1420
import { LiveStoreEvent } from '@livestore/common/schema'
1521
import * as DevtoolsWeb from '@livestore/devtools-web-common/web-channel'
@@ -265,6 +271,12 @@ const makeLeaderThread = ({
265271
batch.map((item) => new LiveStoreEvent.Client.EncodedWithMeta(item)),
266272
{ waitForProcessing: true },
267273
),
274+
stream: (options) =>
275+
streamEventsWithSyncState({
276+
dbEventlog,
277+
syncState: syncProcessor.syncState,
278+
options,
279+
}),
268280
},
269281
initialState: { leaderHead: initialLeaderHead, migrationsReport: initialState.migrationsReport },
270282
export: Effect.sync(() => dbState.export()),

0 commit comments

Comments
 (0)