Skip to content

Settings and state

A plugin keeps two kinds of value outside ECHO:

  • Settings are choices the person makes, such as a default view. They follow the person to every device.
  • State belongs to one device, such as the last item opened or a panel’s width. It does not sync.

Keep them apart. A device-only value stored as a setting moves to the person’s other devices, where it is wrong.

Describe the settings with a schema, and contribute it with AppCapabilities.Settings. The app renders a form for it on the settings page, using each field’s title, and syncs the values. KvsStore.make gives an atom that holds the values, with defaults for a first run.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import Schema
Schema
from 'effect/Schema';
import * as
import Struct
Struct
from 'effect/Struct';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
import * as
import AppCapabilities
AppCapabilities
from '@dxos/app-toolkit/AppCapabilities';
import * as
import KvsStore
KvsStore
from '@dxos/effect/KvsStore';
import {
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
} from './src/namespace.ts';
export const
const Settings: Schema.Struct<{
readonly openInNewTab: Schema.mutableKey<Schema.optional<Schema.Boolean>>;
}>
Settings
=
import Schema
Schema
.
function Struct<{
readonly openInNewTab: Schema.optional<Schema.Boolean>;
}>(fields: {
readonly openInNewTab: Schema.optional<Schema.Boolean>;
}): Schema.Struct<{
readonly openInNewTab: Schema.optional<Schema.Boolean>;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types. Declared fields may be inherited and are copied to own properties in the output. The __proto__ field is accepted only when it is an own property. Parsing does not guarantee that output keys retain their input order.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@stability ― stable

@category ― constructors

@since ― 3.10.0

Struct
({
openInNewTab: Schema.optional<Schema.Boolean>
openInNewTab
:
import Schema
Schema
.
const optional: optionalLambda
<Schema.Boolean>(self: Schema.Boolean) => Schema.optional<Schema.Boolean>

Type-level representation returned by

optional

.

Marks a struct field as optional, allowing the key to be absent or undefined.

Details

The resulting property may be absent or explicitly set to undefined. Equivalent to optionalKey(UndefinedOr(S)).

Use

optionalKey

instead if you want exact optional semantics (absent only, not undefined).

Example (Defining an optional field accepting undefined)

import { Schema } from "effect"
const schema = Schema.Struct({
name: Schema.String,
age: Schema.optional(Schema.Number)
})
// { readonly name: string; readonly age?: number | undefined }
type Person = typeof schema.Type

@stability ― stable

@category ― models

@since ― 3.10.0

@stability ― stable

@category ― combinators

@since ― 3.10.0

optional
(
import Schema
Schema
.
const Boolean: Schema.Boolean

Type-level representation of

Boolean

.

Schema for boolean values. Validates that the input is typeof "boolean".

When to use

Use to validate values that are already JavaScript booleans.

@stability ― stable

@category ― models

@since ― 4.0.0

@see ― BooleanFromBit for a schema that decodes bit literals 0 or 1 into a boolean

@stability ― stable

@category ― schemas

@since ― 4.0.0

Boolean
.
BottomWithoutNew<boolean, boolean, never, never, Boolean, Boolean, boolean, boolean, readonly [], boolean, "readonly", "required", "no-default", "readonly", "required">.annotate(annotations: Schema.Annotations.Bottom<boolean, readonly []>): Schema.Boolean
annotate
({
Annotations.Augment.title?: string | undefined
title
: 'Open links in a new tab' }),
),
}).
Struct<{ readonly openInNewTab: optional<Boolean>; }>.mapFields<{
readonly openInNewTab: Schema.mutableKey<Schema.optional<Schema.Boolean>>;
}>(f: (fields: {
readonly openInNewTab: Schema.optional<Schema.Boolean>;
}) => {
readonly openInNewTab: Schema.mutableKey<Schema.optional<Schema.Boolean>>;
}, options?: {
readonly unsafePreserveChecks?: boolean | undefined;
} | undefined): Schema.Struct<{
readonly openInNewTab: Schema.mutableKey<Schema.optional<Schema.Boolean>>;
}>

Returns a new struct with the fields modified by the provided function.

Details

Options:

  • unsafePreserveChecks - if true, keep any .check(...) constraints that were attached to the original union. Defaults to false.

    Warning: This is an unsafe operation. Since mapFields transformations change the schema type, the original refinement functions may no longer be valid or safe to apply to the transformed schema. Only use this option if you have verified that your refinements remain correct after the transformation.

mapFields
(
import Struct
Struct
.
const map: <mutableKeyLambda>(lambda: mutableKeyLambda) => <S>(self: S) => { [K in keyof S]: S[K] extends Schema.Constraint ? Schema.mutableKey<S[K]> : never; } (+1 overload)

Applies a

Lambda

transformation to every value in a struct.

When to use

Use when you want to apply the same function to every value in a struct.

Details

The lambda must be created with

lambda

so the compiler can track the output types.

Example (Wrapping every value in an array)

import { pipe, Struct } from "effect"
interface AsArray extends Struct.Lambda {
<A>(self: A): Array<A>
readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
const result = pipe({ width: 10, height: 20 }, Struct.map(asArray))
result // => { width: [10], height: [20] }

@see ― mapPick – apply a lambda only to selected keys

@see ― mapOmit – apply a lambda to all keys except selected ones

@see ― evolve – apply different functions to different keys

@stability ― stable

@category ― mapping

@since ― 4.0.0

map
(
import Schema
Schema
.
const mutableKey: mutableKeyLambda

Type-level representation returned by

mutableKey

.

Makes a struct field mutable (removes the readonly modifier on the property). Use

readonlyKey

to reverse.

@stability ― stable

@category ― models

@since ― 4.0.0

@stability ― stable

@category ― combinators

@since ― 4.0.0

mutableKey
));
// The app renders a form from the schema and syncs the values to the person's
// other devices.
export const
const SettingsModule: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">>, never, Scope>
SettingsModule
=
import Capability
Capability
.
const makeModule: <void, Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">>, never, never>(fn: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">>, never, Scope>) => (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">>, never, Scope>

Helper to define a capability module with explicit typing. Wraps the default export function to provide better type inference and make the pattern explicit.

This helper provides explicit typing for the module activation function, making it clear that the function should:

  • Access declared requires via yield* (or the ambient Capability.Service/Plugin.Service)
  • Return an array of typed

Contribution

s (see

contribute

/

contributeAll

)

@example

// Module without options - single capability
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const client = yield* ClientCapabilities.Client;
return Capability.contribute(Capabilities.SettingsStore, store);
})
);
// Module with multiple capabilities
export default Capability.makeModule(
Effect.fnUntraced(function* () {
return [
Capability.contribute(Capabilities.SettingsStore, store),
Capability.contribute(Capabilities.Translations, translations),
];
})
);
// Module with required options (context accessed via layer)
export default Capability.makeModule(
Effect.fnUntraced(function* (props: { observability: boolean }) {
const invoker = yield* Capabilities.OperationInvoker;
return Capability.contribute(Capabilities.OperationHandler, ...);
})
);
// Module with scoped resources (closed automatically on deactivation)
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const scope = yield* Scope.Scope;
yield* Scope.addFinalizer(scope, Effect.sync(() => cleanup()));
return Capability.contribute(Capabilities.MyCapability, implementation);
})
);

makeModule
(() =>
import Effect
Effect
.
const sync: <Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">>>(thunk: LazyArg<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">>>) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">>, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

When to use

Use when you need to wrap a synchronous side-effectful operation that is not expected to throw.

Details

The provided function is evaluated lazily when the effect runs.

Gotchas

The function must not throw. If it throws, the thrown value is treated as a defect, not as a typed failure. Use try when throwing is expected.

Example (Capturing synchronous logging in an Effect)

import { Effect } from "effect"
const output: Array<unknown> = []
const log = (message: string) =>
Effect.sync(() => {
void output.push(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")
Effect.runSync(program)
output // => ["Hello, World!"]

@see ― try_try for a version that can handle failures.

@stability ― stable

@category ― constructors

@since ― 2.0.0

sync
(() =>
import Capability
Capability
.
const contribute: <Capability.MultiTag<AppCapabilities.Settings, "org.dxos.app-framework.capability.settings">>(capability: Capability.MultiTag<AppCapabilities.Settings, "org.dxos.app-framework.capability.settings">, implementation: AppCapabilities.Settings) => Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.settings", "multi">> (+1 overload)
contribute
(
import AppCapabilities
AppCapabilities
.
const Settings: Capability.MultiTag<AppCapabilities.Settings, "org.dxos.app-framework.capability.settings">

@category ― Capability

Settings
, {
prefix: string
prefix
:
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
.
key: "com.example.plugin.stash"
key
,
schema: Schema.Codec<any, any, never, never>
schema
:
const Settings: Schema.Struct<{
readonly openInNewTab: Schema.mutableKey<Schema.optional<Schema.Boolean>>;
}>
Settings
,
atom: Writable<any, any>
atom
:
import KvsStore
KvsStore
.
make<{
openInNewTab?: boolean | undefined;
}>(options: {
key: string;
schema: Schema.Codec<{
openInNewTab?: boolean | undefined;
}, any, never, never>;
defaultValue: () => {
openInNewTab?: boolean | undefined;
};
runtime?: ReturnType<typeof runtime>;
}): Writable<{
openInNewTab?: boolean | undefined;
}, {
openInNewTab?: boolean | undefined;
}>
export make

Creates a KVS-backed atom for structured settings using Atom.kvs. The entire object is stored as a single localStorage key with JSON serialization.

@param ― options.key - The localStorage key to store the value under.

@param ― options.schema - Effect Schema for the value type.

@param ― options.defaultValue - Function returning the default value.

@param ― options.runtime - Optional custom Atom runtime (defaults to localStorage).

@returns ― A writable atom that persists to localStorage.

@idiom ― org.dxos.effect.kvsStore applies: Persisting a plugin's settings/config as one schema-validated localStorage blob (a global user preference set infrequently) instead-of: hand-rolled localStorage reads/writes, or scattering per-field atoms, or using this for per-context UI state uses: createKvsStorerelated: org.dxos.react-ui-attention.viewState

make
({
key: string
key
:
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
.
key: "com.example.plugin.stash"
key
,
schema: Schema.Codec<{
openInNewTab?: boolean | undefined;
}, any, never, never>
schema
:
const Settings: Schema.Struct<{
readonly openInNewTab: Schema.mutableKey<Schema.optional<Schema.Boolean>>;
}>
Settings
,
defaultValue: () => {
openInNewTab?: boolean | undefined;
}
defaultValue
: () => ({
openInNewTab?: boolean | undefined
openInNewTab
: true }),
}),
}),
),
);

Device state is the plugin’s own capability. Declare a singleton on the plugin’s namespace and store it under the key ${key}.state, so it stays on this device.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import Schema
Schema
from 'effect/Schema';
import * as
import Struct
Struct
from 'effect/Struct';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
import * as
import KvsStore
KvsStore
from '@dxos/effect/KvsStore';
import {
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
} from './src/namespace.ts';
export const
const State: Schema.Struct<{
readonly lastOpened: Schema.mutableKey<Schema.optional<Schema.String>>;
}>
State
=
import Schema
Schema
.
function Struct<{
readonly lastOpened: Schema.optional<Schema.String>;
}>(fields: {
readonly lastOpened: Schema.optional<Schema.String>;
}): Schema.Struct<{
readonly lastOpened: Schema.optional<Schema.String>;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types. Declared fields may be inherited and are copied to own properties in the output. The __proto__ field is accepted only when it is an own property. Parsing does not guarantee that output keys retain their input order.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@stability ― stable

@category ― constructors

@since ― 3.10.0

Struct
({
lastOpened: Schema.optional<Schema.String>
lastOpened
:
import Schema
Schema
.
const optional: optionalLambda
<Schema.String>(self: Schema.String) => Schema.optional<Schema.String>

Type-level representation returned by

optional

.

Marks a struct field as optional, allowing the key to be absent or undefined.

Details

The resulting property may be absent or explicitly set to undefined. Equivalent to optionalKey(UndefinedOr(S)).

Use

optionalKey

instead if you want exact optional semantics (absent only, not undefined).

Example (Defining an optional field accepting undefined)

import { Schema } from "effect"
const schema = Schema.Struct({
name: Schema.String,
age: Schema.optional(Schema.Number)
})
// { readonly name: string; readonly age?: number | undefined }
type Person = typeof schema.Type

@stability ― stable

@category ― models

@since ― 3.10.0

@stability ― stable

@category ― combinators

@since ― 3.10.0

optional
(
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@stability ― stable

@category ― models

@since ― 4.0.0

@stability ― stable

@category ― schemas

@since ― 4.0.0

String
),
}).
Struct<{ readonly lastOpened: optional<String>; }>.mapFields<{
readonly lastOpened: Schema.mutableKey<Schema.optional<Schema.String>>;
}>(f: (fields: {
readonly lastOpened: Schema.optional<Schema.String>;
}) => {
readonly lastOpened: Schema.mutableKey<Schema.optional<Schema.String>>;
}, options?: {
readonly unsafePreserveChecks?: boolean | undefined;
} | undefined): Schema.Struct<{
readonly lastOpened: Schema.mutableKey<Schema.optional<Schema.String>>;
}>

Returns a new struct with the fields modified by the provided function.

Details

Options:

  • unsafePreserveChecks - if true, keep any .check(...) constraints that were attached to the original union. Defaults to false.

    Warning: This is an unsafe operation. Since mapFields transformations change the schema type, the original refinement functions may no longer be valid or safe to apply to the transformed schema. Only use this option if you have verified that your refinements remain correct after the transformation.

mapFields
(
import Struct
Struct
.
const map: <mutableKeyLambda>(lambda: mutableKeyLambda) => <S>(self: S) => { [K in keyof S]: S[K] extends Schema.Constraint ? Schema.mutableKey<S[K]> : never; } (+1 overload)

Applies a

Lambda

transformation to every value in a struct.

When to use

Use when you want to apply the same function to every value in a struct.

Details

The lambda must be created with

lambda

so the compiler can track the output types.

Example (Wrapping every value in an array)

import { pipe, Struct } from "effect"
interface AsArray extends Struct.Lambda {
<A>(self: A): Array<A>
readonly "~lambda.out": Array<this["~lambda.in"]>
}
const asArray = Struct.lambda<AsArray>((a) => [a])
const result = pipe({ width: 10, height: 20 }, Struct.map(asArray))
result // => { width: [10], height: [20] }

@see ― mapPick – apply a lambda only to selected keys

@see ― mapOmit – apply a lambda to all keys except selected ones

@see ― evolve – apply different functions to different keys

@stability ― stable

@category ― mapping

@since ― 4.0.0

map
(
import Schema
Schema
.
const mutableKey: mutableKeyLambda

Type-level representation returned by

mutableKey

.

Makes a struct field mutable (removes the readonly modifier on the property). Use

readonlyKey

to reverse.

@stability ― stable

@category ― models

@since ― 4.0.0

@stability ― stable

@category ― combinators

@since ― 4.0.0

mutableKey
));
// Device state is the plugin's own capability, stored under its own key.
export const
const StateCapability: Capability.Tag<Writable<{}, {}>, "com.example.plugin.stash.capability.state">
StateCapability
=
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
.
makeSingleton: <Writable<{}, {}>>() => <N>(name: N) => Capability.Tag<Writable<{}, {}>, `com.example.plugin.stash.capability.${N}`>

A singleton capability with identifier <key>.capability.<name>.

makeSingleton
<
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any

Obtain the return type of a function type

ReturnType
<typeof
const makeState: () => Writable<{}, {}>
makeState
>>()('state');
const
const makeState: () => Writable<{}, {}>
makeState
= () =>
import KvsStore
KvsStore
.
make<{}>(options: {
key: string;
schema: Schema.Codec<{}, any, never, never>;
defaultValue: () => {};
runtime?: ReturnType<typeof runtime>;
}): Writable<{}, {}>
export make

Creates a KVS-backed atom for structured settings using Atom.kvs. The entire object is stored as a single localStorage key with JSON serialization.

@param ― options.key - The localStorage key to store the value under.

@param ― options.schema - Effect Schema for the value type.

@param ― options.defaultValue - Function returning the default value.

@param ― options.runtime - Optional custom Atom runtime (defaults to localStorage).

@returns ― A writable atom that persists to localStorage.

@idiom ― org.dxos.effect.kvsStore applies: Persisting a plugin's settings/config as one schema-validated localStorage blob (a global user preference set infrequently) instead-of: hand-rolled localStorage reads/writes, or scattering per-field atoms, or using this for per-context UI state uses: createKvsStorerelated: org.dxos.react-ui-attention.viewState

make
({
key: string
key
: `${
const StashNamespace: {
key: "com.example.plugin.stash";
make: <T>() => <const N extends string>(name: N) => Capability.MultiTag<T, `com.example.plugin.stash.capability.${N}`>;
makeSingleton: <T>() => <const N extends string>(name: N) => Capability.Tag<T, `com.example.plugin.stash.capability.${N}`>;
}
StashNamespace
.
key: "com.example.plugin.stash"
key
}.state`,
schema: Schema.Codec<{}, any, never, never>
schema
:
const State: Schema.Struct<{
readonly lastOpened: Schema.mutableKey<Schema.optional<Schema.String>>;
}>
State
,
defaultValue: () => {}
defaultValue
: () => ({}),
});
export const
const StateModule: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">>, never, Scope>
StateModule
=
import Capability
Capability
.
const makeModule: <void, Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">>, never, never>(fn: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">>, never, Scope>) => (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">>, never, Scope>

Helper to define a capability module with explicit typing. Wraps the default export function to provide better type inference and make the pattern explicit.

This helper provides explicit typing for the module activation function, making it clear that the function should:

  • Access declared requires via yield* (or the ambient Capability.Service/Plugin.Service)
  • Return an array of typed

Contribution

s (see

contribute

/

contributeAll

)

@example

// Module without options - single capability
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const client = yield* ClientCapabilities.Client;
return Capability.contribute(Capabilities.SettingsStore, store);
})
);
// Module with multiple capabilities
export default Capability.makeModule(
Effect.fnUntraced(function* () {
return [
Capability.contribute(Capabilities.SettingsStore, store),
Capability.contribute(Capabilities.Translations, translations),
];
})
);
// Module with required options (context accessed via layer)
export default Capability.makeModule(
Effect.fnUntraced(function* (props: { observability: boolean }) {
const invoker = yield* Capabilities.OperationInvoker;
return Capability.contribute(Capabilities.OperationHandler, ...);
})
);
// Module with scoped resources (closed automatically on deactivation)
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const scope = yield* Scope.Scope;
yield* Scope.addFinalizer(scope, Effect.sync(() => cleanup()));
return Capability.contribute(Capabilities.MyCapability, implementation);
})
);

makeModule
(() =>
import Effect
Effect
.
const sync: <Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">>>(thunk: LazyArg<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">>>) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">>, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

When to use

Use when you need to wrap a synchronous side-effectful operation that is not expected to throw.

Details

The provided function is evaluated lazily when the effect runs.

Gotchas

The function must not throw. If it throws, the thrown value is treated as a defect, not as a typed failure. Use try when throwing is expected.

Example (Capturing synchronous logging in an Effect)

import { Effect } from "effect"
const output: Array<unknown> = []
const log = (message: string) =>
Effect.sync(() => {
void output.push(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")
Effect.runSync(program)
output // => ["Hello, World!"]

@see ― try_try for a version that can handle failures.

@stability ― stable

@category ― constructors

@since ― 2.0.0

sync
(() =>
import Capability
Capability
.
const contribute: <Capability.Tag<Writable<{}, {}>, "com.example.plugin.stash.capability.state">>(capability: Capability.Tag<Writable<{}, {}>, "com.example.plugin.stash.capability.state">, implementation: Writable<{}, {}>) => Capability.Contribution<Capability.CapabilityIdentifier<"com.example.plugin.stash.capability.state", "single">> (+1 overload)
contribute
(
const StateCapability: Capability.Tag<Writable<{}, {}>, "com.example.plugin.stash.capability.state">
StateCapability
,
const makeState: () => Writable<{}, {}>
makeState
())),
);

Both are atoms. A component reads one with useAtomValue and writes it with useAtomSet, after getting it with useCapability. Other modules read them the same way they read any capability.

  • Settings are not ECHO objects, so they do not appear in a space or in search.
  • Change a settings schema only by adding optional fields, so values stored by an earlier version still decode.