Skip to content

Getting started

Try It Live

You can try self-assert directly in your browser on CodeSandbox.

Installation

sh
npm install self-assert
sh
pnpm add self-assert
sh
yarn add self-assert

Validating Domain Objects with Assertion

The Assertion class lets you express rules declaratively. You can:

  • Define self-contained rules (no parameters)
  • Define reusable rules that apply to different values

A typical example:

ts
import { Assertion, Requirements, Ruleset } from "self-assert";

export class Person {
  static readonly nameNotBlank = Assertion.requiring(
    "name.notBlank",
    "Name must not be blank",
    Requirements.isNotBlank
  );

  static readonly agePositive = Assertion.requiring(
    "age.positive",
    "Age must be positive",
    Requirements.isPositive
  );

  static named(name: string, age: number) {
    Ruleset.ensureAll(
      this.nameNotBlank.evaluateFor(name),
      this.agePositive.evaluateFor(age)
    );
    return new this(name, age);
  }

  protected constructor(protected name: string, protected age: number) {}

  getName() {
    return this.name;
  }

  getAge() {
    return this.age;
  }
}

If any rule fails, a RulesBroken error is thrown with details.

Suggesting Completion with Draft Assistants

FieldDraftAssistant and SectionDraftAssistant help you manage incomplete or draft objects, especially useful in UI flows or external interfaces.

ts
function createPersonAssistant() {
  const nameAssistant = FieldDraftAssistant.handlingAll(
    ["name.notBlank"],
    (person: Person) => person.getName()
  );

  const ageAssistant = IntegerDraftAssistant.for(
    "age.positive",
    (person: Person) => person.getAge()
  );

  const personAssistant = SectionDraftAssistant.topLevelContainerWith(
    [nameAssistant, ageAssistant],
    (name, age) => Person.named(name, age),
    []
  );

  return Object.assign(personAssistant, { nameAssistant, ageAssistant });
}

TIP

Object.assign lets you expose the inner assistants of a composed one, so you can reuse them without creating a dedicated subclass.

The assistant lets you defer object creation until all validations pass:

ts
const personAssistant = createPersonAssistant();

personAssistant.nameAssistant.setModel("   ");
personAssistant.ageAssistant.setModel(30);

personAssistant.withCreatedModelDo(
  () => {
    throw new Error("this should not happen");
  },
  () => console.log("Person not created")
);

console.log(personAssistant.hasBrokenRules());

personAssistant.nameAssistant.setModel("John");
personAssistant.ageAssistant.setModel(50);

personAssistant.withCreatedModelDo(
  (person) => console.log(`Person created: ${person.getName()}`),
  () => {
    throw new Error("this should not happen");
  }
);