@livestore/adapter-web 0.0.0-snapshot-abe9ae4963ab9d3948906a6642c39bc33295e9f6 → 0.0.0-snapshot-1e670bae5105bde781e82aa2a8ee4f2dfc2446f0

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.
Files changed (36) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/web-worker/client-session/index.d.ts.map +1 -1
  3. package/dist/web-worker/client-session/index.js +13 -27
  4. package/dist/web-worker/client-session/index.js.map +1 -1
  5. package/dist/web-worker/common/shutdown-channel.d.ts +1 -5
  6. package/dist/web-worker/common/shutdown-channel.d.ts.map +1 -1
  7. package/dist/web-worker/common/worker-disconnect-channel.d.ts +12 -0
  8. package/dist/web-worker/common/worker-disconnect-channel.d.ts.map +1 -0
  9. package/dist/web-worker/common/worker-disconnect-channel.js +9 -0
  10. package/dist/web-worker/common/worker-disconnect-channel.js.map +1 -0
  11. package/dist/web-worker/common/worker-schema.d.ts +2 -2
  12. package/dist/web-worker/common/worker-schema.d.ts.map +1 -1
  13. package/dist/web-worker/common/worker-schema.js +2 -2
  14. package/dist/web-worker/leader-worker/make-leader-worker.d.ts +1 -1
  15. package/dist/web-worker/leader-worker/make-leader-worker.d.ts.map +1 -1
  16. package/dist/web-worker/leader-worker/make-leader-worker.js +4 -2
  17. package/dist/web-worker/leader-worker/make-leader-worker.js.map +1 -1
  18. package/dist/web-worker/shared-worker/make-shared-worker.d.ts.map +1 -1
  19. package/dist/web-worker/shared-worker/make-shared-worker.js +5 -3
  20. package/dist/web-worker/shared-worker/make-shared-worker.js.map +1 -1
  21. package/package.json +6 -6
  22. package/src/web-worker/client-session/index.ts +20 -34
  23. package/src/web-worker/common/worker-disconnect-channel.ts +10 -0
  24. package/src/web-worker/common/worker-schema.ts +2 -2
  25. package/src/web-worker/leader-worker/make-leader-worker.ts +4 -2
  26. package/src/web-worker/shared-worker/make-shared-worker.ts +2 -2
  27. package/dist/web-worker/client-session/trim-batch.d.ts +0 -4
  28. package/dist/web-worker/client-session/trim-batch.d.ts.map +0 -1
  29. package/dist/web-worker/client-session/trim-batch.js +0 -13
  30. package/dist/web-worker/client-session/trim-batch.js.map +0 -1
  31. package/dist/web-worker/client-session/trim-batch.test.d.ts +0 -2
  32. package/dist/web-worker/client-session/trim-batch.test.d.ts.map +0 -1
  33. package/dist/web-worker/client-session/trim-batch.test.js +0 -38
  34. package/dist/web-worker/client-session/trim-batch.test.js.map +0 -1
  35. package/src/web-worker/client-session/trim-batch.test.ts +0 -48
  36. package/src/web-worker/client-session/trim-batch.ts +0 -15
@@ -3,8 +3,6 @@ import { Devtools, IntentionalShutdownCause, UnexpectedError } from '@livestore/
3
3
  // TODO bring back - this currently doesn't work due to https://github.com/vitejs/vite/issues/8427
4
4
  // NOTE We're using a non-relative import here for Vite to properly resolve the import during app builds
5
5
  // import LiveStoreSharedWorker from '@livestore/adapter-web/internal-shared-worker?sharedworker'
6
- import { ShutdownChannel } from '@livestore/common/leader-thread'
7
- import type { MutationEvent } from '@livestore/common/schema'
8
6
  import { EventId, SESSION_CHANGESET_META_TABLE } from '@livestore/common/schema'
9
7
  import { makeWebDevtoolsChannel } from '@livestore/devtools-web-common/web-channel'
10
8
  import { sqliteDbFactory } from '@livestore/sqlite-wasm/browser'
@@ -12,7 +10,6 @@ import { loadSqlite3Wasm } from '@livestore/sqlite-wasm/load-wasm'
12
10
  import { isDevEnv, shouldNeverHappen, tryAsFunctionAndNew } from '@livestore/utils'
13
11
  import {
14
12
  BrowserWorker,
15
- BucketQueue,
16
13
  Cause,
17
14
  Deferred,
18
15
  Effect,
@@ -32,9 +29,9 @@ import { nanoid } from '@livestore/utils/nanoid'
32
29
  import * as OpfsUtils from '../../opfs-utils.js'
33
30
  import { readPersistedAppDbFromClientSession, resetPersistedDataFromClientSession } from '../common/persisted-sqlite.js'
34
31
  import { makeShutdownChannel } from '../common/shutdown-channel.js'
32
+ import { DedicatedWorkerDisconnectBroadcast, makeWorkerDisconnectChannel } from '../common/worker-disconnect-channel.js'
35
33
  import * as WorkerSchema from '../common/worker-schema.js'
36
34
  import { bootDevtools } from './client-session-devtools.js'
37
- import { trimPushBatch } from './trim-batch.js'
38
35
 
39
36
  // NOTE we're starting to initialize the sqlite wasm binary here to speed things up
40
37
  const sqlite3Promise = loadSqlite3Wasm()
@@ -111,11 +108,11 @@ export const makeAdapter =
111
108
  const sessionId = getPersistedId(`sessionId:${storeId}`, 'session')
112
109
 
113
110
  const shutdownChannel = yield* makeShutdownChannel(storeId)
111
+ const workerDisconnectChannel = yield* makeWorkerDisconnectChannel(storeId)
114
112
 
115
113
  yield* shutdownChannel.listen.pipe(
116
114
  Stream.flatten(),
117
- Stream.filter(Schema.is(IntentionalShutdownCause)),
118
- Stream.tap((msg) => shutdown(Cause.fail(msg))),
115
+ Stream.tap((error) => Effect.sync(() => shutdown(Cause.fail(error)))),
119
116
  Stream.runDrain,
120
117
  Effect.interruptible,
121
118
  Effect.tapCauseLogPretty,
@@ -144,7 +141,7 @@ export const makeAdapter =
144
141
  Effect.provide(BrowserWorker.layer(() => sharedWebWorker)),
145
142
  Effect.tapCauseLogPretty,
146
143
  UnexpectedError.mapToUnexpectedError,
147
- Effect.tapErrorCause(shutdown),
144
+ Effect.tapErrorCause((cause) => Effect.sync(() => shutdown(cause))),
148
145
  Effect.withSpan('@livestore/adapter-web:client-session:setupSharedWorker'),
149
146
  Effect.forkScoped,
150
147
  )
@@ -187,18 +184,19 @@ export const makeAdapter =
187
184
  }).pipe(
188
185
  Effect.provide(BrowserWorker.layer(() => worker)),
189
186
  UnexpectedError.mapToUnexpectedError,
190
- Effect.tapErrorCause(shutdown),
187
+ Effect.tapErrorCause((cause) => Effect.sync(() => shutdown(cause))),
191
188
  Effect.withSpan('@livestore/adapter-web:client-session:setupDedicatedWorker'),
192
189
  Effect.tapCauseLogPretty,
193
190
  Effect.forkScoped,
194
191
  )
195
192
 
196
- yield* shutdownChannel.send(ShutdownChannel.DedicatedWorkerDisconnectBroadcast.make({}))
193
+ yield* workerDisconnectChannel.send(DedicatedWorkerDisconnectBroadcast.make({}))
197
194
 
198
195
  const sharedWorker = yield* Fiber.join(sharedWorkerFiber)
199
- yield* sharedWorker
200
- .executeEffect(new WorkerSchema.SharedWorker.UpdateMessagePort({ port: mc.port2 }))
201
- .pipe(UnexpectedError.mapToUnexpectedError, Effect.tapErrorCause(shutdown))
196
+ yield* sharedWorker.executeEffect(new WorkerSchema.SharedWorker.UpdateMessagePort({ port: mc.port2 })).pipe(
197
+ UnexpectedError.mapToUnexpectedError,
198
+ Effect.tapErrorCause((cause) => Effect.sync(() => shutdown(cause))),
199
+ )
202
200
 
203
201
  yield* Deferred.succeed(waitForSharedWorkerInitialized, undefined)
204
202
 
@@ -304,7 +302,7 @@ export const makeAdapter =
304
302
  Stream.tap((_) => SubscriptionRef.set(networkStatus, _)),
305
303
  Stream.runDrain,
306
304
  Effect.forever, // NOTE Whenever the leader changes, we need to re-start the stream
307
- Effect.tapErrorCause(shutdown),
305
+ Effect.tapErrorCause((cause) => Effect.sync(() => shutdown(cause))),
308
306
  Effect.interruptible,
309
307
  Effect.tapCauseLogPretty,
310
308
  Effect.forkScoped,
@@ -313,7 +311,9 @@ export const makeAdapter =
313
311
  const bootStatusFiber = yield* runInWorkerStream(new WorkerSchema.LeaderWorkerInner.BootStatusStream()).pipe(
314
312
  Stream.tap((_) => Queue.offer(bootStatusQueue, _)),
315
313
  Stream.runDrain,
316
- Effect.tapErrorCause((cause) => (Cause.isInterruptedOnly(cause) ? Effect.void : shutdown(cause))),
314
+ Effect.tapErrorCause((cause) =>
315
+ Cause.isInterruptedOnly(cause) ? Effect.void : Effect.sync(() => shutdown(cause)),
316
+ ),
317
317
  Effect.interruptible,
318
318
  Effect.tapCauseLogPretty,
319
319
  Effect.forkScoped,
@@ -388,21 +388,6 @@ export const makeAdapter =
388
388
  }).pipe(Effect.tapCauseLogPretty, Effect.orDie),
389
389
  )
390
390
 
391
- const pushQueue = yield* BucketQueue.make<MutationEvent.AnyEncoded>()
392
-
393
- yield* Effect.gen(function* () {
394
- const batch = yield* BucketQueue.takeBetween(pushQueue, 1, 100)
395
- // We need to trim "old batches" which can happen during client session rebasing
396
- const trimmedBatch = trimPushBatch(batch)
397
- yield* runInWorker(new WorkerSchema.LeaderWorkerInner.PushToLeader({ batch: trimmedBatch })).pipe(
398
- Effect.withSpan('@livestore/adapter-web:client-session:pushToLeader', {
399
- attributes: { batchSize: batch.length },
400
- }),
401
- // We can ignore the error here because the ClientSessionSyncProcessor will retry after rebasing
402
- Effect.ignoreLogged,
403
- )
404
- }).pipe(Effect.forever, Effect.interruptible, Effect.tapCauseLogPretty, Effect.forkScoped)
405
-
406
391
  const devtools: ClientSession['devtools'] = devtoolsEnabled
407
392
  ? { enabled: true, pullLatch: yield* Effect.makeLatch(true), pushLatch: yield* Effect.makeLatch(true) }
408
393
  : { enabled: false }
@@ -425,11 +410,12 @@ export const makeAdapter =
425
410
  pull: runInWorkerStream(new WorkerSchema.LeaderWorkerInner.PullStream({ cursor: initialLeaderHead })).pipe(
426
411
  Stream.orDie,
427
412
  ),
428
-
429
- // NOTE instead of sending the worker message right away, we're batching the events in order to
430
- // - maintain a consistent order of events
431
- // - improve efficiency by reducing the number of messages
432
- push: (batch) => BucketQueue.offerAll(pushQueue, batch),
413
+ push: (batch) =>
414
+ runInWorker(new WorkerSchema.LeaderWorkerInner.PushToLeader({ batch })).pipe(
415
+ Effect.withSpan('@livestore/adapter-web:client-session:pushToLeader', {
416
+ attributes: { batchSize: batch.length },
417
+ }),
418
+ ),
433
419
  },
434
420
 
435
421
  initialState: { leaderHead: initialLeaderHead, migrationsReport },
@@ -0,0 +1,10 @@
1
+ import { Schema, WebChannel } from '@livestore/utils/effect'
2
+
3
+ export class DedicatedWorkerDisconnectBroadcast extends Schema.TaggedStruct('DedicatedWorkerDisconnectBroadcast', {}) {}
4
+
5
+ /** Used across workers for leader election purposes */
6
+ export const makeWorkerDisconnectChannel = (storeId: string) =>
7
+ WebChannel.broadcastChannel({
8
+ channelName: `livestore.worker-disconnect.${storeId}`,
9
+ schema: DedicatedWorkerDisconnectBroadcast,
10
+ })
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BootStatus,
3
3
  Devtools,
4
- InvalidPushError,
4
+ LeaderAheadError,
5
5
  MigrationsReport,
6
6
  NetworkStatus,
7
7
  SyncState,
@@ -78,7 +78,7 @@ export namespace LeaderWorkerInner {
78
78
  batch: Schema.Array(MutationEvent.AnyEncoded),
79
79
  },
80
80
  success: Schema.Void,
81
- failure: Schema.Union(UnexpectedError, InvalidPushError),
81
+ failure: Schema.Union(UnexpectedError, LeaderAheadError),
82
82
  }) {}
83
83
 
84
84
  export class PullStream extends Schema.TaggedRequest<PullStream>()('PullStream', {
@@ -48,6 +48,8 @@ if (isDevEnv()) {
48
48
  globalThis.__debugLiveStoreUtils = {
49
49
  opfs: OpfsUtils,
50
50
  blobUrl: (buffer: Uint8Array) => URL.createObjectURL(new Blob([buffer], { type: 'application/octet-stream' })),
51
+ runSync: (effect: Effect.Effect<any, any, never>) => Effect.runSync(effect),
52
+ runFork: (effect: Effect.Effect<any, any, never>) => Effect.runFork(effect),
51
53
  }
52
54
  }
53
55
 
@@ -64,7 +66,7 @@ export const makeWorkerEffect = (options: WorkerOptions) => {
64
66
 
65
67
  return makeWorkerRunnerOuter(options).pipe(
66
68
  Layer.provide(BrowserWorkerRunner.layer),
67
- Layer.launch,
69
+ WorkerRunner.launch,
68
70
  Effect.scoped,
69
71
  Effect.tapCauseLogPretty,
70
72
  Effect.annotateLogs({ thread: self.name }),
@@ -91,7 +93,7 @@ const makeWorkerRunnerOuter = (
91
93
  Effect.gen(function* () {
92
94
  yield* makeWorkerRunnerInner(workerOptions).pipe(
93
95
  Layer.provide(BrowserWorkerRunner.layerMessagePort(incomingRequestsPort)),
94
- Layer.launch,
96
+ WorkerRunner.launch,
95
97
  Effect.scoped,
96
98
  Effect.withSpan('@livestore/adapter-web:worker:wrapper:InitialMessage:innerFiber'),
97
99
  Effect.tapCauseLogPretty,
@@ -1,4 +1,4 @@
1
- import { IntentionalShutdownCause, UnexpectedError } from '@livestore/common'
1
+ import { UnexpectedError } from '@livestore/common'
2
2
  import { connectViaWorker } 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'
@@ -233,7 +233,6 @@ const makeWorkerRunner = Effect.gen(function* () {
233
233
 
234
234
  yield* shutdownChannel.listen.pipe(
235
235
  Stream.flatten(),
236
- Stream.filter(Schema.is(IntentionalShutdownCause)),
237
236
  Stream.tap(() => reset),
238
237
  Stream.runDrain,
239
238
  )
@@ -282,6 +281,7 @@ const makeWorkerRunner = Effect.gen(function* () {
282
281
  export const makeWorker = () => {
283
282
  makeWorkerRunner.pipe(
284
283
  Layer.provide(BrowserWorkerRunner.layer),
284
+ // WorkerRunner.launch,
285
285
  Layer.launch,
286
286
  Effect.scoped,
287
287
  Effect.tapCauseLogPretty,
@@ -1,4 +0,0 @@
1
- import type { MutationEvent } from '@livestore/common/schema';
2
- /** [(0,1), (0,2), (1,0), (0,1), (0,2), (1,0), (1,1)] -> [(0,1), (0,2), (1,0), (1,1)] */
3
- export declare const trimPushBatch: (batch: ReadonlyArray<MutationEvent.AnyEncoded>) => readonly MutationEvent.AnyEncoded[];
4
- //# sourceMappingURL=trim-batch.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"trim-batch.d.ts","sourceRoot":"","sources":["../../../src/web-worker/client-session/trim-batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAG7D,wFAAwF;AACxF,eAAO,MAAM,aAAa,UAAW,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,wCAU3E,CAAA"}
@@ -1,13 +0,0 @@
1
- import { EventId } from '@livestore/common/schema';
2
- /** [(0,1), (0,2), (1,0), (0,1), (0,2), (1,0), (1,1)] -> [(0,1), (0,2), (1,0), (1,1)] */
3
- export const trimPushBatch = (batch) => {
4
- // Iterate over batch from the end and stop once we encounter an event with a larger id than the previous event
5
- // Then return the slice of the batch up to and including that event
6
- for (let i = batch.length - 2; i >= 0; i--) {
7
- if (EventId.isGreaterThanOrEqual(batch[i].id, batch[i + 1].id)) {
8
- return batch.slice(i + 1);
9
- }
10
- }
11
- return batch;
12
- };
13
- //# sourceMappingURL=trim-batch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"trim-batch.js","sourceRoot":"","sources":["../../../src/web-worker/client-session/trim-batch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAElD,wFAAwF;AACxF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAA8C,EAAE,EAAE;IAC9E,+GAA+G;IAC/G,oEAAoE;IACpE,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,IAAI,OAAO,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,EAAE,CAAC,EAAE,CAAC;YACjE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=trim-batch.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"trim-batch.test.d.ts","sourceRoot":"","sources":["../../../src/web-worker/client-session/trim-batch.test.ts"],"names":[],"mappings":""}
@@ -1,38 +0,0 @@
1
- import { EventId } from '@livestore/common/schema';
2
- import { describe, expect, it } from 'vitest';
3
- import { trimPushBatch } from './trim-batch.js';
4
- describe('trimPushBatch', () => {
5
- it('should return same batch', () => {
6
- const batch = [
7
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
8
- { id: EventId.make({ global: 0, client: 2 }), parentId: EventId.make({ global: 0, client: 1 }) },
9
- { id: EventId.make({ global: 1, client: 0 }), parentId: EventId.make({ global: 0, client: 0 }) },
10
- { id: EventId.make({ global: 1, client: 1 }), parentId: EventId.make({ global: 1, client: 0 }) },
11
- ];
12
- const trimmed = trimPushBatch(batch);
13
- expect(trimmed).toEqual(batch);
14
- });
15
- it('should trim the batch', () => {
16
- const batch = [
17
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
18
- { id: EventId.make({ global: 0, client: 2 }), parentId: EventId.make({ global: 0, client: 1 }) },
19
- // should trim above
20
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
21
- { id: EventId.make({ global: 0, client: 2 }), parentId: EventId.make({ global: 0, client: 1 }) },
22
- { id: EventId.make({ global: 1, client: 0 }), parentId: EventId.make({ global: 0, client: 0 }) },
23
- { id: EventId.make({ global: 1, client: 1 }), parentId: EventId.make({ global: 1, client: 0 }) },
24
- ];
25
- const trimmed = trimPushBatch(batch);
26
- expect(trimmed).toEqual(batch.slice(2));
27
- });
28
- it('should trim the batch', () => {
29
- const batch = [
30
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
31
- // should trim above
32
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
33
- ];
34
- const trimmed = trimPushBatch(batch);
35
- expect(trimmed).toEqual(batch.slice(1));
36
- });
37
- });
38
- //# sourceMappingURL=trim-batch.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"trim-batch.test.js","sourceRoot":"","sources":["../../../src/web-worker/client-session/trim-batch.test.ts"],"names":[],"mappings":"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"}
@@ -1,48 +0,0 @@
1
- import type { MutationEvent } from '@livestore/common/schema'
2
- import { EventId } from '@livestore/common/schema'
3
- import { describe, expect, it } from 'vitest'
4
-
5
- import { trimPushBatch } from './trim-batch.js'
6
-
7
- describe('trimPushBatch', () => {
8
- it('should return same batch', () => {
9
- const batch = [
10
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
11
- { id: EventId.make({ global: 0, client: 2 }), parentId: EventId.make({ global: 0, client: 1 }) },
12
- { id: EventId.make({ global: 1, client: 0 }), parentId: EventId.make({ global: 0, client: 0 }) },
13
- { id: EventId.make({ global: 1, client: 1 }), parentId: EventId.make({ global: 1, client: 0 }) },
14
- ] as MutationEvent.AnyEncoded[]
15
-
16
- const trimmed = trimPushBatch(batch)
17
-
18
- expect(trimmed).toEqual(batch)
19
- })
20
-
21
- it('should trim the batch', () => {
22
- const batch = [
23
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
24
- { id: EventId.make({ global: 0, client: 2 }), parentId: EventId.make({ global: 0, client: 1 }) },
25
- // should trim above
26
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
27
- { id: EventId.make({ global: 0, client: 2 }), parentId: EventId.make({ global: 0, client: 1 }) },
28
- { id: EventId.make({ global: 1, client: 0 }), parentId: EventId.make({ global: 0, client: 0 }) },
29
- { id: EventId.make({ global: 1, client: 1 }), parentId: EventId.make({ global: 1, client: 0 }) },
30
- ] as MutationEvent.AnyEncoded[]
31
-
32
- const trimmed = trimPushBatch(batch)
33
-
34
- expect(trimmed).toEqual(batch.slice(2))
35
- })
36
-
37
- it('should trim the batch', () => {
38
- const batch = [
39
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
40
- // should trim above
41
- { id: EventId.make({ global: 0, client: 1 }), parentId: EventId.make({ global: 0, client: 0 }) },
42
- ] as MutationEvent.AnyEncoded[]
43
-
44
- const trimmed = trimPushBatch(batch)
45
-
46
- expect(trimmed).toEqual(batch.slice(1))
47
- })
48
- })
@@ -1,15 +0,0 @@
1
- import type { MutationEvent } from '@livestore/common/schema'
2
- import { EventId } from '@livestore/common/schema'
3
-
4
- /** [(0,1), (0,2), (1,0), (0,1), (0,2), (1,0), (1,1)] -> [(0,1), (0,2), (1,0), (1,1)] */
5
- export const trimPushBatch = (batch: ReadonlyArray<MutationEvent.AnyEncoded>) => {
6
- // Iterate over batch from the end and stop once we encounter an event with a larger id than the previous event
7
- // Then return the slice of the batch up to and including that event
8
- for (let i = batch.length - 2; i >= 0; i--) {
9
- if (EventId.isGreaterThanOrEqual(batch[i]!.id, batch[i + 1]!.id)) {
10
- return batch.slice(i + 1)
11
- }
12
- }
13
-
14
- return batch
15
- }