@livestore/adapter-web 0.3.0-dev.36 → 0.3.0-dev.38

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.
@@ -1,15 +1,16 @@
1
1
  import type { Adapter, ClientSession, LockStatus } from '@livestore/common'
2
- import { Devtools, IntentionalShutdownCause, StoreInterrupted, UnexpectedError } from '@livestore/common'
2
+ import {
3
+ Devtools,
4
+ IntentionalShutdownCause,
5
+ makeClientSession,
6
+ StoreInterrupted,
7
+ UnexpectedError,
8
+ } from '@livestore/common'
3
9
  // TODO bring back - this currently doesn't work due to https://github.com/vitejs/vite/issues/8427
4
10
  // NOTE We're using a non-relative import here for Vite to properly resolve the import during app builds
5
11
  // import LiveStoreSharedWorker from '@livestore/adapter-web/internal-shared-worker?sharedworker'
6
12
  import { EventId, SystemTables } from '@livestore/common/schema'
7
- import {
8
- ClientSessionRequestContentscriptMain,
9
- connectViaWorker,
10
- makeChannelForConnectedMeshNode,
11
- makeSessionsChannel,
12
- } from '@livestore/devtools-web-common/web-channel'
13
+ import * as DevtoolsWeb from '@livestore/devtools-web-common/web-channel'
13
14
  import { sqliteDbFactory } from '@livestore/sqlite-wasm/browser'
14
15
  import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
15
16
  import { isDevEnv, shouldNeverHappen, tryAsFunctionAndNew } from '@livestore/utils'
@@ -104,17 +105,10 @@ export type WebAdapterOptions = {
104
105
 
105
106
  export const makePersistedAdapter =
106
107
  (options: WebAdapterOptions): Adapter =>
107
- ({
108
- schema,
109
- storeId,
110
- devtoolsEnabled,
111
- debugInstanceId,
112
- bootStatusQueue,
113
- shutdown,
114
- connectDevtoolsToStore,
115
- syncPayload,
116
- }) =>
108
+ (adapterArgs) =>
117
109
  Effect.gen(function* () {
110
+ const { schema, storeId, devtoolsEnabled, debugInstanceId, bootStatusQueue, shutdown, syncPayload } = adapterArgs
111
+
118
112
  yield* ensureBrowserRequirements
119
113
 
120
114
  yield* Queue.offer(bootStatusQueue, { stage: 'loading' })
@@ -392,135 +386,102 @@ export const makePersistedAdapter =
392
386
  }).pipe(Effect.tapCauseLogPretty, Effect.orDie),
393
387
  )
394
388
 
395
- const devtools: ClientSession['devtools'] = devtoolsEnabled
396
- ? { enabled: true, pullLatch: yield* Effect.makeLatch(true), pushLatch: yield* Effect.makeLatch(true) }
397
- : { enabled: false }
398
-
399
- const clientSession = {
400
- sqliteDb,
401
- devtools,
402
- lockStatus,
403
- clientId,
404
- sessionId,
405
-
406
- leaderThread: {
407
- export: runInWorker(new WorkerSchema.LeaderWorkerInner.Export()).pipe(
408
- Effect.timeout(10_000),
409
- UnexpectedError.mapToUnexpectedError,
410
- Effect.withSpan('@livestore/adapter-web:client-session:export'),
411
- ),
412
-
413
- events: {
414
- pull: ({ cursor }) =>
415
- runInWorkerStream(new WorkerSchema.LeaderWorkerInner.PullStream({ cursor })).pipe(Stream.orDie),
416
- push: (batch) =>
417
- runInWorker(new WorkerSchema.LeaderWorkerInner.PushToLeader({ batch })).pipe(
418
- Effect.withSpan('@livestore/adapter-web:client-session:pushToLeader', {
419
- attributes: { batchSize: batch.length },
420
- }),
421
- ),
422
- },
423
-
424
- initialState: { leaderHead: initialLeaderHead, migrationsReport },
425
-
426
- getEventlogData: runInWorker(new WorkerSchema.LeaderWorkerInner.ExportEventlog()).pipe(
427
- Effect.timeout(10_000),
428
- UnexpectedError.mapToUnexpectedError,
429
- Effect.withSpan('@livestore/adapter-web:client-session:getEventlogData'),
430
- ),
431
-
432
- getSyncState: runInWorker(new WorkerSchema.LeaderWorkerInner.GetLeaderSyncState()).pipe(
433
- UnexpectedError.mapToUnexpectedError,
434
- Effect.withSpan('@livestore/adapter-web:client-session:getLeaderSyncState'),
435
- ),
389
+ const leaderThread: ClientSession['leaderThread'] = {
390
+ export: runInWorker(new WorkerSchema.LeaderWorkerInner.Export()).pipe(
391
+ Effect.timeout(10_000),
392
+ UnexpectedError.mapToUnexpectedError,
393
+ Effect.withSpan('@livestore/adapter-web:client-session:export'),
394
+ ),
436
395
 
437
- sendDevtoolsMessage: (message) =>
438
- runInWorker(new WorkerSchema.LeaderWorkerInner.ExtraDevtoolsMessage({ message })).pipe(
439
- UnexpectedError.mapToUnexpectedError,
440
- Effect.withSpan('@livestore/adapter-web:client-session:devtoolsMessageForLeader'),
396
+ events: {
397
+ pull: ({ cursor }) =>
398
+ runInWorkerStream(new WorkerSchema.LeaderWorkerInner.PullStream({ cursor })).pipe(Stream.orDie),
399
+ push: (batch) =>
400
+ runInWorker(new WorkerSchema.LeaderWorkerInner.PushToLeader({ batch })).pipe(
401
+ Effect.withSpan('@livestore/adapter-web:client-session:pushToLeader', {
402
+ attributes: { batchSize: batch.length },
403
+ }),
441
404
  ),
442
405
  },
443
406
 
444
- shutdown,
445
- } satisfies ClientSession
446
-
447
- if (devtoolsEnabled) {
448
- yield* Effect.gen(function* () {
449
- const sharedWorker = yield* Fiber.join(sharedWorkerFiber)
450
-
451
- const webmeshNode = yield* Webmesh.makeMeshNode(`client-session-${storeId}-${clientId}-${sessionId}`)
452
- globalThis.__debugWebmeshNode = webmeshNode
453
-
454
- yield* logDevtoolsUrl({ clientSession, schema, storeId })
455
-
456
- const sessionsChannel = yield* makeSessionsChannel
457
- const sessionInfoMessage = Devtools.SessionInfo.SessionInfo.make({
458
- storeId,
459
- clientId,
460
- sessionId,
461
- schemaAlias: schema.devtools.alias,
462
- })
463
-
464
- yield* Devtools.SessionInfo.provideSessionInfo({
465
- webChannel: sessionsChannel,
466
- sessionInfo: sessionInfoMessage,
467
- }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
468
-
469
- yield* Effect.gen(function* () {
470
- const clientSessionStaticChannel = yield* WebChannel.windowChannel2({
471
- listenWindow: window,
472
- sendWindow: window,
473
- schema: { listen: Schema.Void, send: ClientSessionRequestContentscriptMain },
474
- ids: { own: 'client-session-static', other: 'contentscript-main-static' },
475
- })
476
-
477
- yield* clientSessionStaticChannel.send(
478
- ClientSessionRequestContentscriptMain.make({ clientId, sessionId, storeId }),
479
- )
407
+ initialState: { leaderHead: initialLeaderHead, migrationsReport },
480
408
 
481
- const contentscriptMainNodeName = `contentscript-main-${storeId}-${clientId}-${sessionId}`
482
-
483
- const contentscriptMainChannel = yield* WebChannel.windowChannel2({
484
- listenWindow: window,
485
- sendWindow: window,
486
- schema: Webmesh.WebmeshSchema.Packet,
487
- ids: { own: webmeshNode.nodeName, other: contentscriptMainNodeName },
488
- })
409
+ getEventlogData: runInWorker(new WorkerSchema.LeaderWorkerInner.ExportEventlog()).pipe(
410
+ Effect.timeout(10_000),
411
+ UnexpectedError.mapToUnexpectedError,
412
+ Effect.withSpan('@livestore/adapter-web:client-session:getEventlogData'),
413
+ ),
489
414
 
490
- yield* webmeshNode.addEdge({
491
- target: contentscriptMainNodeName,
492
- edgeChannel: contentscriptMainChannel,
493
- })
494
- // yield* Effect.logDebug(
495
- // `[@livestore/adapter-web:client-session] initiated connection: ${webmeshNode.nodeName} → contentscript-main`,
496
- // )
415
+ getSyncState: runInWorker(new WorkerSchema.LeaderWorkerInner.GetLeaderSyncState()).pipe(
416
+ UnexpectedError.mapToUnexpectedError,
417
+ Effect.withSpan('@livestore/adapter-web:client-session:getLeaderSyncState'),
418
+ ),
497
419
 
498
- const extensionWorkerChannel = yield* webmeshNode.makeBroadcastChannel({
499
- channelName: 'session-info',
500
- schema: Devtools.SessionInfo.Message,
501
- })
420
+ sendDevtoolsMessage: (message) =>
421
+ runInWorker(new WorkerSchema.LeaderWorkerInner.ExtraDevtoolsMessage({ message })).pipe(
422
+ UnexpectedError.mapToUnexpectedError,
423
+ Effect.withSpan('@livestore/adapter-web:client-session:devtoolsMessageForLeader'),
424
+ ),
425
+ }
502
426
 
427
+ const clientSession = yield* makeClientSession({
428
+ ...adapterArgs,
429
+ sqliteDb,
430
+ lockStatus,
431
+ clientId,
432
+ sessionId,
433
+ leaderThread,
434
+ webmeshMode: 'direct',
435
+ connectWebmeshNode: Effect.fn(function* ({ webmeshNode, sessionInfo }) {
436
+ if (devtoolsEnabled) {
437
+ yield* logDevtoolsUrl({ clientId, sessionId, schema, storeId })
438
+
439
+ // This additional sessioninfo broadcast channel is needed since we can't use the shared worker
440
+ // as it's currently storeId-specific
503
441
  yield* Devtools.SessionInfo.provideSessionInfo({
504
- webChannel: extensionWorkerChannel,
505
- sessionInfo: sessionInfoMessage,
506
- })
507
- }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
508
-
509
- yield* connectViaWorker({ node: webmeshNode, target: 'shared-worker', worker: sharedWorker })
442
+ webChannel: yield* DevtoolsWeb.makeSessionInfoBroadcastChannel,
443
+ sessionInfo,
444
+ }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
445
+
446
+ yield* Effect.gen(function* () {
447
+ const clientSessionStaticChannel = yield* DevtoolsWeb.makeStaticClientSessionChannel.clientSession
448
+
449
+ yield* clientSessionStaticChannel.send(
450
+ DevtoolsWeb.ClientSessionContentscriptMainReq.make({ clientId, sessionId, storeId }),
451
+ )
452
+
453
+ const { tabId } = yield* clientSessionStaticChannel.listen.pipe(
454
+ Stream.flatten(),
455
+ Stream.runHead,
456
+ Effect.flatten,
457
+ )
458
+
459
+ const contentscriptMainNodeName = DevtoolsWeb.makeNodeName.browserExtension.contentscriptMain(tabId)
460
+
461
+ const contentscriptMainChannel = yield* WebChannel.windowChannel({
462
+ listenWindow: window,
463
+ sendWindow: window,
464
+ schema: Webmesh.WebmeshSchema.Packet,
465
+ ids: { own: webmeshNode.nodeName, other: contentscriptMainNodeName },
466
+ })
467
+
468
+ yield* webmeshNode.addEdge({ target: contentscriptMainNodeName, edgeChannel: contentscriptMainChannel })
469
+ }).pipe(
470
+ Effect.withSpan('@livestore/adapter-web:client-session:devtools:browser-extension'),
471
+ Effect.tapCauseLogPretty,
472
+ Effect.forkScoped,
473
+ )
510
474
 
511
- const storeDevtoolsChannel = yield* makeChannelForConnectedMeshNode({
512
- node: webmeshNode,
513
- target: `devtools`,
514
- schema: { listen: Devtools.ClientSession.MessageToApp, send: Devtools.ClientSession.MessageFromApp },
515
- })
475
+ const sharedWorker = yield* Fiber.join(sharedWorkerFiber)
516
476
 
517
- yield* connectDevtoolsToStore(storeDevtoolsChannel)
518
- }).pipe(
519
- Effect.withSpan('@livestore/adapter-web:client-session:devtools'),
520
- Effect.tapCauseLogPretty,
521
- Effect.forkScoped,
522
- )
523
- }
477
+ yield* DevtoolsWeb.connectViaWorker({
478
+ node: webmeshNode,
479
+ target: DevtoolsWeb.makeNodeName.sharedWorker({ storeId }),
480
+ worker: sharedWorker,
481
+ })
482
+ }
483
+ }),
484
+ })
524
485
 
525
486
  return clientSession
526
487
  }).pipe(UnexpectedError.mapToUnexpectedError)
@@ -4,7 +4,6 @@ import type { DevtoolsOptions } from '@livestore/common/leader-thread'
4
4
  import { configureConnection, Eventlog, LeaderThreadCtx, makeLeaderThreadLayer } from '@livestore/common/leader-thread'
5
5
  import type { LiveStoreSchema } from '@livestore/common/schema'
6
6
  import { LiveStoreEvent } from '@livestore/common/schema'
7
- import { makeChannelForConnectedMeshNode } from '@livestore/devtools-web-common/web-channel'
8
7
  import * as WebmeshWorker from '@livestore/devtools-web-common/worker'
9
8
  import { sqliteDbFactory } from '@livestore/sqlite-wasm/browser'
10
9
  import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
@@ -92,7 +91,9 @@ const makeWorkerRunnerOuter = (
92
91
  Effect.scoped,
93
92
  Effect.withSpan('@livestore/adapter-web:worker:wrapper:InitialMessage:innerFiber'),
94
93
  Effect.tapCauseLogPretty,
95
- Effect.provide(WebmeshWorker.CacheService.layer({ nodeName: `leader-${storeId}-${clientId}` })),
94
+ Effect.provide(
95
+ WebmeshWorker.CacheService.layer({ nodeName: Devtools.makeNodeName.client.leader({ storeId, clientId }) }),
96
+ ),
96
97
  Effect.forkScoped,
97
98
  )
98
99
 
@@ -239,22 +240,18 @@ const makeDevtoolsOptions = ({
239
240
  if (devtoolsEnabled === false) {
240
241
  return { enabled: false }
241
242
  }
243
+
242
244
  const { node } = yield* WebmeshWorker.CacheService
243
245
 
244
246
  return {
245
247
  enabled: true,
246
- makeBootContext: Effect.gen(function* () {
247
- return {
248
- devtoolsWebChannel: yield* makeChannelForConnectedMeshNode({
249
- node,
250
- target: `devtools`,
251
- schema: { listen: Devtools.Leader.MessageToApp, send: Devtools.Leader.MessageFromApp },
252
- }),
253
- persistenceInfo: {
254
- state: dbState.metadata.persistenceInfo,
255
- eventlog: dbEventlog.metadata.persistenceInfo,
256
- },
248
+ boot: Effect.gen(function* () {
249
+ const persistenceInfo = {
250
+ state: dbState.metadata.persistenceInfo,
251
+ eventlog: dbEventlog.metadata.persistenceInfo,
257
252
  }
253
+
254
+ return { node, persistenceInfo, mode: 'direct' }
258
255
  }),
259
256
  }
260
257
  })
@@ -1,5 +1,5 @@
1
- import { UnexpectedError } from '@livestore/common'
2
- import { connectViaWorker } from '@livestore/devtools-web-common/web-channel'
1
+ import { Devtools, UnexpectedError } from '@livestore/common'
2
+ import * as DevtoolsWeb from '@livestore/devtools-web-common/web-channel'
3
3
  import * as WebmeshWorker from '@livestore/devtools-web-common/worker'
4
4
  import { isDevEnv, isNotUndefined, LS_DEV } from '@livestore/utils'
5
5
  import {
@@ -207,10 +207,11 @@ const makeWorkerRunner = Effect.gen(function* () {
207
207
  const { node } = yield* WebmeshWorker.CacheService
208
208
  const { storeId, clientId } = initialMessagePayload.initialMessage
209
209
 
210
- yield* connectViaWorker({ node, worker, target: `leader-${storeId}-${clientId}` }).pipe(
211
- Effect.tapCauseLogPretty,
212
- Effect.forkScoped,
213
- )
210
+ yield* DevtoolsWeb.connectViaWorker({
211
+ node,
212
+ worker,
213
+ target: Devtools.makeNodeName.client.leader({ storeId, clientId }),
214
+ }).pipe(Effect.tapCauseLogPretty, Effect.forkScoped)
214
215
 
215
216
  yield* SubscriptionRef.set(leaderWorkerContextSubRef, { worker, scope })
216
217
  }).pipe(Effect.tapCauseLogPretty, Scope.extend(scope), Effect.forkIn(scope))
@@ -239,6 +240,9 @@ const makeWorkerRunner = Effect.gen(function* () {
239
240
  }).pipe(Layer.unwrapScoped)
240
241
 
241
242
  export const makeWorker = () => {
243
+ // Extract from `livestore-shared-worker-${storeId}`
244
+ const storeId = self.name.replace('livestore-shared-worker-', '')
245
+
242
246
  makeWorkerRunner.pipe(
243
247
  Layer.provide(BrowserWorkerRunner.layer),
244
248
  // WorkerRunner.launch,
@@ -248,7 +252,7 @@ export const makeWorker = () => {
248
252
  Effect.annotateLogs({ thread: self.name }),
249
253
  Effect.provide(Logger.prettyWithThread(self.name)),
250
254
  Effect.provide(FetchHttpClient.layer),
251
- Effect.provide(WebmeshWorker.CacheService.layer({ nodeName: 'shared-worker' })),
255
+ Effect.provide(WebmeshWorker.CacheService.layer({ nodeName: DevtoolsWeb.makeNodeName.sharedWorker({ storeId }) })),
252
256
  LS_DEV ? TaskTracing.withAsyncTaggingTracing((name) => (console as any).createTask(name)) : identity,
253
257
  // TODO remove type-cast (currently needed to silence a tsc bug)
254
258
  (_) => _ as any as Effect.Effect<void, any>,