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

@dna-codes/dna-core

v0.6.0

Published

DNA — a JSON description language for business systems. Ships typed schema bindings, a JSON-schema manifest, and the cross-layer validator. Schemas themselves live in @dna-codes/dna-schemas.

Downloads

76

Readme

@dna-codes/dna-core

The TypeScript home for DNA — typed bindings for the JSON schemas, a typed builder API for constructing Operational DNA programmatically, a DnaValidator for validating any DNA against the canonical schemas (with cross-layer checks), and a pure merge() utility for composing multiple DNA chunks into one with conflict reporting.

The raw JSON schemas live in @dna-codes/dna-schemas and are pulled in as a dependency.

| Layer | What it captures | Analogous to | |---|---|---| | Operational DNA | What the business does | Domain-Driven Design | | Product DNA | What gets built | OpenAPI + Atomic Design | | Technical DNA | How it gets built | Terraform / AWS SAM |

DNA is a contract, not a runtime. Producers (authoring agents, humans) emit JSON that conforms to these schemas; consumers (validators, viewers, code-generation cells) read the JSON and do something useful with it.

Contents

Installation

npm install @dna-codes/dna-core

Quick start — build, then validate

End-to-end: construct a small Operational DNA from scratch via the builders, validate it, get back a green light.

import {
  createOperationalDna,
  addResource,
  addPerson,
  addRole,
  addOperation,
  addTrigger,
  addRule,
  addTask,
  DnaValidator,
} from '@dna-codes/dna-core'

// 1. Start from an empty domain.
let dna = createOperationalDna({ domain: { name: 'lending', path: 'acme.lending' } })

// 2. Compose primitives via the builders. Each call returns
//    { dna, conflicts }; same-name composes via the merge engine.
;({ dna } = addResource(dna, {
  name: 'Loan',
  attributes: [
    { name: 'amount', type: 'number', required: true },
    { name: 'status', type: 'enum', values: ['pending', 'active', 'repaid'] },
  ],
  actions: [
    { name: 'Apply',   type: 'write' },
    { name: 'Approve', type: 'write' },
  ],
}))
;({ dna } = addPerson(dna, { name: 'Borrower' }))
;({ dna } = addRole(dna, { name: 'Underwriter' }))
;({ dna } = addOperation(dna, { name: 'Loan.Apply',   target: 'Loan', action: 'Apply' }))
;({ dna } = addOperation(dna, { name: 'Loan.Approve', target: 'Loan', action: 'Approve' }))
;({ dna } = addTrigger(dna, { operation: 'Loan.Apply', source: 'user' }))
;({ dna } = addRule(dna, { operation: 'Loan.Approve', type: 'access', allow: [{ role: 'Underwriter' }] }))
;({ dna } = addTask(dna, { name: 'approve-loan', actor: 'Underwriter', operation: 'Loan.Approve' }))

// 3. Validate.
const validator = new DnaValidator()
const schemaResult = validator.validate(dna, 'operational')
const crossResult = validator.validateCrossLayer({ operational: dna })

if (!schemaResult.valid) console.error(schemaResult.errors)
if (!crossResult.valid)  console.error(crossResult.errors)
// Both green for the DNA above.

That covers the two responsibilities — builders give you a typed, schema-aware way to construct DNA, and DnaValidator confirms the result holds together at the JSON-Schema level and across primitive references.


Constructing Operational DNA

Builders — the canonical way to construct DNA

createOperationalDna(opts) returns an empty DNA shell. Eleven add* builders compose primitives into it: every Operational primitive (Resource, Person, Role, Group, Membership, Operation, Trigger, Rule, Task, Process) plus addRelationship. Every add* has the same shape:

function addResource(
  dna: OperationalDNA,
  resource: Resource,
  opts?: { validate?: boolean }
): { dna: OperationalDNA; conflicts: Conflict[] }

Properties guaranteed by every builder:

  • Pure & immutable — never mutates the input DNA. Same input, same output, every time.
  • Compose-on-duplicate — same-name composes via the merge engine. List-shaped children union by name; scalar disagreements emit Conflict entries. Builders never throw on a duplicate name.
  • Default-on schema validation — catches malformations TypeScript can't express (regex patterns like ^[A-Z][a-zA-Z0-9]*$ on Resource.name, conditional requirements like values when Attribute.type === 'enum', the source-of-truth enum lists). Pass { validate: false } for hot paths where input is already known valid.
  • Compile-time typed — input parameters are typed against each primitive's schema. Misuse fails at build time, not just at runtime.
// Compile-time error: name must be a string
addResource(dna, { name: 123 })

// Compile-time error: action is required on Operation
addOperation(dna, { target: 'Loan' })

// Runtime error: enum requires `values`
addResource(dna, { name: 'Loan', attributes: [{ name: 'status', type: 'enum' }] })
// throws: "operational/resource input failed validation: /attributes/0 must have required property 'values'"

Auditing compose-on-add disagreements:

let r = addResource(dna, { name: 'Loan', description: 'Consumer loan' })
const r2 = addResource(r.dna, { name: 'Loan', description: 'Mortgage product' })
// r2.conflicts contains:
//   { path: 'resources.Loan.description',
//     values: [{ value: 'Consumer loan', source: ... }, { value: 'Mortgage product', source: ... }],
//     recommendation: { value: 'Mortgage product', reason: 'stable-order: tied on all heuristics; first-observed wins' },
//     kind: 'scalar' }

Full reference and patterns: docs/builders.md.

merge() — compose multiple chunks at once

Compose multiple Operational DNA chunks into one. Pure, deterministic, no I/O — designed for the multi-source ingest case (@dna-codes/dna-ingest calls this as its fan-in step) but usable on its own.

import { merge } from '@dna-codes/dna-core'

const result = merge([
  { dna: chunkA, source: { uri: 'file:///sop.md',  loadedAt: '2025-01-01T00:00:00.000Z' } },
  { dna: chunkB, source: { uri: 'gdrive://policy', loadedAt: '2025-06-01T00:00:00.000Z' } },
])

result.dna          // merged OperationalDNA — validates against the operational schema
result.conflicts    // Conflict[] — scalar disagreements + unresolved-ref warnings
result.provenance   // dotted-path → contributing sources, e.g. 'resources.Loan' → [...]

Same-named primitives unify across chunks. List-shaped children union by name. Scalar disagreements use the v1 recommendation policy:

  1. Value backed by the most distinct sources wins.
  2. Tie-break by most recent loadedAt.
  3. Tie-break by longest non-empty string representation.
  4. Final tie-break: stable input order.

The recommendation is written into the merged DNA so the result is always schema-valid; the full list of competing values + sources rides along in Conflict.values for review. Cross-references (Operation.target, Membership.{person,role,group}, Trigger.operation, Task.{actor,operation}, Step.task, Rule.operation) resolve against the merged noun set; unresolved references surface as Conflict entries with kind: 'unresolved-reference' (the referencing primitive is still emitted so a reviewer can fix the source documents).

Full reference: docs/merge.md.

Builders vs. merge() — when to reach for which

| Use case | Reach for | |---|---| | Building DNA from code (fixtures, tests, a custom adapter, programmatic composition) | Builders | | Composing N chunks produced by separate sources (input adapters, multi-document ingest) | merge() | | Adding one primitive to an existing DNA, audit-able with conflict reporting | Builders | | Resolving cross-references after composition | Both — merge() does it as its final step; builders rely on the same engine |

Both share the same composition engine internally; pick the surface that matches your call shape.


Validating Operational DNA

DnaValidator — the validator

AJV-backed. All DNA schemas (per-primitive + per-layer aggregates) pre-registered. No setup beyond new DnaValidator().

import { DnaValidator } from '@dna-codes/dna-core'

const validator = new DnaValidator()

Per-document validation

validate(doc, schemaId) validates a document against any registered schema, by short or full ID:

import operational from './dna/lending/operational.json'

validator.validate(operational, 'operational')                        // full document
validator.validate(operational.domain.resources[0], 'operational/resource')  // single primitive

The result is { valid: boolean; errors: ErrorObject[] } — AJV's standard error shape. Common interpretation pattern:

const result = validator.validate(operational, 'operational')
if (!result.valid) {
  for (const err of result.errors) {
    console.error(`  ${err.instancePath || '/'}  ${err.message}`)
    if (err.params) console.error(`    params:`, err.params)
  }
}

Common error classes you'll see (and why each exists):

| Error | What triggered it | |---|---| | must have required property 'X' | A required field is missing. Check the primitive's schema in @dna-codes/dna-schemas for the required list. | | must match pattern "^[A-Z][a-zA-Z0-9]*$" | A name doesn't match its naming convention. PascalCase for nouns; PascalCase verbs for actions; kebab-case for tasks/processes. | | must be equal to one of the allowed values | An enum field got an unknown value (e.g. Attribute.type: 'invalid' or Trigger.source: 'manual'). | | must NOT have additional properties | An unknown field on a primitive (typo, or a field that belongs on a different primitive). | | must have required property 'values' (on an Attribute) | Attribute.type === 'enum' requires a values array — schema's conditional rule. | | must have required property 'schedule' (on a Trigger) | Trigger.source === 'schedule' requires a schedule cron expression. |

Cross-layer validation

Schema validation alone confirms shape, not consistency. validateCrossLayer() walks references between primitives and across layers:

const result = validator.validateCrossLayer({
  operational,
  productCore,   // optional
  productApi,    // optional
  productUi,     // optional
  technical,     // optional
})

Examples of what cross-layer catches:

  • Operational: Operation.target references a noun that doesn't exist; Task.actor references a Role/Person not declared; Process.steps[].task references an undeclared Task; Membership.person / .role / .group references a primitive not declared; Role.scope/.parent cycles; Role exclusion pairs whose effective scopes don't intersect; etc.
  • Operational → Product Core: every Product Core Resource/Operation must be present in Operational (Product Core never invents).
  • Product API → upstream: every API Resource/Operation must trace back to Product Core (preferred) or Operational (fallback).
  • Product UI → Product API: every Page Resource and Block Operation must be declared in Product API.
  • Technical: every Construct's provider must be declared; every Cell's constructs must be declared.

The result is { valid: boolean; errors: { layer, path, message }[] }. The errors are designed to be human-readable directly:

operational    operations/Loan.Approve/target    Operation "Loan.Approve" target "Loanz" is not a declared
                                                 Resource, Person, Role, Group, or Process; available
                                                 targets: "Loan", "Borrower", "Underwriter"

availableSchemas()

validator.availableSchemas()
// → ['https://dna.codes/schemas/operational/resource', 'operational/resource', ...]

Useful for diagnostics; both the full $id and the short form (without the https://dna.codes/schemas/ prefix) are registered for every schema.

Validating untrusted input

function validateOperationalDna(json: unknown):
  | { ok: true; dna: OperationalDNA }
  | { ok: false; errors: { schema?: ErrorObject[]; cross?: CrossLayerError[] } } {
  const validator = new DnaValidator()
  const schema = validator.validate(json, 'operational')
  if (!schema.valid) return { ok: false, errors: { schema: schema.errors } }
  const cross = validator.validateCrossLayer({ operational: json })
  if (!cross.valid) return { ok: false, errors: { cross: cross.errors } }
  return { ok: true, dna: json as OperationalDNA }
}

Schema validation tells you the shape is right. Cross-layer validation tells you the references hold together. Both run together when you need full confidence.


Recipes

Build then validate

Already shown in the quick start. The pattern:

let dna = createOperationalDna({ domain: { name: 'd' } })
;({ dna } = addX(dna, ...))
const validator = new DnaValidator()
const r = validator.validate(dna, 'operational')

The builders' default-on schema validation already ensures every individual primitive validates; the post-hoc DnaValidator.validate(dna, 'operational') confirms the aggregate document is well-formed and validateCrossLayer({ operational: dna }) confirms references resolve.

Audit conflicts produced during construction

let dna = createOperationalDna({ domain: { name: 'd' } })
const accumulated: Conflict[] = []
for (const r of incomingResources) {
  const result = addResource(dna, r)
  dna = result.dna
  accumulated.push(...result.conflicts)
}
if (accumulated.length > 0) {
  console.warn(`composed ${accumulated.length} disagreements:`)
  for (const c of accumulated) {
    console.warn(`  ${c.path}: ${c.values.length} values, picked: ${JSON.stringify(c.recommendation.value)} (${c.recommendation.reason})`)
  }
}

Merge multiple sources, then validate

import { merge, DnaValidator } from '@dna-codes/dna-core'

const result = merge([
  { dna: chunkFromDrive,   source: { uri: 'gdrive://abc',     loadedAt: '...' } },
  { dna: chunkFromSopFile, source: { uri: 'file:///sop.md',   loadedAt: '...' } },
])

const validator = new DnaValidator()
const schemaResult = validator.validate(result.dna, 'operational')
const crossResult  = validator.validateCrossLayer({ operational: result.dna })

if (!schemaResult.valid || !crossResult.valid || result.conflicts.length > 0) {
  // Decide whether to ship the partial DNA, raise an alert, or fail the run.
}

Skip validation in hot paths

Builders default to validate: true. For inner loops processing many primitives that are already known to validate (e.g., re-emitting a previously-validated DNA), opt out:

;({ dna } = addResource(dna, knownGoodResource, { validate: false }))

merge() is also a hot path: it calls into the same composition engine via builders without re-validating per primitive — it's designed for chunks that are already schema-valid by construction.

Inspect what the validator covers

const validator = new DnaValidator()
console.log(validator.availableSchemas().filter(id => !id.startsWith('https://')))
// → ['operational/action', 'operational/resource', 'operational',
//    'product/core/resource', 'product/api/endpoint', ...]

API reference (other exports)

schemas — typed access to every per-primitive JSON Schema

import { schemas } from '@dna-codes/dna-core'

schemas.operational.resource       // Resource schema (Draft 2020-12)
schemas.product.core.role          // Product Core Role schema
schemas.product.api.endpoint       // Product API Endpoint schema
schemas.product.web.page           // Product UI Page schema
schemas.technical.cell             // Technical Cell schema

schemas.operational.resource.$id
// → 'https://dna.codes/schemas/operational/resource'

documents — per-layer aggregate schemas

import { documents } from '@dna-codes/dna-core'

documents.operational              // shape of operational.json
documents.productCore              // shape of product.core.json
documents.productApi               // shape of product.api.json
documents.productUi                // shape of product.ui.json
documents.technical                // shape of technical.json

allSchemas() — flat array

Convenient for bulk-registering with a JSON Schema validator:

import Ajv from 'ajv'
import { allSchemas } from '@dna-codes/dna-core'

const ajv = new Ajv({ strict: false, allErrors: true })
for (const s of allSchemas()) ajv.addSchema(s)

resolveSchemaFile(family, name)

Returns the on-disk path of a schema file, or null if it doesn't exist:

resolveSchemaFile('operational', 'resource')      // → '/abs/.../resource.json'
resolveSchemaFile('product', 'api/endpoint')      // → '/abs/.../api/endpoint.json'
resolveSchemaFile('operational', 'ghost')         // → null

SCHEMA_ROOT, layerDirs

Filesystem roots for consumers that walk the tree themselves:

SCHEMA_ROOT                        // .../node_modules/@dna-codes/dna-schemas
layerDirs.operational              // .../schemas/operational
layerDirs.product                  // .../schemas/product
layerDirs.technical                // .../schemas/technical

Raw JSON schemas

To import an individual schema directly, depend on @dna-codes/dna-schemas:

import resourceSchema from '@dna-codes/dna-schemas/operational/resource.json'

TypeScript types for every primitive

The package exports per-primitive types alongside the runtime API. Useful for typing function signatures, fixtures, or your own builders:

import type {
  OperationalDNA,
  Resource, Attribute, Action,
  Person, Group, Role, RoleScope,
  Membership,
  Operation, OperationChange,
  Trigger, TriggerSource,
  Rule, RuleAllowEntry, RuleCondition, RuleConditionOperator, RuleType,
  Task,
  Process, ProcessStep,
  Relationship, RelationshipCardinality,
  Domain,
  Conflict, ConflictRecommendation, ConflictValue, Source, Provenance,
  MergeChunk, MergeResult,
} from '@dna-codes/dna-core'

A contract test in this package re-validates each schema's examples[] through the corresponding TypeScript type at build time — if a JSON Schema gains a field the TypeScript type is missing, the build fails.


Using schemas from non-JS languages

Install @dna-codes/dna-schemas directly — zero dependencies, ships only the JSON files:

node_modules/@dna-codes/dna-schemas/
  operational/*.json              # 15 primitive + 1 aggregate
  product/core/*.json             # 5 primitives
  product/api/*.json              # 4 primitives
  product/web/*.json              # 4 primitives
  product/product.{core,api,ui}.json   # 3 aggregates
  technical/*.json                # 11 primitives + 1 aggregate

Schemas cross-reference each other by absolute URI (e.g. https://dna.codes/schemas/operational/attribute), so your validator must register all schemas before validating any one of them. The allSchemas() helper does this for you in JS; reproduce the same load-everything-then-validate pattern in your target language's validator.

Primitive vocabulary

| Layer | Primitives | |---|---| | Operational | Resource, Action, Operation, Attribute, Domain, Relationship, Trigger, Rule, Person, Role, Group, Membership, Task, Process | | Product | Resource, Action, Operation, Layout, Page, Route, Block, Field, Namespace, Endpoint, Schema, Param | | Technical | Environment, Cell, Construct, Provider, Variable, Output, Script, View, Node, Connection, Zone |

Operational is modeled around the Actor > Action > Subject triad. Resource and Action appear at both the Operational and Product layers by design — a Product Resource is the surface projection of an Operational Resource, likewise for Action. The Actor is a Role (or Person) referenced by Rule (access), Task (assignment), Membership (eligibility), and Process (operator), rather than declared on the Operation itself. State mutations live on Operation.changes; there is no separate Outcome primitive. See each layer's doc in docs/ for full semantics.

What this package does not include

  • The raw JSON schemas. Those live in @dna-codes/dna-schemas (core depends on it).
  • A CLI. See @cell/cba (command: cba) for the full authoring lifecycle.
  • Cell runtimes. Cells are separate consumers of DNA — see technical/cells/*.
  • Multi-source orchestration. @dna-codes/dna-ingest fans many sources into one DNA via the merge() from this package.

Versioning

DNA schemas are the contract; breaking changes require a major version bump. $id URIs (https://dna.codes/schemas/<layer>/<primitive>) are stable identifiers and will not change without a deprecation path.

License

MIT.