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

system-definition

v0.1.1

Published

Defining system based on SSOT

Readme

system-design

Descriptive layer for systems designed around a Single Source of Truth (SSOT).

npm-version downloads build security qa-control

language: English also available in: Spanish

Goal

This module provides the vocabulary to describe a system — domain types, entities, fields, procedures — as strongly typed, serializable values. From those descriptions, code generators or on-the-fly implementations can derive the table creation scripts, the CRUD endpoints with their database layer, the frontend screens, the serializers in both directions, the type validators, and so on.

This module covers only the descriptive part of systems: it does not generate anything itself.

Naming convention: Def and Info

Every descriptive concept has (at least) two versions, distinguished by a suffix:

  • XxxDef (definition): what the human writes. It contains only the minimum with semantic meaning; anything with a sensible default can be omitted.
  • XxxInfo: what the framework produces by completing the Def with the defaults. Everything is explicit there; it's what the generators consume.

The Info is derived deterministically from the Def, and both are serializable (representable as plain JSON, with no embedded functions).

Vocabulary

Domain types

Each system defines its own type collection (TypeCollection), associating a type name (e.g. "text", "student id") with the TypeScript type it maps to at runtime. The framework provides a few common types (text, integer, boolean) as a starting point; each system can add its own (in the example, fecha — date — and email).

Fields: FieldDef / FieldInfo

A field is described with FieldDef: its type (a key of the TypeCollection) and, optionally, label, nullable and description. completeRecord produces the corresponding FieldInfo, with those three fields always present (defaults: label derived from the field name, nullable: true, description: ''), preserving the type literal.

Records: RecordDef / RecordInfo

A RecordDef is simply a map of fields (Record<string, FieldDef>): the description of a row. RecordInfoOf<TRecordDef> is the exact Info type that corresponds to a concrete RecordDef — it keeps the keys and the type literal of each field — and it's what completeRecord returns.

RecordInstanceType<TTypeCollection, TRecordDef> deduces, from a RecordDef and the system's TypeCollection, the TypeScript type of an actual instance of that record (the values each field would hold at runtime).

Entities: EntityDef

EntityDef is the container level — the grid-representable unit — shaped as {pk, fields}, where fields is a RecordDef and pk is the tuple of field names that make up the primary key (composite keys are supported). It's built with defineEntity, which checks at compile time that every element of pk is a key of fields, and preserves the literals (pk ends up typed as an exact tuple, not as string[]).

Reusing keys: extractPk / mergePk

  • extractPk(entityDef) returns an entity's pk fields as a RecordDef with the exact type (PkFieldsOf<TEntityDef>), so they can be inherited by spreading them into another entity (for example, curso — course — inherits the pks of periodos, materias and docentes). The rest of the fields need no special function: spreading objects already dedups keys.
  • mergePk(...pks) merges several pks that may overlap, without repeating elements and deduplicating at the type level too (preserving the order of first appearance). It's used for combined pks, like the one for presencias (attendance), which joins the pks of inscripciones (enrollments) and clases (classes).

Example: student system

examples/common/aida.ts describes a student system using this vocabulary. It includes independent entities (docentes — instructors, materias — subjects, periodos — terms, alumnos — students) and entities that inherit keys from others:

  • cursos (courses) inherits the pks of periodos, materias and docentes (the instructor in charge of the course).
  • clases (classes) extends the cursos pk by adding orden (sequence number).
  • preguntas (questions) extends the clases pk by adding pregunta, and opciones (options) extends the preguntas pk by adding opcion (pk inheritance chained across several levels).
  • inscripciones (enrollments) inherits the pks of cursos and alumnos.
  • presencias (attendance) combines, with mergePk, the pks of inscripciones and clases, which share periodo and materia: those fields aren't repeated.

The tests in test/aida-test.ts import these definitions and check, for each part of the vocabulary, both two-way assignability (a hand-written expected Info and the deduced one must be mutually assignable) and the expected compile-time rejections (with // @ts-expect-error): a pk with nonexistent fields, accessing a field the entity doesn't have, reassigning a literal type, and so on.

Structure

  • src/common: the descriptive framework; it knows nothing about any concrete system.
  • examples/common: an example system (a students system) described with the framework.
  • test/: mocha tests that import the example definitions (the examples double as tests).

Way of working

TDD approach, moving forward in small steps: first the test that shows the problem, then the minimal implementation that makes it pass. Type tests aren't loose: they check assignability in both directions and also the expected rejections with // @ts-expect-error.

npm test compiles with TypeScript and runs mocha over the compiled output (no ts-node, no loaders).

Status

Design stage.

License

MIT