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

@formspec-org/core

v0.1.0

Published

Core project model, handlers, and normalization layer for Formspec documents

Downloads

13

Readme

formspec-core

Raw project state management for Formspec. Command dispatch, handler pipeline, undo/redo, cross-artifact normalization, and diagnostics.

This package owns the RawProject class (implementing IProjectCore) and the full handler table — 130 commands across definition, component, theme, mapping, pages, and project areas. It is the foundation that formspec-studio-core composes over.

It manages four editable artifacts:

  • definition — form structure and behavior (items, binds, shapes, variables)
  • component — UI tree and widget configuration
  • theme — presentation tokens and cascade rules
  • mapping — bidirectional data transforms

Install

npm install formspec-core

Runtime dependencies: formspec-engine, formspec-types, ajv

Quick Start

import { createRawProject } from 'formspec-core';

const project = createRawProject();

project.dispatch({
  type: 'definition.setFormTitle',
  payload: { title: 'Eligibility Intake' },
});

project.batch([
  {
    type: 'definition.addItem',
    payload: { type: 'field', key: 'fullName', label: 'Full name', dataType: 'string' },
  },
  {
    type: 'definition.setBind',
    payload: { path: 'fullName', properties: { required: true } },
  },
]);

console.log(project.fieldPaths());    // ['fullName']
console.log(project.bindFor('fullName'));

const bundle = project.export();

Architecture

RawProject is a thin orchestration facade (~380 lines) that delegates to focused subsystems:

RawProject (facade)
 ├── CommandPipeline     — phase-aware dispatch with middleware
 ├── HistoryManager      — undo/redo stacks and command log
 ├── handlers/           — 17 handler modules aggregated into builtinHandlers
 ├── queries/            — 7 pure-function query modules
 ├── tree-reconciler     — definition → component tree reconciliation
 └── state-normalizer    — cross-artifact invariant enforcement

Subsystems

| Module | Responsibility | |--------|---------------| | pipeline.ts | CommandPipeline: clones state, runs commands across phases, calls reconcile between phases when signaled, wraps execution with middleware | | history.ts | HistoryManager<T>: generic undo/redo stacks with depth cap, command log, push/pop/clear operations | | tree-reconciler.ts | reconcileComponentTree(): pure function — takes definition + existing tree + theme, returns a new component tree preserving bound node properties | | state-normalizer.ts | normalizeState(): pure function enforcing cross-artifact invariants (URL sync, breakpoint sort, breakpoint inheritance) | | normalization.ts | normalizeDefinition(): converts legacy serialization shapes to canonical forms (instances array → object, binds object → array); idempotent | | theme-cascade.ts | resolveThemeCascade(): resolves effective presentation properties for an item across defaults → selectors → item-overrides | | page-resolution.ts | resolvePageStructure(): resolves theme.pages into enriched ResolvedPageStructure with region existence checks and diagnostics | | component-documents.ts | Helpers for splitting authored vs. generated component state, creating artifact envelopes | | handlers/index.ts | Aggregates 17 handler modules into a frozen builtinHandlers table. Each module exports a Record<string, CommandHandler> | | queries/*.ts | 7 modules of pure query functions: (ProjectState, ...) => result. Field queries, expression index, dependency graph, statistics, diagnostics, versioning, registry queries |

Design Principles

  • No global mutable state. Each RawProject instance receives its own frozen handler table at construction. Custom handlers can be injected via options.handlers.
  • JSON-native state. ProjectState is fully JSON-serializable — no Maps or class instances. JSON.stringify(project.state) works.
  • Pure functions over methods. Subsystems are pure functions that take state as an argument. This makes them independently testable without instantiating RawProject.
  • Single dispatch pipeline. dispatch(), batch(), and batchWithRebuild() all delegate to a single _execute(phases) method backed by CommandPipeline. Middleware wraps all paths uniformly.

IProjectCore

IProjectCore is the seam between this package and formspec-studio-core. It defines the full public API surface.

Command dispatch: dispatch(command), batch(commands), batchWithRebuild(phase1, phase2)

State getters: state, definition, component, theme, mapping, artifactComponent, generatedComponent

History: undo(), redo(), canUndo, canRedo, log, resetHistory()

Queries: fieldPaths(), itemAt(path), searchItems(filter), statistics(), bindFor(path), componentFor(key), effectivePresentation(key), unboundItems(), resolveToken(key), resolveExtension(name), allDataTypes(), instanceNames(), variableNames(), optionSetUsage(name), listRegistries(), browseExtensions(filter?), responseSchemaRows()

FEL & expressions: parseFEL(expression, context?), felFunctionCatalog(), availableReferences(context?), allExpressions(), expressionDependencies(expression), fieldDependents(fieldPath), variableDependents(variableName), dependencyGraph()

Diagnostics & versioning: diagnose(), diffFromBaseline(fromVersion?), previewChangelog(), export()

Command Dispatch Flow

Every dispatch() follows the same pipeline via _execute(phases):

  1. Clone state snapshot for undo
  2. CommandPipeline.execute():
    • Clone state
    • Run middleware chain (may transform or reject)
    • Execute commands per phase on the clone
    • Reconcile component tree between phases when signaled
  3. normalizeState() — cross-artifact invariants
  4. HistoryManager.push() — snapshot for undo
  5. Swap to new state
  6. Notify subscribers

batch() groups commands into one phase. batchWithRebuild() uses two phases with inter-phase tree reconciliation.

Command Catalog

130 commands across 17 handler modules:

| Area | Commands | Description | |------|----------|-------------| | definition.* | 46 | Items, binds, shapes, variables, option sets, instances, pages, screener, migrations, metadata | | component.* | 25 | Component tree structure, node properties, custom components, responsive overrides | | theme.* | 28 | Tokens, defaults, selectors, item overrides, pages, grid regions, breakpoints, stylesheets | | mapping.* | 16 | Rules, inner rules, adapter config, preview, extensions | | pages.* | 10 | Page add/delete/reorder, mode, item assignment, region management | | project.* | 5 | Import, subform import, registry loading, publishing |

Extensibility

Custom handlers can be injected at construction:

const project = createRawProject({
  handlers: {
    'custom.myCommand': (state, payload) => {
      // mutate state clone
      return { rebuildComponentTree: false };
    },
  },
});

Custom handlers merge with builtins. Keys override builtins.

Middleware wraps the full dispatch pipeline:

const project = createRawProject({
  middleware: [
    (state, commands, next) => {
      console.log('before:', commands);
      const result = next(commands);
      console.log('after');
      return result;
    },
  ],
});

ProjectOptions

| Option | Type | Description | |--------|------|-------------| | seed | Partial<ProjectState> | Initial state. Omitted fields get defaults (empty definition, blank artifacts). | | maxHistoryDepth | number | Maximum undo snapshots (default: 50). | | middleware | Middleware[] | Pipeline wrappers applied to every dispatch. | | handlers | Record<string, CommandHandler> | Custom handlers merged over builtins. | | schemaValidator | SchemaValidator | When set, diagnose() runs structural validation. Omit in environments without bundled schemas. |

Standalone Utilities

These functions are exported for use outside RawProject:

| Export | Description | |--------|-------------| | normalizeDefinition(def) | Converts legacy instances[] → object and binds{} → array. Idempotent. | | resolveThemeCascade(theme, key, type, dataType?) | Resolves effective presentation properties for one item via the three-level cascade. | | resolvePageStructure(state, itemKeys) | Resolves page structure from theme.pages with region existence checks. | | resolveItemLocation(state, path) | Resolves a dot-path to the item and its parent in the definition tree. |

Development

npm run build        # tsc
npm run test         # vitest run (481 tests)
npm run test:watch   # vitest