Skip to content

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.)

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.

@example

import { Effect, Stream } from 'effect'
const status = await store.networkStatus.pipe(Effect.runPromise)
await store.networkStatus.changes.pipe(
Stream.tap((next) => console.log('network status update', next)),
Stream.runDrain,
Effect.scoped,
Effect.runPromise,
)

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 Promise
import { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)
// Output:
// (FiberFailure) Error: my error

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@since2.0.0

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.

MDN Reference

warn
('Sync backend offline since', new
var Date: DateConstructor
new (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.

@example

import { Effect, Stream } from 'effect'
const status = await store.networkStatus.pipe(Effect.runPromise)
await store.networkStatus.changes.pipe(
Stream.tap((next) => console.log('network status update', next)),
Stream.runDrain,
Effect.scoped,
Effect.runPromise,
)

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

@since2.0.0

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"

@since2.0.0

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 Promise
import { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)
// Output:
// (FiberFailure) Error: my error

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@since2.0.0

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.