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

@moatamed-sce/form-engine

v0.1.5

Published

FormGroup construction, named Saudi validators and rule evaluation for SCE form definitions.

Readme

@moatamed-sce/form-engine

The runtime half of the SCE form platform. @moatamed-sce/form-schema says what a form is — a versioned JSON document; this package turns that document into a working Angular form: a typed FormGroup, resolved validators, evaluated rules, repeating rows, and lookup-backed options.

It renders nothing. Rendering is sce-dynamic-form in @moatamed-sce/ui-components. The split is deliberate: the engine is type:core, so it may use @angular/forms but never HttpClient, NgRx, or the design system — which is what lets the same document be built into a form inside a designer preview, a unit test, and the app.

Status — not published

This package is not currently on npmjs.org, and neither is @moatamed-sce/form-schema, which it declares as a peer. Inside the SCE monorepo both resolve through TypeScript path aliases.

What's in it

buildFormGroup(definition, initialValue, fb) — the entry point. Walks the definition's pages depth-first and returns a non-nullable FormGroup: fields become FormControls, group nodes become nested FormGroups, and array nodes become a FormArray of row groups. A field's initial value is coerced to its type, so a number field handed "5" does not poison the form value with a string.

Named validatorsresolveNamedValidator / resolveNamedValidators turn a NamedValidator ({ name: 'saudiId' }) into a real ValidatorFn, resolved against the implementations in @moatamed-sce/core. This is the half of the schema's central constraint that makes it work: the document carries a name because a function is not serializable, and this is where the name becomes a function again.

Rule evaluationapplyRuleState reads the definition's rules against the current form value and produces the per-field state the renderer needs: hidden, disabled, required, readonly. Rules are the closed operator set the schema defines; there is no eval and no expression parser.

Repeating rowsaddArrayRow, removeArrayRow, canAddArrayRow, canRemoveArrayRow, buildArrayRow and arrayNodeAt, which honour the node's minItems / maxItems so the UI never has to re-derive the bounds.

Wizard stepsresolveSteps turns a definition's steps into a FormStepView per step, reporting a FormStepIssue for anything that does not line up.

Data sources — the FORM_DATA_SOURCE injection token and resolveDataSource. The engine never fetches: the host provides a FormDataSource, and a DataSourceRef names a lookupKey the host resolves. A dependsOn source is re-requested with the value of the control it depends on.

pickActiveValues — the submit-time value with hidden and disabled controls removed, so a conditional branch the user never saw does not travel to the API.

structureSignature — a cheap string that changes only when a definition's shape changes, which is what lets the renderer rebuild the FormGroup on a real structural change and leave it alone otherwise.

Peer dependencies

Angular 21 (@angular/core, @angular/forms), RxJS 7.8, @moatamed-sce/core (the Saudi validators live there) and @moatamed-sce/form-schema (the contract).

Quick start

import { inject } from '@angular/core';
import { NonNullableFormBuilder } from '@angular/forms';
import { parseFormDefinition } from '@moatamed-sce/form-schema';
import { buildFormGroup, applyRuleState, pickActiveValues } from '@moatamed-sce/form-engine';

const fb = inject(NonNullableFormBuilder);

// Parse first — buildFormGroup trusts the definition completely.
const { definition, problems } = parseFormDefinition(stored);
if (!definition) {
  return this.error.set(problems);
}

const { group } = buildFormGroup(definition, { nationalId: '1234567890' }, fb);

// Which fields are hidden / required / readonly right now:
const state = applyRuleState(definition, group);

// On submit — hidden and disabled controls are dropped:
const payload = pickActiveValues(definition, group, state);

Provide a data source when the definition uses lookup options:

import {
  FORM_DATA_SOURCE,
  type FormDataSource,
  type FormDataSourceRequest,
  type FormOption,
} from '@moatamed-sce/form-engine';

@Injectable({ providedIn: 'root' })
export class LookupFormDataSource implements FormDataSource {
  private readonly lookups = inject(LookupsApiService);

  // `lookupKey` is a key, never a URL — the document cannot choose an endpoint.
  // Returns an Observable; the engine subscribes, it never fetches.
  options(request: FormDataSourceRequest): Observable<readonly FormOption[]> {
    return this.lookups.byKey(request.lookupKey, request.dependsOn?.value);
  }
}

providers: [{ provide: FORM_DATA_SOURCE, useExisting: LookupFormDataSource }];

The request also carries definitionId, so a host serving several forms can scope caching or permissions. The renderer injects the token { optional: true } — a form built entirely from static sources should not force every host that renders one to supply a resolver.

A caveat worth knowing

buildFormGroup trusts the definition it is handed. Run it through parseFormDefinition first for anything that did not come from a TypeScript literal in this repo — a stored draft, an API response, an uploaded file. The parser is where prototype-pollution paths, non-JSON values and unbounded row counts are refused; the engine assumes that already happened.

Docs

License

MIT