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

@yahoo/uds-create-config

v5.68.1

Published

Readme

@yahoo/uds-create-config

The UDS config layer. A uds.config.ts authors one Config — the single surface both the author (new System().register({ … })) and every consumer (codegen, Studio, the pipeline) read. There are no per-entity derived getters and no CodegenConfig subclass: group enumeration, the dependency graph, the folder/file tree, validation, and the flat wire format are all first-class on Config, and derived projections are free functions that work on any config.

What this fixes from v1

"v1" is the config layer this replaced — a bespoke class per entity, projections hanging off .derived getters, parallel *Group entities. The differences:

  • Projections are free functions, not per-entity .derived getters. In v1 every entity (Token, Component, Mode, StyleProp, …) exposed a .derived sub-object of engine-computed projections that reached cross-entity state through an injected sibling lookup / parent-Config back-ref and recomputed on each access. All of that is now free functions over a plain Config (cssVar, className, resolveTokenValue) — no .derived on entities, no injected back-refs, and they work on any config.
  • A group is a path prefix, not a Group class. v1 had explicit TokenGroup, ComponentGroup, AssetGroup classes (each with its own .derived). Now a kind stores one flat, path-keyed record and a group is simply a prefix of it — membership is emergent, so there are no parallel Group entities to keep in sync. A group node carries only the folder's own metadata, and a prefix with no metadata needs no node at all.
  • A kind is one zod schema, not a hand-written class. v1 had a bespoke class per entity plus define* factories and caller-path capture. defineEntity({ kind, fields }) derives the typed constructors, the kind-bound ref, the create/update schemas, and the operation set from a single field schema. A config TYPE names the kinds it owns, so what a config can hold depends on what was registered into it rather than on import order. The base Entity owns hydration and serialization, so a new kind adds no constructor or toJSON.
  • Identity is the qualified path. color/brand is the identity; no caller-path capture or separately-tracked namespace. A bare value at an item key is shorthand for the kind's primary field, so Token.item('#1167f4') skips the object wrapper.
  • Config is the single home for derived structure. Emergent grouping, the dependency graph, the folder/file tree, validation, and the flat wire format are first-class methods on Config rather than spread across entity getters and helper modules.
  • Downstream projections read the live config. The pipeline owns no operation definitions and no build-time snapshots — CRUD, MCP tools, and the AI prompt are derived at runtime from the current Config, so a mutation is reflected immediately (see apps/studio/src/ai/config-v2).

Authoring

import { System, Token } from '@yahoo/uds-create-config';

export default new System()
  .register({
    settings: { css: { prefix: 'uds' } }, //         one body, no path: settings is a singleton kind
    tokens: {
      color: Token.group({
        label: 'Color',
        items: {
          brand: Token.item('#1167f4'), //                 bare value = the primary field
          primary: Token.item(Token.ref('color/brand')), // a ref, chased at projection time
        },
      }),
    },
  });

A deep path is authored as a group, with item leaves. The stored form is flat and path-keyed — identity IS the qualified path (color/brand) — but the authored form nests, because that is what says which prefixes are folders and lets one carry a label or a cssPrefix. A flat 'color/brand' key registers the same item and leaves the folder emergent, which is a different artifact. Two shapes keep the flat key, since a group cannot express either: an owner path (Card/Header, a PART of the Card item) and a name that contains a slash (w/1/2, the fraction). The full rule, with the failure modes, is .claude/rules/config-authoring.md.

Defining an entity

defineEntity({ kind, fields, groupFields }) is the whole authoring toolkit for one kind, derived from its z.object field schema. Declare the fields; get the typed item/group constructors, the kind-bound ref, and a typed get. Registration lives on Config.register, so the config file reads as a fluent chain.

export const Token = defineEntity({
  kind: 'token',
  fields: z.object({ value: z.string() /* … */ }),
});
export const { ref: token } = Token;

The kind's metadata — field names, create/update schemas, operation set — is built once and hangs off the returned class as kindDef. A config TYPE names the kind in its entities map, and register copies that metadata into the config instance it is loading, which is what Config hydrates and validates against. Token.extend({ … }) makes a VARIANT that takes the base's slot, so a config authors through whichever variant it registers.

Entities

Concrete kinds live in src/entities/system/: Token, StyleProperty, Component, Composite, Mode, Modifier, Motion, Font, Icon, Package, Guidance, CanvasRole, GlobalStyle, Device, LinkedSystem, plus the derived Operation and Tool. src/entities/native/ holds the native variants a ReactNativeSystem registers in their place (NativeToken, NativeMode, NativeModifier, NativeStyleProperty).

Native component style rules accept literal arrays such as fontVariant: ['tabular-nums'], including in linked component extensions. Register a NativeStyleProperty mapping to route a named prop into native style, or put the array inside the layer's native style object. Arrays contain strings, numbers or booleans; element trees remain anatomy. Arrays are admitted by the platform-neutral authoring schema; Web editing operations continue to reject them as CSS style values. Linked size/auto tokens preserve React Native's auto keyword, while CSS-only min-content, max-content and fit-content remain unsupported.

Components are authored with the exported defineComponent (src/entities/system/defineComponent.ts) — see packages/minimal-template/components/. src/authoring/define-component.ts is an unexported prototype of a different chaining shape for the same job; nothing consumes it.

defineComponent(...) and Component.ref(...).extend(...) reject anatomy, renderProps, file, export, and tokenRefs in types and at runtime. Declare source structure with .render(). Component.item(...) remains the typed stored-body form, including anatomy for Create drafts and snapshots. Public editing operations and stored schemas keep their existing behavior.

Projections

CSS var names, class names, and chased token literals are exported as free functions over a plain Config (cssVar, cssVarRef, className, resolveTokenValue) — not methods on a subclass. So codegen and Studio get projections that work on any config without wrapping, and the base Config stays clean. A projection shared by more than one surface is memoized on the snapshot SLICES it reads (createSliceMemo), never on the config — a Config is immutable, so keying on one misses on every edit.

What this package owns vs. what reads it

This package owns the config model and its projections. It does not own operations downstream: @yahoo/uds-create-codegen emits CSS from a Config, and apps/studio/src/ai/config-v2 derives the CRUD facade, MCP tools, and AI prompt from it. Both just read a Config.

Develop

cd packages/config
bun test          # bun:test
bun run typecheck  # tsc --noEmit