@livestore/livestore 0.3.0-dev.46 → 0.3.0-dev.48

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.
@@ -15,6 +15,7 @@ import {
15
15
  Deferred,
16
16
  Effect,
17
17
  Exit,
18
+ Fiber,
18
19
  identity,
19
20
  Layer,
20
21
  Logger,
@@ -210,7 +211,7 @@ export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema, T
210
211
 
211
212
  const runtime = yield* Effect.runtime<Scope.Scope>()
212
213
 
213
- const shutdown = (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) => {
214
+ const shutdown = (cause: Cause.Cause<UnexpectedError | IntentionalShutdownCause>) =>
214
215
  Effect.gen(function* () {
215
216
  yield* Scope.close(lifetimeScope, Exit.failCause(cause)).pipe(
216
217
  Effect.logWarnIfTakesLongerThan({ label: '@livestore/livestore:shutdown', duration: 500 }),
@@ -232,8 +233,8 @@ export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema, T
232
233
  // Given that the shutdown flow might also interrupt the effect that is calling the shutdown,
233
234
  // we want to detach the shutdown effect so it's not interrupted by itself
234
235
  Effect.runFork,
236
+ Fiber.join,
235
237
  )
236
- }
237
238
 
238
239
  const clientSession: ClientSession = yield* adapter({
239
240
  schema,
@@ -23,7 +23,7 @@ import type { LiveStoreSchema } from '@livestore/common/schema'
23
23
  import { getEventDef, LiveStoreEvent, SystemTables } from '@livestore/common/schema'
24
24
  import { assertNever, isDevEnv } from '@livestore/utils'
25
25
  import type { Scope } from '@livestore/utils/effect'
26
- import { Cause, Effect, Inspectable, OtelTracer, Runtime, Schema, Stream } from '@livestore/utils/effect'
26
+ import { Cause, Effect, Fiber, Inspectable, OtelTracer, Runtime, Schema, Stream } from '@livestore/utils/effect'
27
27
  import { nanoid } from '@livestore/utils/nanoid'
28
28
  import * as otel from '@opentelemetry/api'
29
29
 
@@ -375,6 +375,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
375
375
  | QueryBuilder<TResult, any, any>
376
376
  | LiveQuery<TResult>
377
377
  | LiveQueryDef<TResult>
378
+ | SignalDef<TResult>
378
379
  | { query: string; bindValues: ParamsObject },
379
380
  options?: { otelContext?: otel.Context; debugRefreshReason?: RefreshReason },
380
381
  ): TResult => {
@@ -406,14 +407,33 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
406
407
  const result = this.query(query$.value, options)
407
408
  query$.deref()
408
409
  return result
410
+ } else if (query._tag === 'signal-def') {
411
+ const signal$ = query.make(this.reactivityGraph.context!)
412
+ return signal$.value.get()
409
413
  } else {
410
414
  return query.run({ otelContext: options?.otelContext, debugRefreshReason: options?.debugRefreshReason })
411
415
  }
412
416
  }
413
417
 
414
- setSignal = <T>(signalDef: SignalDef<T>, value: T): void => {
418
+ /**
419
+ * Set the value of a signal
420
+ *
421
+ * @example
422
+ * ```ts
423
+ * const count$ = signal(0, { label: 'count$' })
424
+ * store.setSignal(count$, 2)
425
+ * ```
426
+ *
427
+ * @example
428
+ * ```ts
429
+ * const count$ = signal(0, { label: 'count$' })
430
+ * store.setSignal(count$, (prev) => prev + 1)
431
+ * ```
432
+ */
433
+ setSignal = <T>(signalDef: SignalDef<T>, value: T | ((prev: T) => T)): void => {
415
434
  const signalRef = signalDef.make(this.reactivityGraph.context!)
416
- signalRef.value.set(value)
435
+ const newValue: T = typeof value === 'function' ? (value as any)(signalRef.value.get()) : value
436
+ signalRef.value.set(newValue)
417
437
 
418
438
  // The current implementation of signals i.e. the separation into `signal-def` and `signal`
419
439
  // can lead to a situation where a reffed signal is immediately de-reffed when calling `store.setSignal`,
@@ -508,8 +528,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
508
528
  const commitsSpan = otel.trace.getSpan(this.otel.commitsSpanContext)!
509
529
  commitsSpan.addEvent('commit')
510
530
 
511
- // console.group('LiveStore.commit', { skipRefresh, wasSyncMessage, label })
512
- // events.forEach((_) => console.debug(_.name, _.id, _.args))
531
+ // console.group('LiveStore.commit', { skipRefresh })
532
+ // events.forEach((_) => console.debug(_.name, _.args))
513
533
  // console.groupEnd()
514
534
 
515
535
  let durationMs: number
@@ -602,8 +622,11 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
602
622
  *
603
623
  * This is called automatically when the store was created using the React or Effect API.
604
624
  */
605
- shutdown = (cause?: Cause.Cause<UnexpectedError>) =>
606
- this.clientSession.shutdown(cause ?? Cause.fail(IntentionalShutdownCause.make({ reason: 'manual' })))
625
+ shutdown = async (cause?: Cause.Cause<UnexpectedError>) => {
626
+ await this.clientSession
627
+ .shutdown(cause ?? Cause.fail(IntentionalShutdownCause.make({ reason: 'manual' })))
628
+ .pipe(this.runEffectFork, Fiber.join, Effect.runPromise)
629
+ }
607
630
 
608
631
  /**
609
632
  * Helper methods useful during development
@@ -634,6 +657,20 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
634
657
  }).pipe(this.runEffectFork)
635
658
  },
636
659
 
660
+ overrideNetworkStatus: (status: 'online' | 'offline') => {
661
+ const clientId = this.clientSession.clientId
662
+ this.clientSession.leaderThread
663
+ .sendDevtoolsMessage(
664
+ Devtools.Leader.SetSyncLatch.Request.make({
665
+ clientId,
666
+ closeLatch: status === 'offline',
667
+ liveStoreVersion,
668
+ requestId: nanoid(),
669
+ }),
670
+ )
671
+ .pipe(this.runEffectFork)
672
+ },
673
+
637
674
  syncStates: () => {
638
675
  Effect.gen(this, function* () {
639
676
  const session = yield* this.syncProcessor.syncState