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

@dynamic-entity/core

v2.1.0

Published

Shared model + framework-agnostic form logic for the dynamic-entity ecosystem.

Readme

@dynamic-entity/core

npm version License: MIT

Framework-agnostic core models, pure form logic, rules evaluation engine, and field type vocabulary for the @dynamic-entity ecosystem.

Contains no Angular and no RxJS — it is plain TypeScript and can be used from any framework, or on a server.


📦 Installation

npm install @dynamic-entity/core

✨ Features

  • Nested entity form model (EntityFormConfig) — tabbed hierarchies, sub-tabs, nested groups, arrays, and field table display metadata.
  • Pure form logic — label resolution, display value formatting (setDateFormatters for date / datetime / time), nested data access, and masking, all as side-effect-free functions.
  • Rules engine — condition evaluation over 18 operators (EQUAL, NOT_EQUAL, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH, IS_EMPTY, IS_NOT_EMPTY, LESS_THAN, MORE_THAN, LESS_THAN_EQUAL, MORE_THAN_EQUAL, DATE_BEFORE, DATE_AFTER, IN, NOT_IN, HAS_ITEMS, VALUE_CHANGED) producing three action types: visibility, validation, and info.
  • Canonical field catalog — FIELD_TYPE_CATALOG is the single source of truth for the 21 field type keys (text, textarea, markdown, number, currency, email, password, date, datetime, time, monthYear, dropdown, radio, checkbox, boolean, multiSelect, entity-ref, group, array, image, file), consumed by both the renderer and the builder.
  • Entity reference contracts — EntityReferenceLoader, option normalisation, and pure cascade filtering (lookupFilter / lookupPath).
  • File contracts — canonical FileRef and FileUploadHandler, shared by the image and file field types.
  • Config validation — validateConfig checks structure, field types against the catalog, ids unique per scope, and references that would never resolve — including bracketed field paths and, when passed, FormRules. A JSON Schema for editor completion ships alongside it at @dynamic-entity/core/schema. The same check is the dynamic-entity validate command, for gating configs in CI.
  • Record migration — migrateRecord, needsMigration, stampRecord and validateMigrations move a saved record forward as a config's version changes. Pure, so the same steps run in the browser and on a server.

🚀 Quick Start

import {
  evaluateFormRules,
  resolveLabel,
  FIELD_TYPE_CATALOG,
  type FormRule,
} from '@dynamic-entity/core';

// 1. Resolve a localized label
const label = resolveLabel({ en: 'First Name', de: 'Vorname' }, 'en'); // "First Name"

// 2. Inspect the field type vocabulary
console.log(FIELD_TYPE_CATALOG.length); // 21

// 3. Raise an info banner on the `annualBudget` field when it exceeds 5,000,000
const rules: FormRule[] = [
  {
    formConfigId: 'client',
    fieldId: 'annualBudget',
    conditions: [{ operator: 'MORE_THAN', value: 5_000_000, compareType: 'value' }],
    action: { type: 'info', value: 'Budget exceeds $5,000,000' },
    targets: [{ id: 'annualBudget', type: 'field' }],
    enabled: true,
    priority: 0,
  },
];

const result = evaluateFormRules(rules, { annualBudget: 6_000_000 });
console.log(result.infoBanners); // { annualBudget: 'Budget exceeds $5,000,000' }

evaluateFormRules returns a RuleEvaluationResult:

interface RuleEvaluationResult {
  hiddenFields: string[];                        // field ids hidden by a visibility rule
  hiddenTabs: string[];                          // tab ids hidden by a visibility rule
  validationErrors: Record<string, string>;      // target id → message
  validationWarnings: Record<string, string>;    // target id → message
  infoBanners: Record<string, string>;           // target id → message
}

Each map is keyed by the target id the rule points at, not by rule id. Pass a baseline record as the third argument to enable the VALUE_CHANGED operator:

import { evaluateFormRules, type FormRule } from '@dynamic-entity/core';

declare const rules: FormRule[];
declare const currentValues: Record<string, unknown>;
declare const originalValues: Record<string, unknown>;

const changed = evaluateFormRules(rules, currentValues, originalValues);

Date display

formatDisplayValue formats date, datetime and time through the runtime's locale (toLocaleDateString and friends), not the form's language. language selects which LocalizedText key to read; tying the two would change the punctuation for every consumer whose browser (or Node Intl) is set to something else.

import { setDateFormatters } from '@dynamic-entity/core';

setDateFormatters({
  date: (value, lang) => value.toLocaleDateString(lang ?? []),
});

Every caller of formatDisplayValue honours it — which, in the renderer, means every read-only date, datetime and time field as well as the record summary.

A partial object overrides one kind and leaves the rest. setDateFormatters() with no argument restores the defaults. It is module-level rather than an injection token because formatDisplayValue is a pure function — the renderer, the builder and the CLI all call it, and only one of those has an injector.


Validating a config

validateConfig is the API. The package also ships a dynamic-entity bin so the same check can run in CI without writing a script:

npx dynamic-entity validate ./form-config.json

--additional-field-types signature,rating is the command-line form of additionalFieldTypes. --rules rules.json is a FormRule[] checked against the same path/id rule as showWhen. --fail-on-warnings treats a warning as a failure. Exit 0 means no errors, 1 means the config is unusable, 2 means the file or the JSON itself is.