Offline support
- LiveStore supports offline data management out of the box. In order to make your app work fully offline, you might need to also consider the following:
- Design your app in a way to treat the network as an optional feature (e.g. when relying on other APIs / external data)
- Use service workers to cache assets locally (e.g. images, videos, etc.)
Tracking connectivity
Section titled “Tracking connectivity”Use store.networkStatus to react to connectivity transitions. The subscribable emits every time the sync backend connection flips or the devtools latch simulates an offline state.
const const status: Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">
status = await const store: Store<LiveStoreSchema.Any, {}>
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.networkStatus: Subscribable<Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">, never, never>
Reactive connectivity updates emitted by the backing sync backend.
networkStatus.Pipeable.pipe<Subscribable<Struct<Fields extends Struct.Fields>.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">, never, never>, Promise<Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">>>(this: Subscribable<...>, ab: (_: Subscribable<Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">, never, never>) => Promise<...>): Promise<...> (+21 overloads)
pipe(import Effect
Effect.const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>
Executes an effect and returns the result as a Promise.
When to use
Use when you need to execute an effect and work with the
result using Promise syntax, typically for compatibility with other
promise-based code.
If the effect succeeds, the promise will resolve with the result. If the
effect fails, the promise will reject with an error.
Example (Running a successful effect as a Promise)
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)// Output: 1
Example (Running effects as promises)
//Example: Handling a Failing Effect as a Rejected Promiseimport { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)// Output:// (FiberFailure) Error: my error
runPromise)if (const status: Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">
status.isConnected: boolean
True when the upstream sync backend is reachable and responding to health checks.
isConnected === false) { var console: Console
console.Console.warn(...data: any[]): void (+2 overloads)
The console.warn() static method outputs a warning message to the console at the 'warning' log level.
warn('Sync backend offline since', new var Date: DateConstructornew (value: number | string | Date) => Date (+3 overloads)
Date(const status: Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">
status.timestampMs: number
Unix epoch timestamp (ms) of the latest connectivity state transition.
timestampMs))}
await const store: Store<LiveStoreSchema.Any, {}>
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.networkStatus: Subscribable<Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">, never, never>
Reactive connectivity updates emitted by the backing sync backend.
networkStatus.Subscribable<Struct<Fields extends Struct.Fields>.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>; }, "Type">, never, never>.changes: Stream.Stream<Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">, never, never>
changes.Pipeable.pipe<Stream.Stream<Struct<Fields extends Struct.Fields>.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">, never, never>, Stream.Stream<Struct.ReadonlySide<{ readonly isConnected: Boolean; readonly timestampMs: Finite; readonly devtools: Struct<{ readonly latchClosed: Boolean; }>;}, "Type">, never, never>, Effect.Effect<void, never, never>, Effect.Effect<...>, Promise<...>>(this: Stream.Stream<...>, ab: (_: Stream.Stream<...>) => Stream.Stream<...>, bc: (_: Stream.Stream<...>) => Effect.Effect<...>, cd: (_: Effect.Effect<...>) => Effect.Effect<...>, de: (_: Effect.Effect<...>) => Promise<...>): Promise<...> (+21 overloads)
pipe( import Stream
Stream.const runDrain: <A, E, R>(self: Stream.Stream<A, E, R>) => Effect.Effect<void, E, R>
Runs the stream for its effects, discarding emitted elements.
Example (Draining a stream run)
import { Console, Effect, Stream } from "effect"
const program = Effect.gen(function*() { const stream = Stream.make(1, 2, 3).pipe( Stream.mapEffect((n) => Console.log(`Processing: ${n}`)) )
yield* Stream.runDrain(stream)})
Effect.runPromise(program)// Processing: 1// Processing: 2// Processing: 3
runDrain, import Effect
Effect.const scoped: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Scope>>
Runs an effect with a scope that closes when the effect completes.
When to use
Use to acquire scoped resources for the duration of a single workflow.
Details
Finalizers for resources acquired inside the workflow run as soon as the
workflow completes, whether by success, failure, or interruption.
Example (Running a scoped acquisition)
import { Console, Effect } from "effect"
const resource = Effect.acquireRelease( Console.log("Acquiring resource").pipe(Effect.as("resource")), () => Console.log("Releasing resource"))
const program = Effect.scoped( Effect.gen(function*() { const res = yield* resource yield* Console.log(`Using ${res}`) return res }))
Effect.runFork(program)// Output: "Acquiring resource"// Output: "Using resource"// Output: "Releasing resource"
scoped, import Effect
Effect.const runPromise: <A, E>(effect: Effect.Effect<A, E>, options?: Effect.RunOptions | undefined) => Promise<A>
Executes an effect and returns the result as a Promise.
When to use
Use when you need to execute an effect and work with the
result using Promise syntax, typically for compatibility with other
promise-based code.
If the effect succeeds, the promise will resolve with the result. If the
effect fails, the promise will reject with an error.
Example (Running a successful effect as a Promise)
import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)// Output: 1
Example (Running effects as promises)
//Example: Handling a Failing Effect as a Rejected Promiseimport { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)// Output:// (FiberFailure) Error: my error
runPromise,)
When devtools close the sync latch to simulate an offline client, status.devtools.latchClosed is true, allowing you to differentiate between real and simulated outages. Remember to dispose of long-lived subscriptions using the Effect scope you already manage for your runtime.