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

@thingmate/entity

v0.12.1

Published

Universal API around property/action/event

Readme

npm (scoped) npm NPM npm type definitions

@thingmate/entity

Universal API around property/action/event.

📦 Installation

yarn add @thingmate/entity
# or
npm install @thingmate/entity --save

📜 Documentation

Entity

interface Entity {
  readonly properties: EntityPropertyMap;
  readonly actions: EntityActionMap;
  readonly events: EntityEventMap;
}

An Entity is a collection of properties, actions and events: whose goal is to create a universal representation of a thing.

Example: define a smart light bulb

interface SmartLightBulb extends Entity {
  readonly properties: {
    readonly onoff: EntityProperty<boolean>;
    readonly color: EntityProperty<{ r: number, g: number, b: number }>;
  };
  readonly actions: {
    readonly toggle: EntityAction<[], void>;
  };
  readonly events: {
    readonly overheat: EntityEvent<void>;
  };
}

Every async API may be expressed using this universal API.

EntityProperty

interface EntityProperty<GValue> {
  readonly get: EntityPropertyGet<GValue>;
  readonly set: EntityPropertySet<GValue>;
  readonly observer: EntityPropertyObserver<GValue>;
}

This represents a property of an entity: a value that can be read, written and/or observed.

EntityPropertyGet

interface EntityPropertyGet<GValue> {
  (options?: Abortable): Promise<GValue>;
}

Fetches and returns the current value of a property.

Example: read the current color of a light bulb

console.log('color', await lightBulb.properties.color.get());

EntityPropertySet

interface EntityPropertySet<GValue> {
  (value: GValue, options?: Abortable): Promise<void>;
}

Sets the current value of a property.

Example: set the color of a light bulb to red

await lightBulb.properties.color.set({ r: 255, g: 0, b: 0 });

EntityPropertyObserver

type EntityPropertyObserver<GValue> = ReadableFlow<GValue, [options?: PushToPullOptions]>;

A readable flow that observes the value of a property.

Example: observe the color of a light bulb

const controller = new AbortController();

for await (const color of lightBulb.properties.color.observer.open(controller.signal)) {
  console.log('color', color);
}

EntityAction

interface EntityAction<
  GArguments extends readonly any[],
  GReturn,
  GAbortable extends Abortable = Abortable,
> {
  (...args: [...GArguments, options?: GAbortable]): Promise<GReturn>;
}

This represents an action of an entity: something that can be invoked to perform some action and produce a result.

Example: toggle on/off a light bulb when clicking on a button

document.getElementById('toggle-button').addEventListener('click', async () => {
  await lightBulb.actions.toggle();
});

EntityEvent

type EntityEvent<GValue> = ReadableFlow<GValue, [options?: PushToPullOptions]>

This represents an event emitted by an entity: something that may happen any time and can be observed.

Example: toggle off a light bulb when it overheats

const controller = new AbortController();

for await (const _ of lightBulb.events.overheat.open(controller.signal)) {
  await lightBulb.properties.onoff.set(false);
}

Helpers

DEFAULT_ENTITY_PROPERTY

declare const DEFAULT_ENTITY_PROPERTY: EntityProperty<any>;

A default implementation of an EntityProperty that can be used to implement only a subset of the API.

NOTE: all members of an EntityProperty are always defined, but, by default, calling these members will throw a "Not implemented" error.

Example: implement only the get method of a property

const lightBulb: SmartLightBulb = {
  properties: {
    onoff: {
      ...DEFAULT_ENTITY_PROPERTY,
      get: (options?: Abortable) => fetch('http://localhost:3000/onoff', options).then(res => res.json()),
    },
    // ...
  },
  // ...
};

await lightBulb.properties.onoff.get(); // => true/false
await lightBulb.properties.onoff.set(false); // => throws an error, because `set` is not implemented

mapEntityProperty

declare function mapEntityProperty<GIn, GOut>(
  property: EntityProperty<GIn>,
  { inOut, outIn }: BidirectionalMapFunctions<GIn, GOut>,
): EntityProperty<GOut>;

Transforms the input and output values of an EntityProperty.

Example: converts a temperature from celcius to fahrenheit

const temperatureSensor = {
  properties: {
    // converts the temperature from celcius to fahrenheit
    temperature: mapEntityProperty(
      temperatureProperty, // assumes this is a property of a temperature sensor in celcius
      {
        inOut: (value: number) => value * 9 / 5 + 32,
        outIn: (value: number) => (value - 32) * 5 / 9,
      },
    ),
  },
  // ...
};

await temperatureSensor.properties.temperature.get(); // => 32°F if the sensor reported 0°C