@edgerules/portable
TypeScript type definitions for the EdgeRules Portable JSON format — the canonical serialization schema for EdgeRules models, functions, types, and errors.
This package holds the TypeScript types and one runtime constant, Kinds: the common @kind groups of
service.get(path, kinds?). It is safe to use in both browser and Node.js projects.
Installation
npm install @edgerules/portable
What is Portable JSON?
Every EdgeRules model is a plain JSON object. The Portable format is how you store a model in a database, send it over
HTTP, and pass it to DecisionService — without any special serialization library.
- Every object node has an
@kind. Only@kind,@node, and@namehave the@prefix. - A model root is a
PortableContext:{ '@kind': 'context', fields: { ... } }. - A body declaration has a compact interface node (
*-interface) without the body.
@kind | TypeScript type | Description |
|---|---|---|
context, workbook | PortableContext | Record of fields; a workbook is root-only |
placeholder | PortablePlaceholderValue | Typed input slot — the host supplies a value |
expression, expression-interface | PortableExpression, PortableExpressionInterface | Computed field with its variables |
invocation | PortableInvocation | Call to a user method within the model |
type | PortableType | User-defined type: fields or alias |
facts | PortableFacts | Derived fields of a structure type |
method, method-interface | PortableMethod, PortableMethodInterface | func: parameters, return, body |
ruleset, ruleset-interface | PortableRuleset, PortableRulesetInterface | Decision table: hitPolicy, rules, default |
loop, loop-interface | PortableLoop, PortableLoopInterface | Bounded state machine: state, do, output |
optimise, optimise-interface | PortableOptimise, PortableOptimiseInterface | LP/MILP: variables, objective, constraints |
external-method | PortableExternalMethod | external func: parameters, required return |
decision, decision-interface | PortableDecision, PortableDecisionInterface | decision: context, body, linked requires |
service, service-interface | PortableService, PortableServiceInterface | service: parameters, body, linked requires |
error | PortableError | Structured error crossing the host boundary |
Usage
Typed model construction
import type { PortableContext, PortablePlaceholderValue } from '@edgerules/portable';
const ageInput: PortablePlaceholderValue = {
'@kind': 'placeholder',
type: 'number',
required: true,
};
const model: PortableContext = {
'@kind': 'context',
fields: {
minAge: 18,
applicant: {
'@kind': 'context',
fields: {
age: ageInput,
income: { '@kind': 'placeholder', type: 'number', default: 0 },
},
},
isEligible: 'applicant.age >= minAge and applicant.income >= 1000',
},
};
Defining a method
import type { PortableMethod } from '@edgerules/portable';
const isEligible: PortableMethod = {
'@kind': 'method',
parameters: { age: 'number', income: 'number' },
body: {
'@kind': 'context',
fields: { result: 'age >= 18 and income >= 1000' },
},
};
Defining a type
import type { PortableType } from '@edgerules/portable';
const CustomerType: PortableType = {
'@kind': 'type',
fields: {
name: { '@kind': 'placeholder', type: 'string', required: true },
age: 'number',
tier: { '@kind': 'placeholder', type: 'string', default: 'STANDARD', enum: ['GOLD', 'SILVER', 'STANDARD'] },
},
};
Calling a ruleset (invocation)
import type { PortableInvocation } from '@edgerules/portable';
const call: PortableInvocation = {
'@kind': 'invocation',
method: 'risk',
arguments: { age: 'applicant.age', income: 'applicant.income' },
};
Reading with a kind filter
import { Kinds } from '@edgerules/portable';
service.get('*', Kinds.METHODS); // methods, rulesets, loops, optimise declarations
service.get('*', Kinds.EXTERNALS); // the resolver-wiring catalog
service.get('*', [...Kinds.EXPRESSION_INTERFACES, ...Kinds.TYPES, ...Kinds.INVOCABLE_INTERFACES]); // project explorer
Handling errors
import type { PortableError, PortableErrorType } from '@edgerules/portable';
function handleError(err: unknown): void {
const error = err as PortableError;
switch (error.type as PortableErrorType) {
case 'EntryNotFound':
console.error('Path not found:', error.path);
break;
case 'WrongFilter':
console.error('Unknown @kind in kinds:', error.message);
break;
default:
console.error(error.message);
}
}
Key Types
// The root model object, a nested record, or a root workbook
PortableContext
// Any node returned by `service.get(path, kinds?)`
PortableNode
// Any value `service.set(path, value)` accepts
PortableSetValue
// A filterable @kind, and the common groups of them
Kind
Kinds
// The base of each method-like interface: { parameters, return? }
MethodDefinition
// A structured error from the engine, and its discriminant
PortableError
PortableErrorType // "EntryNotFound" | "WrongFieldPath" | "DuplicateName" | "Parse" | "Execution" | "WrongFilter"
Related packages
@edgerules/node— Node.js decision service@edgerules/web— browser decision service