npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kyneta/changefeed

v1.7.0

Published

Universal reactive contract — a Moore machine identified by [CHANGEFEED]

Downloads

3,870

Readme

@kyneta/changefeed

The universal reactive contract for Kyneta — a Moore machine identified by [CHANGEFEED].

Overview

A changefeed is a reactive value with a current state and a stream of future changes. You read .current to see what's there now; you .subscribe() to learn what changes next.

The protocol is expressed through a single well-known symbol: CHANGEFEED (Symbol.for("kyneta:changefeed")). Any object carrying this symbol participates in the reactive protocol — schema-interpreted refs, local state, peer lifecycle feeds, or anything else.

This package contains the contract only — zero dependencies, no schema, no interpreters, no paths. Schema-specific extensions (Op, TreeChangefeedProtocol, tree observation) live in @kyneta/schema, which depends on this package.

Install

pnpm add @kyneta/changefeed

API

Types

// The universal base type for all changes — an open protocol identified by a string discriminant.
interface ChangeBase {
  readonly type: string
}

// A batch of changes with optional provenance.
interface Changeset<C = ChangeBase> {
  readonly changes: readonly C[]
  readonly origin?: string
}

// The protocol object behind [CHANGEFEED] — a Moore machine coalgebra.
interface ChangefeedProtocol<S, C extends ChangeBase = ChangeBase> {
  readonly current: S
  subscribe(callback: (changeset: Changeset<C>) => void): () => void
}

// Developer-facing type: [CHANGEFEED] marker + direct .current and .subscribe().
interface Changefeed<S, C extends ChangeBase = ChangeBase> {
  readonly [CHANGEFEED]: ChangefeedProtocol<S, C>
  readonly current: S
  subscribe(callback: (changeset: Changeset<C>) => void): () => void
}

// Marker interface — any object with [CHANGEFEED] participates in the protocol.
interface HasChangefeed<S = unknown, A extends ChangeBase = ChangeBase> {
  readonly [CHANGEFEED]: ChangefeedProtocol<S, A>
}

// A Changefeed that is also callable — feed() returns feed.current.
type CallableChangefeed<S, C extends ChangeBase = ChangeBase> =
  Changefeed<S, C> & (() => S)

Functions

createChangefeed<S, C>(getCurrent: () => S): [Changefeed<S, C>, emit]

Create a standalone changefeed with push semantics. Returns a [feed, emit] tuple.

import { createChangefeed } from "@kyneta/changefeed"

let count = 0
const [feed, emit] = createChangefeed(() => count)

feed.current              // 0
feed.subscribe(cs => console.log(cs.changes))

count = 1
emit({ changes: [{ type: "increment", amount: 1 }] })
// subscriber receives the changeset

createCallable<S, C>(feed: Changefeed<S, C>): CallableChangefeed<S, C>

Wrap a changefeed in a callable function-object. feed() returns feed.current.

import { createChangefeed, createCallable } from "@kyneta/changefeed"

let count = 0
const [source, emit] = createChangefeed(() => count)
const feed = createCallable(source)

feed()          // 0 — callable
feed.current    // 0 — getter
feed.subscribe  // subscribe to changes

changefeed<S, C>(source: HasChangefeed<S, C>): Changefeed<S, C>

Project any object with [CHANGEFEED] into a developer-facing Changefeed — lifting the hidden protocol surface to direct .current and .subscribe() accessibility.

import { changefeed } from "@kyneta/changefeed"

const feed = changefeed(doc.title)
feed.current          // live value
feed.subscribe(cb)    // subscribe to changes

hasChangefeed(value: unknown): value is HasChangefeed

Type guard — returns true if value has a [CHANGEFEED] property.

staticChangefeed<S>(head: S): ChangefeedProtocol<S, never>

Creates a protocol object that never emits changes — useful for static data sources that still need to participate in the protocol.

Relationship to @kyneta/schema

@kyneta/schema depends on @kyneta/changefeed and extends the contract with tree-structured observation:

| This package (@kyneta/changefeed) | @kyneta/schema | |---|---| | ChangeBase | TextChange, MapChange, SequenceChange, ... | | Changeset<C> | Op<C> (addressed delta with Path) | | ChangefeedProtocol<S, C> | TreeChangefeedProtocol<S, C> (adds subscribeTree) | | Changefeed<S, C> | HasTreeChangefeed<S, C> | | hasChangefeed() | hasTreeChangefeed(), getOrCreateChangefeed() | | createChangefeed(), createCallable() | expandMapOpsToLeaves() |

Consumers import the contract from @kyneta/changefeed directly — schema does not re-export contract symbols. The import path tells the truth about the dependency.

Relationship to @kyneta/cast

The Kyneta compiler detects [CHANGEFEED] structurally on types for automatic reactive subscription. HasChangefeed<S, C> is the type-level marker the compiler looks for. The Cast runtime uses hasChangefeed() at runtime to discover reactive values and subscribe to their change streams.

License

MIT