@livestore/adapter-node 0.4.0-dev.2 → 0.4.0-dev.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,24 +8,14 @@ if (process.execArgv.includes('--inspect')) {
8
8
  }
9
9
 
10
10
  import type { SyncOptions } from '@livestore/common'
11
- import { UnexpectedError } from '@livestore/common'
12
- import { Eventlog, LeaderThreadCtx } from '@livestore/common/leader-thread'
11
+ import { LogConfig, UnknownError } from '@livestore/common'
12
+ import type { StreamEventsOptions } from '@livestore/common/leader-thread'
13
+ import { Eventlog, LeaderThreadCtx, streamEventsWithSyncState } from '@livestore/common/leader-thread'
13
14
  import type { LiveStoreSchema } from '@livestore/common/schema'
14
15
  import { LiveStoreEvent } from '@livestore/common/schema'
15
16
  import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
16
17
  import { sqliteDbFactory } from '@livestore/sqlite-wasm/node'
17
- import {
18
- Effect,
19
- FetchHttpClient,
20
- identity,
21
- Layer,
22
- Logger,
23
- LogLevel,
24
- OtelTracer,
25
- Schema,
26
- Stream,
27
- WorkerRunner,
28
- } from '@livestore/utils/effect'
18
+ import { Effect, FetchHttpClient, Layer, OtelTracer, Schema, Stream, WorkerRunner } from '@livestore/utils/effect'
29
19
  import { PlatformNode } from '@livestore/utils/node'
30
20
  import type * as otel from '@opentelemetry/api'
31
21
 
@@ -36,21 +26,19 @@ import * as WorkerSchema from './worker-schema.ts'
36
26
  export type WorkerOptions = {
37
27
  schema: LiveStoreSchema
38
28
  sync?: SyncOptions
29
+ syncPayloadSchema?: Schema.Schema<any>
39
30
  otelOptions?: {
40
31
  tracer?: otel.Tracer
41
32
  /** @default 'livestore-node-leader-thread' */
42
33
  serviceName?: string
43
34
  }
44
35
  testing?: TestingOverrides
45
- }
36
+ } & LogConfig.WithLoggerOptions
46
37
 
47
38
  export const getWorkerArgs = () => Schema.decodeSync(WorkerSchema.WorkerArgv)(process.argv[2]!)
48
39
 
49
40
  export const makeWorker = (options: WorkerOptions) => {
50
- makeWorkerEffect(options).pipe(
51
- Effect.provide(Logger.prettyWithThread(options.otelOptions?.serviceName ?? 'livestore-node-leader-thread')),
52
- PlatformNode.NodeRuntime.runMain,
53
- )
41
+ makeWorkerEffect(options).pipe(PlatformNode.NodeRuntime.runMain)
54
42
  }
55
43
 
56
44
  export const makeWorkerEffect = (options: WorkerOptions) => {
@@ -60,6 +48,13 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
60
48
  )
61
49
  : undefined
62
50
 
51
+ // Merge the runtime dependencies once so we can provide them together without chaining Effect.provide.
52
+ const runtimeLayer = Layer.mergeAll(
53
+ FetchHttpClient.layer,
54
+ PlatformNode.NodeFileSystem.layer,
55
+ TracingLive ?? Layer.empty,
56
+ )
57
+
63
58
  return WorkerRunner.layerSerialized(WorkerSchema.LeaderWorkerInnerRequest, {
64
59
  InitialMessage: (args) =>
65
60
  Effect.gen(function* () {
@@ -73,12 +68,14 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
73
68
  schema: options.schema,
74
69
  testing: options.testing,
75
70
  makeSqliteDb,
71
+ syncPayloadEncoded: args.syncPayloadEncoded,
72
+ syncPayloadSchema: options.syncPayloadSchema,
76
73
  })
77
74
  }).pipe(Layer.unwrapScoped),
78
75
  PushToLeader: ({ batch }) =>
79
76
  Effect.andThen(LeaderThreadCtx, (_) =>
80
77
  _.syncProcessor.push(
81
- batch.map((item) => new LiveStoreEvent.EncodedWithMeta(item)),
78
+ batch.map((item) => new LiveStoreEvent.Client.EncodedWithMeta(item)),
82
79
  // We'll wait in order to keep back pressure on the client session
83
80
  { waitForProcessing: true },
84
81
  ),
@@ -90,29 +87,55 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
90
87
  const { syncProcessor } = yield* LeaderThreadCtx
91
88
  return syncProcessor.pull({ cursor })
92
89
  }).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
+ ),
93
104
  Export: () =>
94
105
  Effect.andThen(LeaderThreadCtx, (_) => _.dbState.export()).pipe(
95
- UnexpectedError.mapToUnexpectedError,
106
+ UnknownError.mapToUnknownError,
96
107
  Effect.withSpan('@livestore/adapter-node:worker:Export'),
97
108
  ),
98
109
  ExportEventlog: () =>
99
110
  Effect.andThen(LeaderThreadCtx, (_) => _.dbEventlog.export()).pipe(
100
- UnexpectedError.mapToUnexpectedError,
111
+ UnknownError.mapToUnknownError,
101
112
  Effect.withSpan('@livestore/adapter-node:worker:ExportEventlog'),
102
113
  ),
103
114
  GetLeaderHead: () =>
104
115
  Effect.gen(function* () {
105
116
  const workerCtx = yield* LeaderThreadCtx
106
117
  return Eventlog.getClientHeadFromDb(workerCtx.dbEventlog)
107
- }).pipe(UnexpectedError.mapToUnexpectedError, Effect.withSpan('@livestore/adapter-node:worker:GetLeaderHead')),
118
+ }).pipe(UnknownError.mapToUnknownError, Effect.withSpan('@livestore/adapter-node:worker:GetLeaderHead')),
108
119
  GetLeaderSyncState: () =>
109
120
  Effect.gen(function* () {
110
121
  const workerCtx = yield* LeaderThreadCtx
111
122
  return yield* workerCtx.syncProcessor.syncState
112
- }).pipe(
113
- UnexpectedError.mapToUnexpectedError,
114
- Effect.withSpan('@livestore/adapter-node:worker:GetLeaderSyncState'),
115
- ),
123
+ }).pipe(UnknownError.mapToUnknownError, Effect.withSpan('@livestore/adapter-node:worker:GetLeaderSyncState')),
124
+ SyncStateStream: () =>
125
+ Effect.gen(function* () {
126
+ const workerCtx = yield* LeaderThreadCtx
127
+ return workerCtx.syncProcessor.syncState.changes
128
+ }).pipe(Stream.unwrapScoped),
129
+ GetNetworkStatus: () =>
130
+ Effect.gen(function* () {
131
+ const workerCtx = yield* LeaderThreadCtx
132
+ return yield* workerCtx.networkStatus
133
+ }).pipe(UnknownError.mapToUnknownError, Effect.withSpan('@livestore/adapter-node:worker:GetNetworkStatus')),
134
+ NetworkStatusStream: () =>
135
+ Effect.gen(function* () {
136
+ const workerCtx = yield* LeaderThreadCtx
137
+ return workerCtx.networkStatus.changes
138
+ }).pipe(Stream.unwrapScoped),
116
139
  GetRecreateSnapshot: () =>
117
140
  Effect.gen(function* () {
118
141
  const workerCtx = yield* LeaderThreadCtx
@@ -123,11 +146,9 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
123
146
  // return cachedSnapshot ?? workerCtx.db.export()
124
147
  const snapshot = workerCtx.dbState.export()
125
148
  return { snapshot, migrationsReport: workerCtx.initialState.migrationsReport }
126
- }).pipe(
127
- UnexpectedError.mapToUnexpectedError,
128
- Effect.withSpan('@livestore/adapter-node:worker:GetRecreateSnapshot'),
129
- ),
149
+ }).pipe(UnknownError.mapToUnknownError, Effect.withSpan('@livestore/adapter-node:worker:GetRecreateSnapshot')),
130
150
  Shutdown: () =>
151
+ // @effect-diagnostics-next-line unnecessaryEffectGen:off
131
152
  Effect.gen(function* () {
132
153
  // const { db, dbEventlog } = yield* LeaderThreadCtx
133
154
  yield* Effect.logDebug('[@livestore/adapter-node:worker] Shutdown')
@@ -141,10 +162,10 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
141
162
  // Buy some time for Otel to flush
142
163
  // TODO find a cleaner way to do this
143
164
  // yield* Effect.sleep(1000)
144
- }).pipe(UnexpectedError.mapToUnexpectedError, Effect.withSpan('@livestore/adapter-node:worker:Shutdown')),
165
+ }).pipe(UnknownError.mapToUnknownError, Effect.withSpan('@livestore/adapter-node:worker:Shutdown')),
145
166
  ExtraDevtoolsMessage: ({ message }) =>
146
167
  Effect.andThen(LeaderThreadCtx, (_) => _.extraIncomingMessagesQueue.offer(message)).pipe(
147
- UnexpectedError.mapToUnexpectedError,
168
+ UnknownError.mapToUnknownError,
148
169
  Effect.withSpan('@livestore/adapter-node:worker:ExtraDevtoolsMessage'),
149
170
  ),
150
171
  }).pipe(
@@ -156,12 +177,13 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
156
177
  thread: options.otelOptions?.serviceName ?? 'livestore-node-leader-thread',
157
178
  processId: process.pid,
158
179
  }),
180
+ LogConfig.withLoggerConfig(
181
+ { logger: options.logger, logLevel: options.logLevel },
182
+ { threadName: options.otelOptions?.serviceName ?? 'livestore-node-leader-thread' },
183
+ ),
159
184
  // TODO bring back with Effect 4 once it's easier to work with replacing loggers.
160
185
  // We basically only want to provide this logger if it's replacing the default logger, not if there's a custom logger already provided.
161
186
  // Effect.provide(Logger.prettyWithThread(options.otelOptions?.serviceName ?? 'livestore-node-leader-thread')),
162
- Effect.provide(FetchHttpClient.layer),
163
- Effect.provide(PlatformNode.NodeFileSystem.layer),
164
- TracingLive ? Effect.provide(TracingLive) : identity,
165
- Logger.withMinimumLogLevel(LogLevel.Debug),
187
+ Effect.provide(runtimeLayer),
166
188
  )
167
189
  }
@@ -1,4 +1,13 @@
1
- import { BootStatus, Devtools, LeaderAheadError, MigrationsReport, SyncState, UnexpectedError } from '@livestore/common'
1
+ import {
2
+ BootStatus,
3
+ Devtools,
4
+ LeaderAheadError,
5
+ MigrationsReport,
6
+ SyncBackend,
7
+ SyncState,
8
+ UnknownError,
9
+ } from '@livestore/common'
10
+ import { StreamEventsOptionsFields } from '@livestore/common/leader-thread'
2
11
  import { EventSequenceNumber, LiveStoreEvent } from '@livestore/common/schema'
3
12
  import { Schema, Transferable } from '@livestore/utils/effect'
4
13
 
@@ -7,6 +16,7 @@ export const WorkerArgv = Schema.parseJson(
7
16
  clientId: Schema.String,
8
17
  storeId: Schema.String,
9
18
  sessionId: Schema.String,
19
+ extraArgs: Schema.UndefinedOr(Schema.JsonValue),
10
20
  }),
11
21
  )
12
22
 
@@ -52,7 +62,7 @@ export class LeaderWorkerOuterInitialMessage extends Schema.TaggedRequest<Leader
52
62
  {
53
63
  payload: { port: Transferable.MessagePort },
54
64
  success: Schema.Void,
55
- failure: UnexpectedError,
65
+ failure: UnknownError,
56
66
  },
57
67
  ) {}
58
68
 
@@ -65,7 +75,7 @@ export class LeaderWorkerInnerInitialMessage extends Schema.TaggedRequest<Leader
65
75
  storeId: Schema.String,
66
76
  clientId: Schema.String,
67
77
  storage: StorageType,
68
- syncPayload: Schema.UndefinedOr(Schema.JsonValue),
78
+ syncPayloadEncoded: Schema.UndefinedOr(Schema.JsonValue),
69
79
  devtools: Schema.Union(
70
80
  Schema.Struct({
71
81
  enabled: Schema.Literal(true),
@@ -79,7 +89,7 @@ export class LeaderWorkerInnerInitialMessage extends Schema.TaggedRequest<Leader
79
89
  ),
80
90
  },
81
91
  success: Schema.Void,
82
- failure: UnexpectedError,
92
+ failure: UnknownError,
83
93
  },
84
94
  ) {}
85
95
 
@@ -88,35 +98,44 @@ export class LeaderWorkerInnerBootStatusStream extends Schema.TaggedRequest<Lead
88
98
  {
89
99
  payload: {},
90
100
  success: BootStatus,
91
- failure: UnexpectedError,
101
+ failure: UnknownError,
92
102
  },
93
103
  ) {}
94
104
 
95
105
  export class LeaderWorkerInnerPullStream extends Schema.TaggedRequest<LeaderWorkerInnerPullStream>()('PullStream', {
96
106
  payload: {
97
- cursor: EventSequenceNumber.EventSequenceNumber,
107
+ cursor: Schema.typeSchema(EventSequenceNumber.Client.Composite),
98
108
  },
99
109
  success: Schema.Struct({
100
110
  payload: SyncState.PayloadUpstream,
101
111
  }),
102
- failure: UnexpectedError,
112
+ failure: UnknownError,
103
113
  }) {}
104
114
 
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
+
105
124
  export class LeaderWorkerInnerPushToLeader extends Schema.TaggedRequest<LeaderWorkerInnerPushToLeader>()(
106
125
  'PushToLeader',
107
126
  {
108
127
  payload: {
109
- batch: Schema.Array(LiveStoreEvent.AnyEncoded),
128
+ batch: Schema.Array(Schema.typeSchema(LiveStoreEvent.Client.Encoded)),
110
129
  },
111
- success: Schema.Void,
112
- failure: Schema.Union(UnexpectedError, LeaderAheadError),
130
+ success: Schema.Void as Schema.Schema<void>,
131
+ failure: Schema.Union(UnknownError, LeaderAheadError),
113
132
  },
114
133
  ) {}
115
134
 
116
135
  export class LeaderWorkerInnerExport extends Schema.TaggedRequest<LeaderWorkerInnerExport>()('Export', {
117
136
  payload: {},
118
137
  success: Transferable.Uint8Array as Schema.Schema<Uint8Array<ArrayBuffer>>,
119
- failure: UnexpectedError,
138
+ failure: UnknownError,
120
139
  }) {}
121
140
 
122
141
  export class LeaderWorkerInnerGetRecreateSnapshot extends Schema.TaggedRequest<LeaderWorkerInnerGetRecreateSnapshot>()(
@@ -127,7 +146,7 @@ export class LeaderWorkerInnerGetRecreateSnapshot extends Schema.TaggedRequest<L
127
146
  snapshot: Transferable.Uint8Array as Schema.Schema<Uint8Array<ArrayBuffer>>,
128
147
  migrationsReport: MigrationsReport,
129
148
  }),
130
- failure: UnexpectedError,
149
+ failure: UnknownError,
131
150
  },
132
151
  ) {}
133
152
 
@@ -136,7 +155,7 @@ export class LeaderWorkerInnerExportEventlog extends Schema.TaggedRequest<Leader
136
155
  {
137
156
  payload: {},
138
157
  success: Transferable.Uint8Array as Schema.Schema<Uint8Array<ArrayBuffer>>,
139
- failure: UnexpectedError,
158
+ failure: UnknownError,
140
159
  },
141
160
  ) {}
142
161
 
@@ -144,8 +163,8 @@ export class LeaderWorkerInnerGetLeaderHead extends Schema.TaggedRequest<LeaderW
144
163
  'GetLeaderHead',
145
164
  {
146
165
  payload: {},
147
- success: EventSequenceNumber.EventSequenceNumber,
148
- failure: UnexpectedError,
166
+ success: Schema.typeSchema(EventSequenceNumber.Client.Composite),
167
+ failure: UnknownError,
149
168
  },
150
169
  ) {}
151
170
 
@@ -154,14 +173,41 @@ export class LeaderWorkerInnerGetLeaderSyncState extends Schema.TaggedRequest<Le
154
173
  {
155
174
  payload: {},
156
175
  success: SyncState.SyncState,
157
- failure: UnexpectedError,
176
+ failure: UnknownError,
177
+ },
178
+ ) {}
179
+
180
+ export class LeaderWorkerInnerSyncStateStream extends Schema.TaggedRequest<LeaderWorkerInnerSyncStateStream>()(
181
+ 'SyncStateStream',
182
+ {
183
+ payload: {},
184
+ success: SyncState.SyncState,
185
+ failure: UnknownError,
186
+ },
187
+ ) {}
188
+
189
+ export class LeaderWorkerInnerGetNetworkStatus extends Schema.TaggedRequest<LeaderWorkerInnerGetNetworkStatus>()(
190
+ 'GetNetworkStatus',
191
+ {
192
+ payload: {},
193
+ success: SyncBackend.NetworkStatus,
194
+ failure: UnknownError,
195
+ },
196
+ ) {}
197
+
198
+ export class LeaderWorkerInnerNetworkStatusStream extends Schema.TaggedRequest<LeaderWorkerInnerNetworkStatusStream>()(
199
+ 'NetworkStatusStream',
200
+ {
201
+ payload: {},
202
+ success: SyncBackend.NetworkStatus,
203
+ failure: UnknownError,
158
204
  },
159
205
  ) {}
160
206
 
161
207
  export class LeaderWorkerInnerShutdown extends Schema.TaggedRequest<LeaderWorkerInnerShutdown>()('Shutdown', {
162
208
  payload: {},
163
209
  success: Schema.Void,
164
- failure: UnexpectedError,
210
+ failure: UnknownError,
165
211
  }) {}
166
212
 
167
213
  export class LeaderWorkerInnerExtraDevtoolsMessage extends Schema.TaggedRequest<LeaderWorkerInnerExtraDevtoolsMessage>()(
@@ -171,7 +217,7 @@ export class LeaderWorkerInnerExtraDevtoolsMessage extends Schema.TaggedRequest<
171
217
  message: Devtools.Leader.MessageToApp,
172
218
  },
173
219
  success: Schema.Void,
174
- failure: UnexpectedError,
220
+ failure: UnknownError,
175
221
  },
176
222
  ) {}
177
223
 
@@ -179,12 +225,16 @@ export const LeaderWorkerInnerRequest = Schema.Union(
179
225
  LeaderWorkerInnerInitialMessage,
180
226
  LeaderWorkerInnerBootStatusStream,
181
227
  LeaderWorkerInnerPullStream,
228
+ LeaderWorkerInnerStreamEvents,
182
229
  LeaderWorkerInnerPushToLeader,
183
230
  LeaderWorkerInnerExport,
184
231
  LeaderWorkerInnerGetRecreateSnapshot,
185
232
  LeaderWorkerInnerExportEventlog,
186
233
  LeaderWorkerInnerGetLeaderHead,
187
234
  LeaderWorkerInnerGetLeaderSyncState,
235
+ LeaderWorkerInnerSyncStateStream,
236
+ LeaderWorkerInnerGetNetworkStatus,
237
+ LeaderWorkerInnerNetworkStatusStream,
188
238
  LeaderWorkerInnerShutdown,
189
239
  LeaderWorkerInnerExtraDevtoolsMessage,
190
240
  )