Skip to main content
An ORSet (Observed-Remove Set) allows concurrent adds and removes without conflicts. Each add generates a unique tag; a remove only removes the specific tags it observed — so a concurrent add of the same element wins.

Usage without schema

const tags = client.orset("or:article-123-tags");

tags.add("typescript");
tags.remove("javascript");

console.log(tags.elements()); // string[]

tags.onChange(elements => console.log("tags:", elements));

Usage with schema

import { Schema } from "effect";

const Item = Schema.Struct({ id: Schema.String, name: Schema.String });
const cart = client.orset("or:cart-user-42", Item);

cart.add({ id: "prod-1", name: "Keyboard" });

cart.onChange(items => {
  // items: { id: string; name: string }[]
  for (const item of items) {
    console.log(item.name);
  }
});
Incoming deltas are validated against the schema at runtime via Schema.decodeUnknownSync. Invalid entries are discarded.

Element depth limit

ORSet elements must be scalars or single-level objects/arrays (depth ≤ 1). Nested structures are rejected by the server:
// ✅ allowed
tags.add("typescript");
tags.add(42);
tags.add({ id: "prod-1", name: "Keyboard" }); // flat object

// ❌ rejected — nested object, depth > 1
cart.add({ id: "prod-1", meta: { color: "red" } });
Rejected ops return a 400 error with the message ORSet element nesting depth X exceeds maximum 1. This limit is enforced at the server level and cannot be bypassed.

API

MethodDescription
add(value: T)Add an element
remove(value: T)Remove all observed instances of an element
has(value: T)Returns true if the element is currently in the set
elements()Returns current elements as T[]
onChange(fn)Subscribe — returns unsubscribe function
stream()Returns an Effect Stream<T[]> that emits on every change