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

@appling/model

v0.1.2

Published

Appling semantic graph: nodes, edges and module dependencies of a resolved model

Readme

@appling/model

The semantic graph of an Appling model and the edit operations that change it. Every element is a node, every reference an edge, and module dependencies are computed from them. Visualization, AI and code generation all read this graph and change the model through these operations.

API

import { buildGraph, dependencies } from '@appling/model';

const graph = buildGraph(models, services); // resolved Model roots and the Appling services from @appling/lang
const deps = dependencies(graph);

| Export | Purpose | |---|---| | buildGraph(models, services) | Nodes and edges of resolved models, in document order | | dependencies(graph) | Module to module dependencies with the number of references behind each | | isElement(node) | Whether an AST node becomes a graph node | | qualifiedNames(models) | The qualified name of every element AST node, in document order | | parentElement(node, qns) | The nearest element containing a node | | applyEdit(sources, operation, { release }) | Applies one edit operation to sources keyed by URI | | writeRelease(root, version, graph), readReleases(root), latestRelease(releases) | Release snapshots in <root>/.appling/releases/ | | migrationBlockName(version) | The migration block for steps after a release, such as since_v1_0_0 | | recordReferences(root, qn, ranges), readReferences(root), resolveReferences(root, graph?) | Code references in <root>/.appling/references.json: where elements are implemented, by file, line range and anchor line, resolved as current, shifted, broken or orphaned | | Graph, GraphNode, GraphEdge, Dependency | The serializable graph types |

In a browser bundle, import the graph from @appling/model/graph and the edit operations from @appling/model/edit; the package root also loads the file system functions.

Nodes

| Field | Meaning | |---|---| | qn | Qualified name, such as sales.Order.status | | kind | The AST type, such as Field | | layer | cross, domain, behavior, application, interface or experience | | level | Zoom level: out for modules and use cases, medium for other top level declarations, in for nested elements | | module | Qualified name of the owning module | | props | For display: scalar properties, plus nested non element parts (types, expressions, preconditions, steps) as canonical text | | data | For tools: the same properties with references as qualified names and nested parts as objects with $type; expressions also carry their type and, for names, the target they stand for |

Unnamed elements get a key derived from their content: invariant["total never negative"], policy[read fields total], transition[draft,placed->cancelled], endpoint[GET /orders], effect[set placed_at]. Duplicate keys among siblings get an ordinal, such as policy[read]#2.

Edges

contains links an element to its children. uses links an element to what the names in its expressions stand for, such as a field, an enum value or a role. Every other edge is a reference, named after the referencing property: type, entity, action, event, target, projection and so on.

Edit operations

import { applyEdit } from '@appling/model';

const result = await applyEdit(sources, { op: 'rename', target: 'sales.Order.total', name: 'amount' });
if (result.ok) {
  result.edits;   // minimal text edits: { uri, offset, length, text }
  result.sources; // the changed texts
} else {
  result.errors;  // why the edit was rejected
}

| Operation | Does | |---|---| | { op: 'add', parent, source } | Inserts a declaration, such as note: Text? or requires { ... } else E, at the last position inside the parent where it parses | | { op: 'rename', target, name } | Renames the element and everything that names it: references (also as part of qualified names such as crm.Account), names in expressions, transitions over a renamed enum value, export lists, message keys, and projection fields copied from a renamed field | | { op: 'retype', target, type } | Replaces the type of a field or parameter | | { op: 'remove', target } | Removes the element, with its separating comma in a list and its name in the module's export list | | { op: 'set', target, property, value } | Replaces a property's text, removes it (with its keyword) when the value is empty, or inserts it with its keyword when it is absent |

When release is given, renaming, retyping or removing a domain (L1) element that exists in that release also appends a migration step (rename Order.code -> reference, retype Order.total: Decimal -> Money, remove Order.legacy) to the module's since_v<version> block, creating it when needed. Elements renamed since the release are traced back through the steps already in that block.

The touched element is printed canonically, so a formatted file stays formatted. An edit is rejected when it does not parse or when validation reports an error that was not there before.

Simulator

Simulator evaluates behavior against sample data and actors (import { Simulator } from '@appling/model', or @appling/model/simulator without the language dependencies):

const simulator = new Simulator(graph);
simulator.reach('sales.OrderFlow', 'paid');    // shortest transition path from the initial state, or undefined
simulator.actors('sales.Cancel');              // per actor: 'yes', 'no' or 'depends' (on the data)
simulator.access('sales.Shopper');            // what one actor may do: entity operations, actions, queries, use cases, pages, each with verdict and deciding policies
simulator.risks('sales.Discount');             // invariants that read a field the action changes, also through derived fields
simulator.run('sales.Cancel', { status: 'draft' }, { actor: 'sales.Staff' }, { reason: 'late' });

An action is allowed when its own policies, the update policies of its entity and the create policies of every entity it creates hold. Nothing is open by default: a target that no policy guards is no for every actor. access adds policies up along the reference path the same way: a query needs its entity's read policies, a use case everything its steps call, a page what its lists and projections show. Expressions are evaluated with three valued logic: anything that needs data that is not given is UNKNOWN, which && and || short circuit around. run checks the policies and preconditions, takes the transition whose state and guard match, applies the effects, records emitted events and reports violated invariants.

Relevance

relevance(graph) scores every module and top level declaration from 0 to 1 by how much it matters to the application (import { relevance } from '@appling/model', or @appling/model/relevance without the language dependencies). The score counts the declarations that use it and those it uses, on a log scale against the busiest one; a declared level (core, supporting, peripheral), its own or its module's, maps it into the ranges 0.7 to 1, 0.35 to 0.7 and 0 to 0.35. A module scores as its best declaration. relevanceLevel(score) names the level of a score.

License

MIT