@livestore/adapter-node 0.3.0-dev.32 → 0.3.0-dev.34
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/client-session/adapter.d.ts +44 -0
- package/dist/client-session/adapter.d.ts.map +1 -0
- package/dist/client-session/adapter.js +202 -0
- package/dist/client-session/adapter.js.map +1 -0
- package/dist/client-session/in-memory-adapter.d.ts +145 -10
- package/dist/client-session/in-memory-adapter.d.ts.map +1 -1
- package/dist/client-session/in-memory-adapter.js +6 -7
- package/dist/client-session/in-memory-adapter.js.map +1 -1
- package/dist/client-session/persisted-adapter.d.ts +26 -14
- package/dist/client-session/persisted-adapter.d.ts.map +1 -1
- package/dist/client-session/persisted-adapter.js +81 -24
- package/dist/client-session/persisted-adapter.js.map +1 -1
- package/dist/devtools/devtools-server.d.ts.map +1 -1
- package/dist/devtools/devtools-server.js +3 -1
- package/dist/devtools/devtools-server.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/leader-shared.d.ts +29 -0
- package/dist/leader-shared.d.ts.map +1 -0
- package/dist/leader-shared.js +87 -0
- package/dist/leader-shared.js.map +1 -0
- package/dist/leader-thread-lazy.js +4 -0
- package/dist/leader-thread-lazy.js.map +1 -1
- package/dist/leader-thread-shared.d.ts +29 -0
- package/dist/leader-thread-shared.d.ts.map +1 -0
- package/dist/leader-thread-shared.js +88 -0
- package/dist/leader-thread-shared.js.map +1 -0
- package/dist/make-leader-worker.d.ts +5 -1
- package/dist/make-leader-worker.d.ts.map +1 -1
- package/dist/make-leader-worker.js +16 -83
- package/dist/make-leader-worker.js.map +1 -1
- package/dist/webchannel.d.ts.map +1 -1
- package/dist/webchannel.js +13 -3
- package/dist/webchannel.js.map +1 -1
- package/dist/worker-schema.d.ts +35 -19
- package/dist/worker-schema.d.ts.map +1 -1
- package/dist/worker-schema.js +14 -19
- package/dist/worker-schema.js.map +1 -1
- package/package.json +12 -11
- package/src/client-session/{persisted-adapter.ts → adapter.ts} +193 -57
- package/src/devtools/devtools-server.ts +3 -0
- package/src/index.ts +1 -2
- package/src/leader-thread-lazy.ts +5 -0
- package/src/leader-thread-shared.ts +167 -0
- package/src/make-leader-worker.ts +22 -138
- package/src/webchannel.ts +16 -3
- package/src/worker-schema.ts +23 -26
- package/rollup.config.mjs +0 -24
- package/src/client-session/in-memory-adapter.ts +0 -170
- package/tmp/pack.tgz +0 -0
- package/tsconfig.json +0 -17
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import './thread-polyfill.js'
|
|
2
2
|
|
|
3
3
|
import inspector from 'node:inspector'
|
|
4
|
-
import path from 'node:path'
|
|
5
4
|
|
|
6
5
|
if (process.execArgv.includes('--inspect')) {
|
|
7
6
|
inspector.open()
|
|
@@ -9,15 +8,12 @@ if (process.execArgv.includes('--inspect')) {
|
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
import type { SyncOptions } from '@livestore/common'
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import { configureConnection, Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
|
|
11
|
+
import { UnexpectedError } from '@livestore/common'
|
|
12
|
+
import { Eventlog, LeaderThreadCtx } from '@livestore/common/leader-thread'
|
|
15
13
|
import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
16
14
|
import { LiveStoreEvent } from '@livestore/common/schema'
|
|
17
|
-
import { makeNodeDevtoolsChannel } from '@livestore/devtools-node-common/web-channel'
|
|
18
15
|
import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
|
|
19
16
|
import { sqliteDbFactory } from '@livestore/sqlite-wasm/node'
|
|
20
|
-
import type { FileSystem, HttpClient, Scope } from '@livestore/utils/effect'
|
|
21
17
|
import {
|
|
22
18
|
Effect,
|
|
23
19
|
FetchHttpClient,
|
|
@@ -33,17 +29,19 @@ import {
|
|
|
33
29
|
import { PlatformNode } from '@livestore/utils/node'
|
|
34
30
|
import type * as otel from '@opentelemetry/api'
|
|
35
31
|
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
32
|
+
import type { TestingOverrides } from './leader-thread-shared.js'
|
|
33
|
+
import { makeLeaderThread } from './leader-thread-shared.js'
|
|
38
34
|
import * as WorkerSchema from './worker-schema.js'
|
|
39
35
|
|
|
40
36
|
export type WorkerOptions = {
|
|
37
|
+
schema: LiveStoreSchema
|
|
41
38
|
sync?: SyncOptions
|
|
42
39
|
otelOptions?: {
|
|
43
40
|
tracer?: otel.Tracer
|
|
44
41
|
/** @default 'livestore-node-leader-thread' */
|
|
45
42
|
serviceName?: string
|
|
46
43
|
}
|
|
44
|
+
testing?: TestingOverrides
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
export const getWorkerArgs = () => Schema.decodeSync(WorkerSchema.WorkerArgv)(process.argv[2]!)
|
|
@@ -60,7 +58,20 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
|
|
|
60
58
|
: undefined
|
|
61
59
|
|
|
62
60
|
return WorkerRunner.layerSerialized(WorkerSchema.LeaderWorkerInner.Request, {
|
|
63
|
-
InitialMessage: (args) =>
|
|
61
|
+
InitialMessage: (args) =>
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
const sqlite3 = yield* Effect.promise(() => loadSqlite3Wasm()).pipe(
|
|
64
|
+
Effect.withSpan('@livestore/adapter-node:leader-thread:loadSqlite3Wasm'),
|
|
65
|
+
)
|
|
66
|
+
const makeSqliteDb = yield* sqliteDbFactory({ sqlite3 })
|
|
67
|
+
return yield* makeLeaderThread({
|
|
68
|
+
...args,
|
|
69
|
+
syncOptions: options.sync,
|
|
70
|
+
schema: options.schema,
|
|
71
|
+
testing: options.testing,
|
|
72
|
+
makeSqliteDb,
|
|
73
|
+
})
|
|
74
|
+
}).pipe(Layer.unwrapScoped),
|
|
64
75
|
PushToLeader: ({ batch }) =>
|
|
65
76
|
Effect.andThen(LeaderThreadCtx, (_) =>
|
|
66
77
|
_.syncProcessor.push(
|
|
@@ -77,7 +88,7 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
|
|
|
77
88
|
return syncProcessor.pull({ cursor })
|
|
78
89
|
}).pipe(Stream.unwrapScoped),
|
|
79
90
|
Export: () =>
|
|
80
|
-
Effect.andThen(LeaderThreadCtx, (_) => _.
|
|
91
|
+
Effect.andThen(LeaderThreadCtx, (_) => _.dbState.export()).pipe(
|
|
81
92
|
UnexpectedError.mapToUnexpectedError,
|
|
82
93
|
Effect.withSpan('@livestore/adapter-node:worker:Export'),
|
|
83
94
|
),
|
|
@@ -107,7 +118,7 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
|
|
|
107
118
|
// const cachedSnapshot =
|
|
108
119
|
// result._tag === 'Recreate' ? yield* Ref.getAndSet(result.snapshotRef, undefined) : undefined
|
|
109
120
|
// return cachedSnapshot ?? workerCtx.db.export()
|
|
110
|
-
const snapshot = workerCtx.
|
|
121
|
+
const snapshot = workerCtx.dbState.export()
|
|
111
122
|
return { snapshot, migrationsReport: workerCtx.initialState.migrationsReport }
|
|
112
123
|
}).pipe(
|
|
113
124
|
UnexpectedError.mapToUnexpectedError,
|
|
@@ -149,130 +160,3 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
|
|
|
149
160
|
Logger.withMinimumLogLevel(LogLevel.Debug),
|
|
150
161
|
)
|
|
151
162
|
}
|
|
152
|
-
|
|
153
|
-
const makeLeaderThread = ({
|
|
154
|
-
storeId,
|
|
155
|
-
clientId,
|
|
156
|
-
syncOptions,
|
|
157
|
-
baseDirectory,
|
|
158
|
-
devtools,
|
|
159
|
-
schemaPath,
|
|
160
|
-
syncPayload,
|
|
161
|
-
}: WorkerSchema.LeaderWorkerInner.InitialMessage & {
|
|
162
|
-
syncOptions: SyncOptions | undefined
|
|
163
|
-
schemaPath: string
|
|
164
|
-
}): Layer.Layer<LeaderThreadCtx, UnexpectedError, Scope.Scope | HttpClient.HttpClient | FileSystem.FileSystem> =>
|
|
165
|
-
Effect.gen(function* () {
|
|
166
|
-
const schema = yield* Effect.promise(() => import(schemaPath).then((m) => m.schema as LiveStoreSchema))
|
|
167
|
-
|
|
168
|
-
const sqlite3 = yield* Effect.promise(() => loadSqlite3Wasm()).pipe(
|
|
169
|
-
Effect.withSpan('@livestore/adapter-node:leader-thread:loadSqlite3Wasm'),
|
|
170
|
-
)
|
|
171
|
-
const makeSqliteDb = yield* sqliteDbFactory({ sqlite3 })
|
|
172
|
-
const runtime = yield* Effect.runtime<never>()
|
|
173
|
-
|
|
174
|
-
const schemaHashSuffix = schema.migrationOptions.strategy === 'manual' ? 'fixed' : schema.hash.toString()
|
|
175
|
-
|
|
176
|
-
const makeDb = (kind: 'app' | 'eventlog') =>
|
|
177
|
-
makeSqliteDb({
|
|
178
|
-
_tag: 'fs',
|
|
179
|
-
directory: path.join(baseDirectory ?? '', storeId),
|
|
180
|
-
fileName: kind === 'app' ? getAppDbFileName(schemaHashSuffix) : `eventlog@${liveStoreStorageFormatVersion}.db`,
|
|
181
|
-
// TODO enable WAL for nodejs
|
|
182
|
-
configureDb: (db) =>
|
|
183
|
-
configureConnection(db, { foreignKeys: true }).pipe(Effect.provide(runtime), Effect.runSync),
|
|
184
|
-
}).pipe(Effect.acquireRelease((db) => Effect.sync(() => db.close())))
|
|
185
|
-
|
|
186
|
-
// Might involve some async work, so we're running them concurrently
|
|
187
|
-
const [dbReadModel, dbEventlog] = yield* Effect.all([makeDb('app'), makeDb('eventlog')], { concurrency: 2 })
|
|
188
|
-
|
|
189
|
-
const devtoolsOptions = yield* makeDevtoolsOptions({
|
|
190
|
-
devtoolsEnabled: devtools.enabled,
|
|
191
|
-
devtoolsPort: devtools.port,
|
|
192
|
-
devtoolsHost: devtools.host,
|
|
193
|
-
dbReadModel,
|
|
194
|
-
dbEventlog,
|
|
195
|
-
storeId,
|
|
196
|
-
clientId,
|
|
197
|
-
schemaPath,
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
const shutdownChannel = yield* makeShutdownChannel(storeId)
|
|
201
|
-
|
|
202
|
-
return makeLeaderThreadLayer({
|
|
203
|
-
schema,
|
|
204
|
-
storeId,
|
|
205
|
-
clientId,
|
|
206
|
-
makeSqliteDb,
|
|
207
|
-
syncOptions,
|
|
208
|
-
dbReadModel,
|
|
209
|
-
dbEventlog,
|
|
210
|
-
devtoolsOptions,
|
|
211
|
-
shutdownChannel,
|
|
212
|
-
syncPayload,
|
|
213
|
-
})
|
|
214
|
-
}).pipe(
|
|
215
|
-
Effect.tapCauseLogPretty,
|
|
216
|
-
UnexpectedError.mapToUnexpectedError,
|
|
217
|
-
Effect.withSpan('@livestore/adapter-node:worker:InitialMessage'),
|
|
218
|
-
Layer.unwrapScoped,
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
const getAppDbFileName = (suffix: string) => `app${suffix}@${liveStoreStorageFormatVersion}.db`
|
|
222
|
-
|
|
223
|
-
const makeDevtoolsOptions = ({
|
|
224
|
-
devtoolsEnabled,
|
|
225
|
-
dbReadModel,
|
|
226
|
-
dbEventlog,
|
|
227
|
-
storeId,
|
|
228
|
-
clientId,
|
|
229
|
-
devtoolsPort,
|
|
230
|
-
devtoolsHost,
|
|
231
|
-
schemaPath,
|
|
232
|
-
}: {
|
|
233
|
-
devtoolsEnabled: boolean
|
|
234
|
-
dbReadModel: LeaderSqliteDb
|
|
235
|
-
dbEventlog: LeaderSqliteDb
|
|
236
|
-
storeId: string
|
|
237
|
-
clientId: string
|
|
238
|
-
devtoolsPort: number
|
|
239
|
-
devtoolsHost: string
|
|
240
|
-
schemaPath: string
|
|
241
|
-
}): Effect.Effect<DevtoolsOptions, UnexpectedError, Scope.Scope> =>
|
|
242
|
-
Effect.gen(function* () {
|
|
243
|
-
if (devtoolsEnabled === false) {
|
|
244
|
-
return {
|
|
245
|
-
enabled: false,
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return {
|
|
250
|
-
enabled: true,
|
|
251
|
-
makeBootContext: Effect.gen(function* () {
|
|
252
|
-
// TODO instead of failing when the port is already in use, we should try to use that WS server instead of starting a new one
|
|
253
|
-
yield* startDevtoolsServer({
|
|
254
|
-
schemaPath,
|
|
255
|
-
storeId,
|
|
256
|
-
clientId,
|
|
257
|
-
sessionId: 'static', // TODO make this dynamic
|
|
258
|
-
port: devtoolsPort,
|
|
259
|
-
host: devtoolsHost,
|
|
260
|
-
}).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
|
|
261
|
-
|
|
262
|
-
const devtoolsWebChannel = yield* makeNodeDevtoolsChannel({
|
|
263
|
-
nodeName: `leader-${storeId}-${clientId}`,
|
|
264
|
-
target: `devtools-${storeId}-${clientId}-static`,
|
|
265
|
-
url: `ws://localhost:${devtoolsPort}`,
|
|
266
|
-
schema: { listen: Devtools.Leader.MessageToApp, send: Devtools.Leader.MessageFromApp },
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
devtoolsWebChannel,
|
|
271
|
-
persistenceInfo: {
|
|
272
|
-
readModel: dbReadModel.metadata.persistenceInfo,
|
|
273
|
-
eventlog: dbEventlog.metadata.persistenceInfo,
|
|
274
|
-
},
|
|
275
|
-
}
|
|
276
|
-
}).pipe(Effect.provide(FetchHttpClient.layer)),
|
|
277
|
-
}
|
|
278
|
-
})
|
package/src/webchannel.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BroadcastChannel } from 'node:worker_threads'
|
|
1
|
+
import { type BroadcastChannel as NodeBroadcastChannel } from 'node:worker_threads'
|
|
2
2
|
|
|
3
3
|
import type { Either, ParseResult } from '@livestore/utils/effect'
|
|
4
4
|
import { Deferred, Effect, Exit, Schema, Scope, Stream, WebChannel } from '@livestore/utils/effect'
|
|
@@ -12,7 +12,14 @@ export const makeBroadcastChannel = <Msg, MsgEncoded>({
|
|
|
12
12
|
}): Effect.Effect<WebChannel.WebChannel<Msg, Msg>, never, Scope.Scope> =>
|
|
13
13
|
Effect.scopeWithCloseable((scope) =>
|
|
14
14
|
Effect.gen(function* () {
|
|
15
|
-
|
|
15
|
+
if (globalThis.BroadcastChannel === undefined) {
|
|
16
|
+
yield* Effect.logWarning('BroadcastChannel is not supported in this environment. Using a NoopChannel instead.')
|
|
17
|
+
return (yield* WebChannel.noopChannel()) as any as WebChannel.WebChannel<Msg, Msg>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// NOTE we're using `globalThis.BroadcastChannel` here because `BroadcastChannel`
|
|
21
|
+
// from `node:worker_threads` is not yet stable in Deno
|
|
22
|
+
const channel = new globalThis.BroadcastChannel(channelName) as any as NodeBroadcastChannel
|
|
16
23
|
|
|
17
24
|
yield* Effect.addFinalizer(() => Effect.try(() => channel.close()).pipe(Effect.ignoreLogged))
|
|
18
25
|
|
|
@@ -37,7 +44,13 @@ export const makeBroadcastChannel = <Msg, MsgEncoded>({
|
|
|
37
44
|
|
|
38
45
|
return channel
|
|
39
46
|
}),
|
|
40
|
-
(channel) =>
|
|
47
|
+
(channel) =>
|
|
48
|
+
Effect.sync(() => {
|
|
49
|
+
// NOTE not all BroadcastChannel implementations have the `unref` method
|
|
50
|
+
if (typeof channel.unref === 'function') {
|
|
51
|
+
channel.unref()
|
|
52
|
+
}
|
|
53
|
+
}),
|
|
41
54
|
),
|
|
42
55
|
)
|
|
43
56
|
|
package/src/worker-schema.ts
CHANGED
|
@@ -18,31 +18,25 @@ export const WorkerArgv = Schema.parseJson(
|
|
|
18
18
|
}),
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
-
export const
|
|
22
|
-
type: Schema.Literal('
|
|
21
|
+
export const StorageTypeInMemory = Schema.Struct({
|
|
22
|
+
type: Schema.Literal('in-memory'),
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export type StorageTypeInMemory = typeof StorageTypeInMemory.Type
|
|
26
|
+
|
|
27
|
+
export const StorageTypeFs = Schema.Struct({
|
|
28
|
+
type: Schema.Literal('fs'),
|
|
23
29
|
/**
|
|
24
|
-
*
|
|
30
|
+
* Where to store the database files
|
|
25
31
|
*
|
|
26
|
-
*
|
|
27
|
-
* conflicts with other LiveStore apps.
|
|
32
|
+
* @default Current working directory
|
|
28
33
|
*/
|
|
29
|
-
|
|
34
|
+
baseDirectory: Schema.String,
|
|
30
35
|
})
|
|
31
36
|
|
|
32
|
-
export type
|
|
37
|
+
export type StorageTypeFs = typeof StorageTypeFs.Type
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
// type: Schema.Literal('indexeddb'),
|
|
36
|
-
// /** @default "livestore" */
|
|
37
|
-
// databaseName: Schema.optionalWith(Schema.String, { default: () => 'livestore' }),
|
|
38
|
-
// /** @default "livestore-" */
|
|
39
|
-
// storeNamePrefix: Schema.optionalWith(Schema.String, { default: () => 'livestore-' }),
|
|
40
|
-
// })
|
|
41
|
-
|
|
42
|
-
export const StorageType = Schema.Union(
|
|
43
|
-
StorageTypeOpfs,
|
|
44
|
-
// StorageTypeIndexeddb
|
|
45
|
-
)
|
|
39
|
+
export const StorageType = Schema.Union(StorageTypeInMemory, StorageTypeFs)
|
|
46
40
|
export type StorageType = typeof StorageType.Type
|
|
47
41
|
export type StorageTypeEncoded = typeof StorageType.Encoded
|
|
48
42
|
|
|
@@ -71,14 +65,17 @@ export namespace LeaderWorkerInner {
|
|
|
71
65
|
payload: {
|
|
72
66
|
storeId: Schema.String,
|
|
73
67
|
clientId: Schema.String,
|
|
74
|
-
|
|
75
|
-
schemaPath: Schema.String,
|
|
68
|
+
storage: StorageType,
|
|
76
69
|
syncPayload: Schema.UndefinedOr(Schema.JsonValue),
|
|
77
|
-
devtools: Schema.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
devtools: Schema.Union(
|
|
71
|
+
Schema.Struct({
|
|
72
|
+
enabled: Schema.Literal(true),
|
|
73
|
+
schemaPath: Schema.String,
|
|
74
|
+
port: Schema.Number,
|
|
75
|
+
host: Schema.String,
|
|
76
|
+
}),
|
|
77
|
+
Schema.Struct({ enabled: Schema.Literal(false) }),
|
|
78
|
+
),
|
|
82
79
|
},
|
|
83
80
|
success: Schema.Void,
|
|
84
81
|
failure: UnexpectedError,
|
package/rollup.config.mjs
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import commonjs from '@rollup/plugin-commonjs'
|
|
2
|
-
import { nodeResolve } from '@rollup/plugin-node-resolve'
|
|
3
|
-
import terser from '@rollup/plugin-terser'
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
input: 'dist/leader-thread.js',
|
|
7
|
-
output: {
|
|
8
|
-
file: 'dist/leader-thread.bundle.js',
|
|
9
|
-
// dir: 'dist/leader-thread-bundle',
|
|
10
|
-
format: 'esm',
|
|
11
|
-
// inlineDynamicImports: true,
|
|
12
|
-
},
|
|
13
|
-
external: ['@livestore/sqlite-wasm', '@opentelemetry/otlp-exporter-base'],
|
|
14
|
-
plugins: [
|
|
15
|
-
nodeResolve({
|
|
16
|
-
// esnext is needed for @opentelemetry/* packages
|
|
17
|
-
mainFields: ['esnext', 'module', 'main'],
|
|
18
|
-
}),
|
|
19
|
-
commonjs(),
|
|
20
|
-
terser(),
|
|
21
|
-
],
|
|
22
|
-
// Needed for @opentelemetry/* packages
|
|
23
|
-
// inlineDynamicImports: true,
|
|
24
|
-
}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Adapter,
|
|
3
|
-
ClientSession,
|
|
4
|
-
ClientSessionLeaderThreadProxy,
|
|
5
|
-
LockStatus,
|
|
6
|
-
MakeSqliteDb,
|
|
7
|
-
SqliteDb,
|
|
8
|
-
SyncOptions,
|
|
9
|
-
} from '@livestore/common'
|
|
10
|
-
import { UnexpectedError } from '@livestore/common'
|
|
11
|
-
import { Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
|
|
12
|
-
import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
13
|
-
import { LiveStoreEvent } from '@livestore/common/schema'
|
|
14
|
-
import { sqliteDbFactory } from '@livestore/sqlite-wasm/browser'
|
|
15
|
-
import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
|
|
16
|
-
import type { Schema } from '@livestore/utils/effect'
|
|
17
|
-
import { Cause, Effect, FetchHttpClient, Layer, Stream, SubscriptionRef } from '@livestore/utils/effect'
|
|
18
|
-
import { nanoid } from '@livestore/utils/nanoid'
|
|
19
|
-
|
|
20
|
-
import { makeShutdownChannel } from '../shutdown-channel.js'
|
|
21
|
-
|
|
22
|
-
// TODO unify in-memory adapter with other in-memory adapter implementations
|
|
23
|
-
|
|
24
|
-
export interface InMemoryAdapterOptions {
|
|
25
|
-
sync?: SyncOptions
|
|
26
|
-
/**
|
|
27
|
-
* @default 'in-memory'
|
|
28
|
-
*/
|
|
29
|
-
clientId?: string
|
|
30
|
-
/**
|
|
31
|
-
* @default nanoid(6)
|
|
32
|
-
*/
|
|
33
|
-
sessionId?: string
|
|
34
|
-
|
|
35
|
-
/** Only used internally for testing */
|
|
36
|
-
testing?: {
|
|
37
|
-
overrides?: TestingOverrides
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type TestingOverrides = {
|
|
42
|
-
clientSession?: {
|
|
43
|
-
leaderThreadProxy?: Partial<ClientSessionLeaderThreadProxy>
|
|
44
|
-
}
|
|
45
|
-
makeLeaderThread?: {
|
|
46
|
-
dbEventlog?: (makeSqliteDb: MakeSqliteDb) => Effect.Effect<SqliteDb, UnexpectedError>
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** NOTE: This adapter is currently only used for testing */
|
|
51
|
-
export const makeInMemoryAdapter =
|
|
52
|
-
({ sync: syncOptions, clientId = 'in-memory', sessionId = nanoid(6), testing }: InMemoryAdapterOptions): Adapter =>
|
|
53
|
-
({
|
|
54
|
-
schema,
|
|
55
|
-
storeId,
|
|
56
|
-
shutdown,
|
|
57
|
-
syncPayload,
|
|
58
|
-
// devtoolsEnabled, bootStatusQueue, shutdown, connectDevtoolsToStore
|
|
59
|
-
}) =>
|
|
60
|
-
Effect.gen(function* () {
|
|
61
|
-
const sqlite3 = yield* Effect.promise(() => loadSqlite3Wasm())
|
|
62
|
-
|
|
63
|
-
const makeSqliteDb = sqliteDbFactory({ sqlite3 })
|
|
64
|
-
const sqliteDb = yield* makeSqliteDb({ _tag: 'in-memory' })
|
|
65
|
-
|
|
66
|
-
const lockStatus = yield* SubscriptionRef.make<LockStatus>('has-lock')
|
|
67
|
-
|
|
68
|
-
const shutdownChannel = yield* makeShutdownChannel(storeId)
|
|
69
|
-
|
|
70
|
-
yield* shutdownChannel.listen.pipe(
|
|
71
|
-
Stream.flatten(),
|
|
72
|
-
Stream.tap((error) => Effect.sync(() => shutdown(Cause.fail(error)))),
|
|
73
|
-
Stream.runDrain,
|
|
74
|
-
Effect.interruptible,
|
|
75
|
-
Effect.tapCauseLogPretty,
|
|
76
|
-
Effect.forkScoped,
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
const { leaderThread, initialSnapshot } = yield* makeLeaderThread({
|
|
80
|
-
storeId,
|
|
81
|
-
clientId,
|
|
82
|
-
schema,
|
|
83
|
-
makeSqliteDb,
|
|
84
|
-
syncOptions,
|
|
85
|
-
syncPayload,
|
|
86
|
-
testing,
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
sqliteDb.import(initialSnapshot)
|
|
90
|
-
|
|
91
|
-
const clientSession = {
|
|
92
|
-
sqliteDb,
|
|
93
|
-
devtools: { enabled: false },
|
|
94
|
-
clientId,
|
|
95
|
-
sessionId,
|
|
96
|
-
lockStatus,
|
|
97
|
-
leaderThread,
|
|
98
|
-
shutdown,
|
|
99
|
-
} satisfies ClientSession
|
|
100
|
-
|
|
101
|
-
return clientSession
|
|
102
|
-
}).pipe(UnexpectedError.mapToUnexpectedError)
|
|
103
|
-
|
|
104
|
-
const makeLeaderThread = ({
|
|
105
|
-
storeId,
|
|
106
|
-
clientId,
|
|
107
|
-
schema,
|
|
108
|
-
makeSqliteDb,
|
|
109
|
-
syncOptions,
|
|
110
|
-
syncPayload,
|
|
111
|
-
testing,
|
|
112
|
-
}: {
|
|
113
|
-
storeId: string
|
|
114
|
-
clientId: string
|
|
115
|
-
schema: LiveStoreSchema
|
|
116
|
-
makeSqliteDb: MakeSqliteDb
|
|
117
|
-
syncOptions: SyncOptions | undefined
|
|
118
|
-
syncPayload: Schema.JsonValue | undefined
|
|
119
|
-
testing?: {
|
|
120
|
-
overrides?: TestingOverrides
|
|
121
|
-
}
|
|
122
|
-
}) =>
|
|
123
|
-
Effect.gen(function* () {
|
|
124
|
-
const layer = yield* Layer.memoize(
|
|
125
|
-
makeLeaderThreadLayer({
|
|
126
|
-
clientId,
|
|
127
|
-
dbReadModel: yield* makeSqliteDb({ _tag: 'in-memory' }),
|
|
128
|
-
dbEventlog: testing?.overrides?.makeLeaderThread?.dbEventlog
|
|
129
|
-
? yield* testing.overrides.makeLeaderThread.dbEventlog(makeSqliteDb)
|
|
130
|
-
: yield* makeSqliteDb({ _tag: 'in-memory' }),
|
|
131
|
-
devtoolsOptions: { enabled: false },
|
|
132
|
-
makeSqliteDb,
|
|
133
|
-
schema,
|
|
134
|
-
// NOTE we're creating a separate channel here since you can't listen to your own channel messages
|
|
135
|
-
shutdownChannel: yield* makeShutdownChannel(storeId),
|
|
136
|
-
storeId,
|
|
137
|
-
syncOptions,
|
|
138
|
-
syncPayload,
|
|
139
|
-
}).pipe(Layer.provideMerge(FetchHttpClient.layer)),
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
return yield* Effect.gen(function* () {
|
|
143
|
-
const { dbReadModel, dbEventlog, syncProcessor, extraIncomingMessagesQueue, initialState } =
|
|
144
|
-
yield* LeaderThreadCtx
|
|
145
|
-
|
|
146
|
-
const initialLeaderHead = Eventlog.getClientHeadFromDb(dbEventlog)
|
|
147
|
-
|
|
148
|
-
const leaderThread = {
|
|
149
|
-
events: {
|
|
150
|
-
pull:
|
|
151
|
-
testing?.overrides?.clientSession?.leaderThreadProxy?.events?.pull ??
|
|
152
|
-
(({ cursor }) => syncProcessor.pull({ cursor })),
|
|
153
|
-
push: (batch) =>
|
|
154
|
-
syncProcessor.push(
|
|
155
|
-
batch.map((item) => new LiveStoreEvent.EncodedWithMeta(item)),
|
|
156
|
-
{ waitForProcessing: true },
|
|
157
|
-
),
|
|
158
|
-
},
|
|
159
|
-
initialState: { leaderHead: initialLeaderHead, migrationsReport: initialState.migrationsReport },
|
|
160
|
-
export: Effect.sync(() => dbReadModel.export()),
|
|
161
|
-
getEventlogData: Effect.sync(() => dbEventlog.export()),
|
|
162
|
-
getSyncState: syncProcessor.syncState,
|
|
163
|
-
sendDevtoolsMessage: (message) => extraIncomingMessagesQueue.offer(message),
|
|
164
|
-
} satisfies ClientSessionLeaderThreadProxy
|
|
165
|
-
|
|
166
|
-
const initialSnapshot = dbReadModel.export()
|
|
167
|
-
|
|
168
|
-
return { leaderThread, initialSnapshot }
|
|
169
|
-
}).pipe(Effect.provide(layer))
|
|
170
|
-
})
|
package/tmp/pack.tgz
DELETED
|
Binary file
|
package/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
"rootDir": "./src",
|
|
6
|
-
"resolveJsonModule": true,
|
|
7
|
-
"tsBuildInfoFile": "./dist/.tsbuildinfo"
|
|
8
|
-
},
|
|
9
|
-
"include": ["./src"],
|
|
10
|
-
"references": [
|
|
11
|
-
{ "path": "../common" },
|
|
12
|
-
{ "path": "../utils" },
|
|
13
|
-
{ "path": "../devtools-node-common" },
|
|
14
|
-
{ "path": "../webmesh" },
|
|
15
|
-
{ "path": "../sqlite-wasm" }
|
|
16
|
-
]
|
|
17
|
-
}
|