@livestore/adapter-web 0.0.0-snapshot-04bc235dd721238cb10c060705672376b6e06dd3 → 0.0.0-snapshot-f695fd72a038f4deb7b452e82cb37999b5cc30b9

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,7 +1,7 @@
1
1
  import type { Adapter, ClientSession, LockStatus } from '@livestore/common'
2
2
  import {
3
- Devtools,
4
3
  IntentionalShutdownCause,
4
+ liveStoreVersion,
5
5
  makeClientSession,
6
6
  StoreInterrupted,
7
7
  UnexpectedError,
@@ -9,8 +9,7 @@ import {
9
9
  // TODO bring back - this currently doesn't work due to https://github.com/vitejs/vite/issues/8427
10
10
  // NOTE We're using a non-relative import here for Vite to properly resolve the import during app builds
11
11
  // import LiveStoreSharedWorker from '@livestore/adapter-web/internal-shared-worker?sharedworker'
12
- import { EventId, SystemTables } from '@livestore/common/schema'
13
- import * as DevtoolsWeb from '@livestore/devtools-web-common/web-channel'
12
+ import { EventSequenceNumber, SystemTables } from '@livestore/common/schema'
14
13
  import { sqliteDbFactory } from '@livestore/sqlite-wasm/browser'
15
14
  import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
16
15
  import { isDevEnv, shouldNeverHappen, tryAsFunctionAndNew } from '@livestore/utils'
@@ -26,20 +25,18 @@ import {
26
25
  Schema,
27
26
  Stream,
28
27
  SubscriptionRef,
29
- WebChannel,
30
28
  WebLock,
31
29
  Worker,
32
30
  WorkerError,
33
31
  } from '@livestore/utils/effect'
34
32
  import { nanoid } from '@livestore/utils/nanoid'
35
- import * as Webmesh from '@livestore/webmesh'
36
33
 
37
34
  import * as OpfsUtils from '../../opfs-utils.js'
38
35
  import { readPersistedAppDbFromClientSession, resetPersistedDataFromClientSession } from '../common/persisted-sqlite.js'
39
36
  import { makeShutdownChannel } from '../common/shutdown-channel.js'
40
37
  import { DedicatedWorkerDisconnectBroadcast, makeWorkerDisconnectChannel } from '../common/worker-disconnect-channel.js'
41
38
  import * as WorkerSchema from '../common/worker-schema.js'
42
- import { logDevtoolsUrl } from './client-session-devtools.js'
39
+ import { connectWebmeshNodeClientSession } from './client-session-devtools.js'
43
40
 
44
41
  // NOTE we're starting to initialize the sqlite wasm binary here to speed things up
45
42
  const sqlite3Promise = loadSqlite3Wasm()
@@ -147,7 +144,7 @@ export const makePersistedAdapter =
147
144
 
148
145
  yield* shutdownChannel.listen.pipe(
149
146
  Stream.flatten(),
150
- Stream.tap((error) => Effect.sync(() => shutdown(Cause.fail(error)))),
147
+ Stream.tap((error) => shutdown(Cause.fail(error))),
151
148
  Stream.runDrain,
152
149
  Effect.interruptible,
153
150
  Effect.tapCauseLogPretty,
@@ -161,6 +158,7 @@ export const makePersistedAdapter =
161
158
  concurrency: 100,
162
159
  initialMessage: () =>
163
160
  new WorkerSchema.SharedWorker.InitialMessage({
161
+ liveStoreVersion,
164
162
  payload: {
165
163
  _tag: 'FromClientSession',
166
164
  initialMessage: new WorkerSchema.LeaderWorkerInner.InitialMessage({
@@ -177,7 +175,7 @@ export const makePersistedAdapter =
177
175
  Effect.provide(BrowserWorker.layer(() => sharedWebWorker)),
178
176
  Effect.tapCauseLogPretty,
179
177
  UnexpectedError.mapToUnexpectedError,
180
- Effect.tapErrorCause((cause) => Effect.sync(() => shutdown(cause))),
178
+ Effect.tapErrorCause(shutdown),
181
179
  Effect.withSpan('@livestore/adapter-web:client-session:setupSharedWorker'),
182
180
  Effect.forkScoped,
183
181
  )
@@ -220,7 +218,7 @@ export const makePersistedAdapter =
220
218
  }).pipe(
221
219
  Effect.provide(BrowserWorker.layer(() => worker)),
222
220
  UnexpectedError.mapToUnexpectedError,
223
- Effect.tapErrorCause((cause) => Effect.sync(() => shutdown(cause))),
221
+ Effect.tapErrorCause(shutdown),
224
222
  Effect.withSpan('@livestore/adapter-web:client-session:setupDedicatedWorker'),
225
223
  Effect.tapCauseLogPretty,
226
224
  Effect.forkScoped,
@@ -229,10 +227,9 @@ export const makePersistedAdapter =
229
227
  yield* workerDisconnectChannel.send(DedicatedWorkerDisconnectBroadcast.make({}))
230
228
 
231
229
  const sharedWorker = yield* Fiber.join(sharedWorkerFiber)
232
- yield* sharedWorker.executeEffect(new WorkerSchema.SharedWorker.UpdateMessagePort({ port: mc.port2 })).pipe(
233
- UnexpectedError.mapToUnexpectedError,
234
- Effect.tapErrorCause((cause) => Effect.sync(() => shutdown(cause))),
235
- )
230
+ yield* sharedWorker
231
+ .executeEffect(new WorkerSchema.SharedWorker.UpdateMessagePort({ port: mc.port2 }))
232
+ .pipe(UnexpectedError.mapToUnexpectedError, Effect.tapErrorCause(shutdown))
236
233
 
237
234
  yield* Deferred.succeed(waitForSharedWorkerInitialized, undefined)
238
235
 
@@ -308,9 +305,7 @@ export const makePersistedAdapter =
308
305
  const bootStatusFiber = yield* runInWorkerStream(new WorkerSchema.LeaderWorkerInner.BootStatusStream()).pipe(
309
306
  Stream.tap((_) => Queue.offer(bootStatusQueue, _)),
310
307
  Stream.runDrain,
311
- Effect.tapErrorCause((cause) =>
312
- Cause.isInterruptedOnly(cause) ? Effect.void : Effect.sync(() => shutdown(cause)),
313
- ),
308
+ Effect.tapErrorCause((cause) => (Cause.isInterruptedOnly(cause) ? Effect.void : shutdown(cause))),
314
309
  Effect.interruptible,
315
310
  Effect.tapCauseLogPretty,
316
311
  Effect.forkScoped,
@@ -355,15 +350,18 @@ export const makePersistedAdapter =
355
350
  // We're restoring the leader head from the SESSION_CHANGESET_META_TABLE, not from the eventlog db/table
356
351
  // in order to avoid exporting/transferring the eventlog db/table, which is important to speed up the fast path.
357
352
  const initialLeaderHeadRes = sqliteDb.select<{
358
- idGlobal: EventId.GlobalEventId
359
- idClient: EventId.ClientEventId
353
+ seqNumGlobal: EventSequenceNumber.GlobalEventSequenceNumber
354
+ seqNumClient: EventSequenceNumber.ClientEventSequenceNumber
360
355
  }>(
361
- `select idGlobal, idClient from ${SystemTables.SESSION_CHANGESET_META_TABLE} order by idGlobal desc, idClient desc limit 1`,
356
+ `select seqNumGlobal, seqNumClient from ${SystemTables.SESSION_CHANGESET_META_TABLE} order by seqNumGlobal desc, seqNumClient desc limit 1`,
362
357
  )[0]
363
358
 
364
359
  const initialLeaderHead = initialLeaderHeadRes
365
- ? EventId.make({ global: initialLeaderHeadRes.idGlobal, client: initialLeaderHeadRes.idClient })
366
- : EventId.ROOT
360
+ ? EventSequenceNumber.make({
361
+ global: initialLeaderHeadRes.seqNumGlobal,
362
+ client: initialLeaderHeadRes.seqNumClient,
363
+ })
364
+ : EventSequenceNumber.ROOT
367
365
 
368
366
  // console.debug('[@livestore/adapter-web:client-session] initialLeaderHead', initialLeaderHead)
369
367
 
@@ -424,63 +422,28 @@ export const makePersistedAdapter =
424
422
  ),
425
423
  }
426
424
 
425
+ const sharedWorker = yield* Fiber.join(sharedWorkerFiber)
426
+
427
427
  const clientSession = yield* makeClientSession({
428
428
  ...adapterArgs,
429
429
  sqliteDb,
430
430
  lockStatus,
431
431
  clientId,
432
432
  sessionId,
433
+ // isLeader: gotLocky, // TODO update when leader is changing
434
+ isLeader: true,
433
435
  leaderThread,
434
436
  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
441
- yield* Devtools.SessionInfo.provideSessionInfo({
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
- )
474
-
475
- const sharedWorker = yield* Fiber.join(sharedWorkerFiber)
476
-
477
- yield* DevtoolsWeb.connectViaWorker({
478
- node: webmeshNode,
479
- target: DevtoolsWeb.makeNodeName.sharedWorker({ storeId }),
480
- worker: sharedWorker,
481
- })
437
+ connectWebmeshNode: ({ sessionInfo, webmeshNode }) =>
438
+ connectWebmeshNodeClientSession({ webmeshNode, sessionInfo, sharedWorker, devtoolsEnabled, schema }),
439
+ registerBeforeUnload: (onBeforeUnload) => {
440
+ if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') {
441
+ window.addEventListener('beforeunload', onBeforeUnload)
442
+ return () => window.removeEventListener('beforeunload', onBeforeUnload)
482
443
  }
483
- }),
444
+
445
+ return () => {}
446
+ },
484
447
  })
485
448
 
486
449
  return clientSession
@@ -3,11 +3,12 @@ import {
3
3
  Devtools,
4
4
  LeaderAheadError,
5
5
  LeaderPullCursor,
6
+ liveStoreVersion,
6
7
  MigrationsReport,
7
8
  SyncState,
8
9
  UnexpectedError,
9
10
  } from '@livestore/common'
10
- import { EventId, LiveStoreEvent } from '@livestore/common/schema'
11
+ import { EventSequenceNumber, LiveStoreEvent } from '@livestore/common/schema'
11
12
  import * as WebmeshWorker from '@livestore/devtools-web-common/worker'
12
13
  import { Schema, Transferable } from '@livestore/utils/effect'
13
14
 
@@ -116,7 +117,7 @@ export namespace LeaderWorkerInner {
116
117
 
117
118
  export class GetLeaderHead extends Schema.TaggedRequest<GetLeaderHead>()('GetLeaderHead', {
118
119
  payload: {},
119
- success: EventId.EventId,
120
+ success: EventSequenceNumber.EventSequenceNumber,
120
121
  failure: UnexpectedError,
121
122
  }) {}
122
123
 
@@ -165,6 +166,9 @@ export namespace SharedWorker {
165
166
  export class InitialMessage extends Schema.TaggedRequest<InitialMessage>()('InitialMessage', {
166
167
  payload: {
167
168
  payload: Schema.Union(InitialMessagePayloadFromClientSession, Schema.TaggedStruct('FromWebBridge', {})),
169
+ // To guard against scenarios where a client session is already running a newer version of LiveStore
170
+ // We should probably find a better way to handle those cases once they become more common.
171
+ liveStoreVersion: Schema.Literal(liveStoreVersion),
168
172
  },
169
173
  success: Schema.Void,
170
174
  failure: UnexpectedError,