@cyftec/signal
v0.3.9
Published
Signal implementation in TypeScript
Maintainers
Readme
@cyftec/signal
@cyftec/signal is a small reactive state library for TypeScript. It provides mutable source signals, read-only derived signals, synchronous effects, snapshot-style dead signals, and data-specific helpers attached directly to signals.
The current package version is 0.2.4. This implementation has its own semantics; do not infer its behavior from another signal library.
Install
bun add @cyftec/signalQuick start
import { derive, effect, signal } from "@cyftec/signal";
const count = signal(1);
const doubled = derive(() => count.value * 2);
const logger = effect(() => {
console.log({ count: count.value, doubled: doubled.value });
});
count.value = 2; // the effect runs synchronously
logger.dispose(); // unsubscribes immediately
doubled.dispose();An effect runs once when it is created. Signal reads made during that initial run establish its dependencies. Later writes propagate synchronously; there is no batching or scheduler.
Data-specific methods
Source-signal mutations live under .mutate. Read-only methods return signals instead of plain values.
const items = signal([1, 2, 3]);
const last = items.lastItem();
items.mutate.push(4);
console.log(last.value); // 4
const user = signal({ name: "Ada", active: false });
const name = user.get("name");
user.mutate.set({ name: "Grace" });
console.log(name.value); // "Grace"
const enabled = signal(false);
enabled.mutate.toggle();Arrays, objects, strings, numbers, and booleans receive methods appropriate to their initial runtime value. For a nullable signal, pass a non-null exemplar as the second argument so the method family can be attached:
const names = signal<string[] | undefined>(undefined, []);
names.value = ["Ada", "Grace"];Object and array initial values and .value reads are copied, but assigned values are retained by reference. After assigning an object or array, do not mutate that original value; use another assignment or .mutate so subscribers are notified. Treat the low-level prevValue and nonReactiveValue views as read-only because they can expose stored references.
Generic logical methods
Primitive, string, and array signals expose fluent logical helpers:
const count = signal(3);
const positive = count.is.greaterThan(0);
const label = count.if.greaterThan(0).then("positive", "not positive");
const fallback = signal<string | undefined>(undefined, "").or("anonymous");Live inputs return reactive DerivedSignal results. deadSignal(...) inputs return DeadSignal snapshots.
Main exports
signal,derive,effect,disposedeadSignalfor read-only non-live values with the same projection helperscompute,tmpl,receive, andtransmitpromstatesfor promise result, error, and running statenullablefor adding generic logical methods to possibly-null primitive inputsopfor the older chainable operation APIvalueand thevalueIs...runtime type guards
See the source-backed contracts in docs-architecture/semantics.md, the API inventory in docs-architecture/behavior.md, the signal-widening contract in docs-architecture/type-variance.md, and the contributor model in docs-architecture/overview.md.
Development
bun install
bun run testUseful commands:
bun run test:runtime— run behavioral testsbun run test:types— run TypeScript type checksbun run test:coverage— collect runtime coveragebun run build:meta— rebuild source-comment metadata for the websitebun run build:validate— validate generated metadatabun run docs— rebuild metadata and publish the static website with Brahmabun run setup:hooks— configure the repository pre-commit hook
Generated website output lives under docs/. Edit source comments or files under website/dev/, then regenerate; do not hand-edit the generated output.
