Components
Answer with nodes the shop front draws in its own style.
import { ... } from '@storeware/plugin-sdk/components';countComponentNodes
Every node in a tree, children included.
function countComponentNodes(nodes: readonly ComponentNode[]): numbercomponentDepth
How deep a tree goes; a stack inside a stack is two.
function componentDepth(nodes: readonly ComponentNode[]): numberparseComponents
Parses an answer, or says why it cannot be drawn. Storeware runs the same check, so a tree this accepts is a tree the shop front draws.
function parseComponents(value: unknown): { ok: true; components: ComponentNode[]; } | { ok: false; problems: string[]; }components
Builds an answer, checked, so a mistake shows in development, not on a shop.
function components(...nodes: ComponentNode[]): ComponentsAnswerComponentNode
type ComponentNode = {
type: 'heading';
text: string;
level?: 2 | 3 | 4 | undefined;
} | {
type: 'text';
text: string;
emphasis?: 'strong' | 'subdued' | undefined;
} | {
type: 'list';
items: string[];
ordered?: boolean | undefined;
} | {
type: 'table';
head?: string[] | undefined;
rows: string[][];
} | {
type: 'rating';
value: number;
count?: number | undefined;
label?: string | undefined;
} | {
type: 'badge';
text: string;
tone?: (typeof COMPONENT_TONES)[number] | undefined;
} | {
type: 'image';
src: string;
alt: string;
width?: number | undefined;
height?: number | undefined;
} | {
type: 'link';
text: string;
href: string;
} | {
type: 'button_link';
text: string;
href: string;
} | {
type: 'accordion';
items: {
title: string;
children: ComponentNode[];
}[];
} | {
type: 'notice';
text: string;
tone?: (typeof COMPONENT_TONES)[number] | undefined;
title?: string | undefined;
} | {
type: 'stack';
children: ComponentNode[];
direction?: 'row' | 'column' | undefined;
};ComponentType
type ComponentType = ComponentNode['type'];ComponentsAnswer
type ComponentsAnswer = z.infer<typeof componentsAnswerSchema>;COMPONENTS_CONTENT_TYPE
const COMPONENTS_CONTENT_TYPE: 'application/vnd.storeware.components+json'MAX_COMPONENT_NODES
The most nodes one answer may hold, counting every child.
const MAX_COMPONENT_NODES: 200COMPONENT_IMAGE_HOSTS
The one host an image may come from. An app puts an image there with POST /api/v1/app/assets, which needs no scope, and uses the URL it gets.
const COMPONENT_IMAGE_HOSTS: readonly ['cdn.mystoreware.com']COMPONENT_TONES
The tones a badge or notice can take, drawn from the theme's own colours.
const COMPONENT_TONES: readonly ['neutral', 'info', 'success', 'warning', 'critical']COMPONENT_TYPES
const COMPONENT_TYPES: readonly ['heading', 'text', 'list', 'table', 'rating', 'badge', 'image', 'link', 'button_link', 'accordion', 'notice', 'stack']componentNodeSchema
const componentNodeSchema: z.ZodType<ComponentNode, unknown, z.core.$ZodTypeInternals<ComponentNode, unknown>>MAX_COMPONENT_DEPTH
The deepest a tree may nest. Enough for an accordion of stacks.
const MAX_COMPONENT_DEPTH: 4componentsAnswerSchema
What an app answers with: \{ components: [...] \}.
const componentsAnswerSchema: z.ZodObject<{ components: z.ZodArray<z.ZodType<ComponentNode, unknown, z.core.$ZodTypeInternals<ComponentNode, unknown>>>; }, z.core.$strict>