@nestjs-dash/support
v1.1.1
Published
Shared utilities and types for NestJS Dash
Readme
@nestjs-dash/support
Shared JSON/SDUI types and small, dependency-free utilities used across NestJS Dash: the SchemaNode tree that server-side builders emit and the client renders, ChartSpec, component kinds, declarative field conditions, and a few helpers.
Most apps never import this package directly — @nestjs-dash/core and @nestjs-dash/design-system depend on it. Depend on @nestjs-dash/support when you write custom widgets/pages (for escapeHtml), build tooling, or a custom renderer against the same JSON shapes.
Install
pnpm add @nestjs-dash/support- No runtime dependencies and no peer dependencies.
- ESM only (
"type": "module",importexport condition only) — load it withimport, notrequire(). - Compiled to ES2023; tested on Node.js 24+ (the monorepo's supported runtime) and works in any modern browser.
API
JSON types
import type { JsonPrimitive, JsonValue, JsonObject } from '@nestjs-dash/support';JsonPrimitive (string | number | boolean | null), JsonValue, and JsonObject describe values that survive JSON.stringify — used for SchemaNode.props, which is sent to the browser for hydration.
SchemaNode and ComponentKind
import type { SchemaNode, ComponentKind } from '@nestjs-dash/support';
const node: SchemaNode = {
kind: 'text-input',
name: 'title',
props: { name: 'title', label: 'Title' },
};ComponentKind is the union of every node kind a renderer must handle: form fields (text-input, select, checkbox, toggle, textarea, radio, date-picker, date-time-picker, color-picker, code-editor, tags-input, rich-editor, rich-box, file-upload, image-upload, repeater), layout (schema, section, grid, tabs, tab, html, dialog), table columns (text-column, icon-column, image-column, badge-column, boolean-column, date-column), infolist entries (text-entry, image-entry, badge-entry, boolean-entry, date-entry), filters (select-filter, text-filter, date-filter), and actions (action, edit-action, delete-action, restore-action, create-action, view-action, bulk-action, bulk-delete-action, associate-action, dissociate-action, attach-action, detach-action, bulk-dissociate-action, bulk-detach-action).
Field conditions — FieldCondition, evaluateFieldCondition, validateRequiredWhen
The JSON-safe rule format behind .visibleWhen(), .disabledWhen() and .requiredWhen() on @nestjs-dash/core form fields.
type FieldCondition =
| { field: string; op: 'eq' | 'neq' | 'filled' | 'blank' | 'in'; value?: JsonValue | JsonValue[] }
| { and: FieldCondition[] }
| { or: FieldCondition[] };import { evaluateFieldCondition, validateRequiredWhen } from '@nestjs-dash/support';
evaluateFieldCondition({ field: 'type', op: 'eq', value: 'business' }, { type: 'business' }); // true
evaluateFieldCondition({ field: 'country', op: 'in', value: ['US', 'CA'] }, { country: 'CA' }); // true
evaluateFieldCondition(
{ or: [{ field: 'a', op: 'filled' }, { field: 'b', op: 'filled' }] },
{ a: '', b: 'x' },
); // true
evaluateFieldCondition(undefined, {}); // true — no condition means "always"
const form = {
props: {},
children: [
{
kind: 'text-input',
name: 'company',
props: { name: 'company', requiredWhen: { field: 'type', op: 'eq', value: 'business' } },
},
],
};
validateRequiredWhen(form, { type: 'business' }); // { company: ['This field is required.'] }
validateRequiredWhen(form, { type: 'business', company: 'Acme' }); // nullSemantics:
filled/blank: blank meansnull,undefined, a whitespace-only string, or an empty array.eq/neqwithout avaluecompare against blank (null,undefined,'').- When either side is a boolean, both are coerced to booleans; the strings
'','false','0'and'off'count asfalse, so submitted form strings compare correctly. - When either side is a number, both are compared numerically (
'5'equals5); a blank value never equals a number (so''does not equal0). - Otherwise values are compared as strings.
inrequiresvalueto be an array and matches if any entry is equal by the rules above.validateRequiredWhen(node, values)walks aSchemaNode-shaped tree, skips any node (and its subtree) whosevisibleWhenis false, and returns{ [fieldName]: string[] }for each field whoserequiredWhenholds but whose value is blank — ornullwhen everything passes.
EntityKey<TEntity>
The field-name type used by .make() builders when a resource opts into Resource.for<TEntity>(): the entity's own string keys plus one level of dot-paths into object-shaped (non-array, non-Date) relations.
import type { EntityKey } from '@nestjs-dash/support';
interface Book {
title: string;
author?: { id: string; name: string };
reviewers: { name: string }[];
}
const a: EntityKey<Book> = 'author.name'; // ok
const b: EntityKey<Book> = 'reviewers'; // ok — arrays are not expanded
// const c: EntityKey<Book> = 'reviewers.name'; // type errorEntityKey<Record<string, unknown>> is plain string. An index signature on the entity type collapses the result to string as well.
Charts — ChartSpec, ChartKind, ChartSeriesSpec, ChartRow
The JSON chart description produced by @nestjs-dash/core's BarChart/LineChart/PieChart and rendered by @nestjs-dash/design-system's ChartWidget.
import type { ChartSpec } from '@nestjs-dash/support';
const spec: ChartSpec = {
kind: 'bar', // 'bar' | 'line' | 'pie'
categoryKey: 'month',
series: [{ key: 'signups', label: 'Signups', color: 2 }], // color: 1-5 → --chart-1..5
data: [
{ month: 'Jan', signups: 12 },
{ month: 'Feb', signups: 30 },
],
title: 'Signups',
height: 240, // default 240
legend: true,
emptyLabel: 'No data yet',
};BADGE_COLORS and BadgeColor
The Tailwind palette names accepted by BadgeColumn.colors(): gray, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose. BADGE_COLORS is a readonly tuple; BadgeColor is its element union.
escapeHtml(value: string): string
Escapes &, <, >, " and '. Use it on any user- or database-supplied string you interpolate into the raw HTML returned by a widget render() or a page content(). It is meant for text content and quoted attribute values — not for unquoted attributes, URLs (href/src), inline <script>, or CSS.
import { escapeHtml } from '@nestjs-dash/support';
const html = `<aside class="welcome">Hello, ${escapeHtml(user.name)}</aside>`;jsonTextareaFieldKey(name: string, property?: string): string
The HTML name used by a Textarea.make(name).json(property) field: 'name.property' when property is given, otherwise name. Shared by the server and client so property-scoped JSON textareas on the same column don't collide.
jsonTextareaFieldKey('meta', 'en'); // 'meta.en'
jsonTextareaFieldKey('meta'); // 'meta'compactObject(value)
Returns a shallow copy without keys whose value is undefined (null and other falsy values are kept). Typed as Partial<T>.
compactObject({ a: 1, b: undefined, c: null }); // { a: 1, c: null }assertNever(value: never, message?: string): never
Exhaustiveness helper for switch statements; throws an Error reading <message>: <value> (default message 'Unexpected value') if reached at runtime.
function label(kind: ChartKind): string {
switch (kind) {
case 'bar':
return 'Bar';
case 'line':
return 'Line';
case 'pie':
return 'Pie';
default:
return assertNever(kind);
}
}License
MIT
