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

@latkit/model

v0.6.0

Published

Immutable columnar network model, its series, and its byte form for Latkit.

Readme

@latkit/model

The immutable, columnar description of a network and its element classes that a vendor produces once and every latkit renderer and view consumes directly, plus the byte form that lets it cross a process boundary lazily.

Seven nouns:

| Noun | What it is | | --------- | ----------------------------------------------------------------------------------------- | | Model | A value: topology, owners, element classes, and one lazy, cached loader for class columns | | Field | One quantity of a class a host binds or plots: a numeric column or a recorded signal | | Series | Append-only samples over one element axis and time, read in bounded windows | | A run | What any engine emits: RunUpdates whose frames collect into a Series | | Results | What a run leaves behind: its recorded samples, read back class by class as those batches | | Source | The same model as bytes: one core plus one shard per class, owned by whoever asks | | Netlist | A block diagram's structure: blocks, the ports each owns, and the nets that join ports |

Topology and Item are field-for-field the shapes @latkit/network loads and picks, so a model never adapts for a renderer. Domain is the [min, max] every renderer takes; extent scans one, validateDomain checks one, and validateTopology checks a topology, all before a device exists. Netlist is likewise the shape @latkit/diagram loads, and validateNetlist checks one. The package has no dependencies, I/O, or rendering.

Produce a model

A vendor builds a model with createModel; there is no interface to implement. Owner classes are the ones whose element i is vertex i or edge i, and they declare nothing more. Any other class may anchor each element to a topology item, 0xffffffff marking an element with no place. Columns are scalar: number, text, or flag.

import { createModel, type ClassData } from '@latkit/model';

const model = createModel(
  {
    vendor: 'gridkit',
    id: caseId,
    name: 'IEEE 14',
    meta: { freqBase: 60 },
    topology,
    owners: { vertex: 'bus', edge: 'branch' },
    classes: [
      { id: 'bus', label: 'Bus', count: 14, signals: BUS_SIGNALS },
      {
        id: 'gen',
        label: 'Generator',
        count: 5,
        anchor: { kind: 'vertex', index: genBus },
        signals: GEN_SIGNALS,
      },
    ],
  },
  {
    load: async (classId): Promise<ClassData> => columnsFor(classId),
    bytes: async () => caseBytes,
  },
);

Consume a model

import { createGrid, elementAt, extent, itemOf } from '@latkit/model';

network.load(model.topology);

const bus = await model.load('bus');
const vm = bus.columns.find((column) => column.id === 'Vm');
if (vm?.kind === 'number') {
  const values = Float32Array.from(vm.values);
  network.setChannel('vertexColor', values, extent(values));
}

network.on('select', (item) => {
  const ref = item && elementAt(model, item);
  if (ref) console.log(bus.labels[ref.index]);
});

const grid = createGrid(bus.labels, bus.columns);
const { rows, total } = await grid.window('north', { column: 'Vm', dir: 'desc' }, 0, 50);

Describe a diagram

A Netlist is a block diagram's structure as columns: blocks, the ports each block owns (portStart offsets), and the nets that join ports (netStart offsets into netPorts). A net has at most one out port, its driver, and a port joins at most one net. Placement is not structure; blockKey keeps each block's position, placement, and selection across reloads.

import { validateNetlist, type Netlist } from '@latkit/model';

// TGOV1 drives pmech, IEEET1 drives efd, GENROU's speed feeds both back.
const unit: Netlist = {
  blockCount: 3,
  blockKey: ['Genrou/1_1_genrou', 'Tgov1/1_1_tgov1', 'Ieeet1/1_1_ieeet1'],
  blockTitle: ['GENROU', 'TGOV1', 'IEEET1'],
  portStart: Uint32Array.of(0, 3, 5, 7),
  portFlow: Uint8Array.of(0, 0, 1, /* tgov1 */ 0, 1, /* ieeet1 */ 0, 1),
  portLabel: ['pmech', 'efd', 'speed', 'speed', 'pmech', 'speed', 'efd'],
  netStart: Uint32Array.of(0, 2, 4, 7),
  netPorts: Uint32Array.of(4, 0, /* efd */ 6, 1, /* speed */ 2, 3, 5),
  netLabel: ['1_1_pmech', '1_1_efd', '1_1_speed'],
};
validateNetlist(unit);

Name what a host shows

A Field is one quantity of a class: a numeric column, or a signal the class records. A binding picker, a plot lane, and an inspector row all speak in fields, never in columns or arrays. fieldsOf lists a class's fields and fieldKey keys a reference to one.

import { fieldKey, fieldsOf } from '@latkit/model';

const fields = fieldsOf(spec, await model.load(spec.id), results);
const bound = new Map(fields.map((field) => [fieldKey(field), field]));

Run a model

A Runner emits RunUpdates. Every frame batch names its resultId and classId; keep those pairs separate when collecting histories.

import { collect, sample, type RunFrames } from '@latkit/model';

const frames: RunFrames[] = [];
for await (const update of runner.run(command, signal)) {
  if (update.type === 'frames' && update.resultId === resultId && update.classId === 'bus')
    frames.push(update);
}
const series = collect(frames);
monitor.load(series, 0);

const head = series.state.frameCount;
if (head) {
  const [, end] = await series.locate([t, t], head, signal);
  const values = await sample(series, 0, Math.max(0, end - 1), signal);
  console.log(values);
}

Read and retain samples

createSeries({ elementCount, signalCount }) creates an empty history with an append(batch) method. Optional initial time and values use signal-major order; appended RunFrames use frame-major order. Float64 time and float32 or float64 values are borrowed, never mutated or detached after publication. Reads within a retained chunk are strided views; reads across chunks pack only the requested window.

Series.state publishes committed frameCount, timeRange, and per-signal ranges together. Previous states remain unchanged. A null range array means the extents are unknown; a NaN pair means that signal has no finite samples. Optional sorted elements maps stored columns to class indices. on('append', listener) returns an unsubscribe function.

A disk store or remote recording implements the same Results interface:

import type { Results } from '@latkit/model';

const results: Results = {
  id: resultId,
  series: (classId, signal) => store.series(classId, signal),
  read: (classId, signals, signal) => store.batches(classId, signals, signal),
};
const series = await results.series('bus');
const block = await series.read(
  0,
  {
    frameOffset: 10,
    frameCount: 20,
    elementOffset: 0,
    elementCount: Math.min(4, series.elementCount),
  },
  signal,
);
const first = block.values[0];
const next = block.values[block.stride];

Only request committed frames. Returned arrays are borrowed and immutable. A transport copies them before transfer. locate([from, to], frameCount) returns the half-open frame interval containing every timestamp in that inclusive range, including duplicates, within the captured head. Results.read emits batches with signals in requested order; null selects all signals. collect accepts arrays or async iterables and retains their batches without a full transpose.

Move a model

import { openModel, sourceOf } from '@latkit/model';

// pack, for example when staging a library at build time
const source = sourceOf(model);
await write('core.bin', await source.core());
for (const cls of model.classes) await write(`${cls.id}.bin`, await source.class(cls.id));

// unpack, classes still lazy
const opened = await openModel(
  { core: fetchCore, class: fetchShard, bytes: fetchCase },
  { signal, progress: (loaded, total) => bar.set(loaded / total) },
);

The pack format is versioned and private: a small JSON directory followed by 8-byte-aligned typed sections, so unpacking is a set of typed-array views into the received buffer.