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

propertea

v1.0.2

Published

![CI](https://github.com/cha0s/propertea/actions/workflows/ci.yml/badge.svg)

Readme

CI

Propertea :tea:

A high-performance low-level state management system for games :video_game:. Contains supercharged proxies.

:fire: Features

  • Change/dirty tracking
  • Creating and applying diffs
  • Full (de)serialization to/from JSON
  • Fixed-size contiguous binary representation for efficient state transformations using TypedArray or even WASM.
  • Object pooling

Examples :mag:

Pool (fixed-size)

Given a blueprint schema for a 2D position with x and y coordinates, let's create a pool of positions and allocate a couple:

import {Pool} from 'propertea';
// create
const positionPool = new Pool({
  type: 'object',
  properties: {
    x: {type: 'float32'},
    y: {type: 'float32'},
  },
});
// allocated with defaults
const first = positionPool.allocate();
assert(first.x === 0);
assert(first.y === 0);
// allocated with explicit value
const second = positionPool.allocate({x: 100, y: 200});
assert(second.x === 100);
assert(second.y === 200);

The state is mapped to contiguous memory:

const view = new Float32Array(positionPool.data.memory.buffer);
// equivalent!
assert(view[0] === first.x);
assert(view[1] === first.y);
assert(view[2] === second.x);
assert(view[3] === second.y);

State synchronization is bidirectional; updating the proxy updates the buffer and vice-versa:

// proxy -> buffer
second.x = 123;
assert(view[2] === 123);
// buffer -> proxy
view[3] = 234;
assert(second.y === 234);

Pool (dynamic-sized)

Not all types are fixed-size. A familiar example would be a string.

Dynamic-sized types aren't mapped to contiguous memory. Their data lives in JS properties.

// create
const userPool = new Pool({
  type: 'object',
  properties: {
    age: {type: 'uint32'},
    name: {type: 'string'},
  },
});
const user = userPool.allocate();
assert(user.age === 0);
assert(user.name === '');
// same structure, null buffer
assert(userPool.data.memory.buffer.byteLength === 0);

Notice that even though age is a uint32 (a fixed-size type), the type becomes dynamic-sized if any type contained within is dynamic-sized.

Change tracking

By default, changes are tracked. Let's continue with our position pool from above:

import {Diff} from 'propertea';

const another = positionPool.allocate();
console.log(another[Diff]());
// {x: 0, y: 0}

Why is there already a diff? Proxies are created dirty. Think about a world with monsters. If a new monster spawns, all clients should receive an update with the new monster as a diff. Make sense?

A proxy may be marked clean:

import {MarkClean} from 'propertea';
another[MarkClean]();
assert(another[Diff]() === undefined);

Reactivity

Changes may trigger a callback:

const blueprint = {
  type: 'object',
  properties: {
    foo: {type: 'string'},
  },
};
const params = {
  onDirty: (
    // the dirty bit
    bit,
    // the proxy triggering this change
    proxy,
  ) => {
    // ... do something!
    console.log('bit:', bit, 'proxy:', proxy);
  }
};
const reactivePool = new Pool(blueprint, params);
reactivePool.allocate();
// bit: 0 proxy: ConcreteProxy {
//   [Symbol(element)]: { foo: '' },
//   [Symbol(DataOffset)]: 0,
//   [Symbol(DirtyOffset)]: 0
// }

WASM

Pools are structured to be operated on by WASM. See src/pool.test.wat for a minimal example of using WASM to transform data (and track changes).

Excerpted from src/pool.test.js:

const pool = new Pool({
  type: 'object',
  properties: {
    z: {
      type: 'float32',
    },
  },
});
// generate random samples and use them to initialize our pool
const samples = [];
for (let i = 0; i < 10; ++i) {
  const sample = Math.random();
  samples.push(sample);
  pool.allocate({z: sample});
}
pool.markClean();
// compile the WAT to WASM and get the exports
const {default: buffer} = await import('./pool.test.wat?multi_memory');
const exports = await WebAssembly.instantiate(buffer, {pool: pool.imports()})
  .then(({instance: {exports}}) => exports);
// generate a random sample to pass to the WASM
const parameter = Math.random();
exports.thisIsAWasmTest(parameter);

NOTE: Reactive callbacks are not invoked by WASM.

Serialization

The proxy can be serialized to JSON:

import {ToJSON} from 'propertea';
console.log(another[ToJSON]());
// {x: 0, y: 0}

Motivation

Networked real-time applications with arbitrarily-large mutable state (read: games :video_game:) need to efficiently synchronize state from server to client(s). This generally involves tracking state changes and sending only the delta each update interval ("diffing").

Performance

Code generation (new Function) is used to generate a monomorphic proxy shape from a blueprint. This keeps the inline cache hot and performant.

It is greatly beneficial for performance when data is arranged contiguously so that e.g. SIMD may be leveraged for data transformations.

This library is fast. As you can see in src/pool.bench.js, Propertea beats native JavaScript by 100-1000x transforming contiguous data. Pooled allocations actually beat native after warming the pool.

Onward and upward

Specifically, this is motivated by my pure JavaScript ECS which is the next to be released.

TODO

  • Fixed-length arrays
  • Fixed-shape maps (depends on crunches codec support)
  • More array proxy ops