@dynamic-entity/core

中文文档
3.3.2发布于 2026/10/24MITTypeScriptESM + CJS构建来源已验证近 30 天下载 2156

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

原始使用文档
文档目录5 节
  1. 📦 Installation
  2. ✨ Features
  3. 🚀 Quick Start
  4. Date display
  5. Validating a config

@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 27 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, url, phone, slider, rating, color, tags), 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.
  • Spreadsheet import engine — deriveImportColumns, suggestMapping and applyMapping turn sheet rows into records against a config, applying the field validators and the rules the way the form does. The engine also includes a streaming CSV reader that detects , / ; / tab (createCsvReader, parseCsv), numbered-header matching for repeating fields, arrayBoundFor to size them from a sheet, and cellText for rendering typed cells. It is pure, so the same engine runs in the browser and in @dynamic-entity/server.
  • 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.
  • Row identity — every object row of an array field carries a $rowId (ROW_ID_KEY). applyRowIds and rowIdMigration give rows saved before 3.2 positional ids, legacy-1, legacy-2, …, the same on every run; newRowId makes a fresh one. See the root README's "Row identity" section.
  • Change history — @dynamic-entity/core/history: diffEntityRecords and describeEntityChanges turn two versions of a record into structured changes and one sentence each, in any language, through audit-trail-text. diffFormConfigs does the same for a form's config and rules. ESM-only underneath: from CommonJS it needs Node 20.19+ or 22.12+, or import(). See the root README's "Change history" section.

🚀 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); // 27

// 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[];                        // fields hidden by a visibility rule
  hiddenTabs: string[];                          // tabs hidden by a visibility rule
  shownFields: string[];                         // fields a `visibility: true` rule showed
  shownTabs: string[];                           // tabs a `visibility: true` rule showed
  validationErrors: Record<string, string>;      // target id → message
  validationWarnings: Record<string, string>;    // target id → message
  infoBanners: Record<string, string>;           // target id → message
}

Each list and map holds the target as the rule names it — a bare id, or a bracketed path such as [personal.status] — not the rule id. A hide beats a show; a show beats a field's static visibility: false or showWhen. The values map you pass is looked up by the same names, so a rule written with paths needs values keyed by path too: flattenFieldValues builds that map, every field under both its id and its [path]. Pass a baseline record as the third argument to enable the VALUE_CHANGED operator, and { onProblem } as the fourth to hear about a malformed rule, which is skipped rather than thrown:

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,nps is the command-line form of additionalFieldTypes, and --validators noDisposable,uniqueEmail of knownValidators, so a validators.custom naming something unregistered is caught before it is silently dropped at render time. --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.

版本与发布记录

@dynamic-entity/core 共发布 40 个版本,首次发布于 2026/05/02;过去 12 个月发布了 40 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.3.2latest2026/10/2420 天前
3.3.12026/10/2222 天前
3.3.02026/10/2222 天前
3.2.02026/10/2222 天前
3.1.02026/10/1925 天前
3.1.0-rc.12026/10/1925 天前

关于 @dynamic-entity/core 的常见问题

@dynamic-entity/core 是什么?

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

如何安装 @dynamic-entity/core?

在项目目录运行 npm install @dynamic-entity/core,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 dynamic-entity,可以用 npx dynamic-entity 直接运行。

@dynamic-entity/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dynamic-entity/core 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@dynamic-entity/core 可以商用吗?

@dynamic-entity/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dynamic-entity/core 的下载量有多少?

根据 npm 官方下载统计,@dynamic-entity/core 在 2026/10/13 – 2026/11/11 这 30 天内被下载 2,156 次,日均约 72 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dynamic-entity/core 还在维护吗?

最新版本 3.3.2 发布于 2026/10/24(20 天前);至今共发布 40 个版本,其中过去 12 个月发布了 40 个;首次发布于 2026/05/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。