@bladelabs/dmn-zod
v0.4.1
Published
Generate Zod schemas from OMG DMN 1.4 metamodel — like drizzle-zod but for decision models
Keywords
Readme
@bladelabs/dmn-zod
Generate Zod schemas from the OMG DMN 1.4 metamodel — like drizzle-zod but for decision models.
Install
npm install @bladelabs/dmn-zod dmn-moddle
# or
pnpm add @bladelabs/dmn-zod dmn-moddledmn-moddle provides the dmn13.json metamodel. zod is a peer dependency.
Usage
Golden shapes (pure absorption)
import { createDmnSchema, createDmnEnumSchema } from '@bladelabs/dmn-zod'
// Generate a complete DecisionTable schema from the metamodel
export const DecisionTableShape = createDmnSchema('DecisionTable')
// Generate an enum schema
export const HitPolicyShape = createDmnEnumSchema('HitPolicy')Before (hand-authored — 190 lines):
import { z } from 'zod/v4'
import { InputClauseShape } from './input-clause.shape.js'
import { OutputClauseShape } from './output-clause.shape.js'
import { DecisionRuleShape } from './decision-rule.shape.js'
// ... 7 more imports
export const DecisionTableShape = z
.object({
id: z.string().describe('Unique identifier...'),
description: z.string().optional().describe('Human-readable...'),
extensionElements: ExtensionElementsShape.optional().describe('Container for...'),
extensionAttribute: z.array(ExtensionAttributeShape).optional().describe('Array of...'),
label: z.string().optional().describe('Display label...'),
typeRef: z.string().optional().describe('Reference to...'),
input: z.array(InputClauseShape).optional().describe('Array of input clauses...'),
output: z.array(OutputClauseShape).optional().describe('Array of output clauses...'),
annotation: z.array(RuleAnnotationClauseShape).optional().describe('...'),
rule: z.array(DecisionRuleShape).optional().describe('Array of decision rules...'),
hitPolicy: HitPolicyShape.optional().describe('Hit policy governing...'),
aggregation: BuiltinAggregatorShape.optional().describe('Aggregation function...'),
preferredOrientation: DecisionTableOrientationShape.optional().describe('Visual layout...'),
outputLabel: z.string().optional().describe('Display label for...'),
})
.describe('DMN decision table...')
.meta({
id: 'zeroh:dmn/decision-table',
prefLabel: 'DMN Decision Table',
definition: '...',
broader: ['zeroh:dmn/expression'],
related: ['zeroh:dmn/input-clause', ...],
exactMatch: ['omg-dmn:DecisionTable'],
wasDerivedFrom: ['https://www.omg.org/spec/DMN/1.4/PDF', ...],
cascadeGroup: 'dmn',
name: 'DecisionTableShape',
description: '...',
version: '1.0.0',
category: 'dmn',
standards_provenance: { ... },
}) satisfies z.ZodType<DmnDecisionTable>After (generated — 1 line):
import { createDmnSchema } from '@bladelabs/dmn-zod'
export const DecisionTableShape = createDmnSchema('DecisionTable')Same fields. Same .describe() text. Same .meta() with provenance. One line.
Domain shapes (refined + extended)
import { z } from 'zod/v4'
import { createDmnSchema } from '@bladelabs/dmn-zod'
// Mudarabah compliance decision — extends golden DMN shape
export const MudarabahDecisionShape = createDmnSchema('DecisionTable', {
// Refine: tighten hitPolicy for Islamic finance
refinements: {
hitPolicy: (s) => s.default('FIRST'),
},
// Extend: add domain-specific fields
extensions: {
obligation_refs: z.array(z.string()).describe('AAOIFI SS-13 obligations'),
is_hard_gate: z.boolean().describe('Blocks workflow if failed'),
},
// Override meta for domain identity
metaOverrides: {
id: 'zeroh:grc-islamic/mudarabah-decision',
cascadeGroup: 'grc-islamic-finance',
},
})Explicit metamodel path
If dmn-moddle isn't in node_modules, provide the path:
import { initDmnMetamodel, createDmnSchema } from '@bladelabs/dmn-zod'
initDmnMetamodel('/path/to/dmn13.json')
const schema = createDmnSchema('DecisionTable')Subpath imports
// Main API
import { createDmnSchema } from '@bladelabs/dmn-zod'
// Schema module directly
import { createDmnSchema } from '@bladelabs/dmn-zod/schema'
// Metamodel types and reader
import type { ModdleDescriptor } from '@bladelabs/dmn-zod/metamodel'Architecture
Follows drizzle-zod's 6-layer pattern:
- Entry —
createDmnSchema(typeName, options?) - Introspect — reads
dmn13.jsonmetamodel - Map types — moddle types to Zod (String to z.string(), Enum to z.enum(), etc.)
- Inheritance — flattens superClass chain (DMNElement to NamedElement to Expression to ...)
- Refinements — callbacks or overrides per field (drizzle-zod pattern)
- Meta — auto-populates
.meta()with OMG DMN 1.4 provenance
Plus a description layer from descriptions.ts — hand-authored semantic descriptions for every field, since the metamodel has no annotations.
API
| Function | Input | Output |
|---|---|---|
| createDmnSchema(type, opts?) | DMN type name | z.ZodObject with .meta() |
| createDmnEnumSchema(enum, opts?) | DMN enum name | z.ZodEnum with .meta() |
| initDmnMetamodel(path) | File path or object | void (configures reader) |
| getDmnTypeNames() | — | string[] of all type names |
| getDmnEnumNames() | — | string[] of all enum names |
| getDmnTypeProperties(type) | DMN type name | ModdleProperty[] |
| registerDmnSchema(type, schema) | Name + schema | void (for cross-type refs) |
Test
pnpm test # 124 tests- 28 schema smoke tests (fields, meta, refinements, parse)
- 96 gold-set parity tests (generated vs hand-authored for all 49 types)
Upgrading (when OMG releases a new DMN version)
When dmn-moddle ships a new version (e.g., DMN 1.5):
# 1. Update the moddle dependency
pnpm update dmn-moddle
# 2. Copy the new descriptor into the package
cp node_modules/dmn-moddle/resources/dmn/json/*.json src/data/
# 3. Update the spec constants (one file, ~5 fields)
# Edit src/standard-config.ts:
# version: '1.5'
# fullName: 'OMG Decision Model and Notation 1.5'
# specPdfUrl: 'https://www.omg.org/spec/DMN/1.5/PDF'
# 4. Regenerate types and verify
pnpm generate:overloads # regenerates types.generated.ts
pnpm check # build + test + type-check + publintThe package derives metadata from the moddle JSON descriptor at runtime (prefix, uri, type names, inheritance chains). Only the declared constants in standard-config.ts need manual updating — everything else regenerates automatically.
Architecture
src/
data/dmn13.json # Bundled moddle descriptor (copied from dmn-moddle)
standard-config.ts # Spec constants — the ONE file to edit on upgrade
metamodel.ts # Reads descriptor, resolves inheritance chains
descriptions.ts # Hand-authored field descriptions from OMG spec
schema.ts # Schema factory — derives all meta from config + descriptor
types.generated.ts # Auto-generated: type names, interfaces, TYPE_CHAINS
scripts/
generate-overloads.ts # Codegen: reads descriptor → emits types.generated.ts