@forumone/throughline-design-system 1.6.0 → 1.7.0-next-e473c47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @forumone/throughline-design-system
2
2
 
3
+ ## 1.7.0-next-e473c47
4
+
5
+ ### Minor Changes
6
+
7
+ - e473c47: Adds `@forumone/throughline-design-system/recipes`: composed sections stored as data. A recipe is a provisional component contract plus a tree of the manifest's primitives and inline components, with every word bound to a content field. `lintRecipe` checks a recipe against the current manifest (props, slots, bindings, heading order, image fields, component placement and required fields, size and depth) and reports each issue with its path. `RenderRecipe` draws a recipe with the host's components, through the same `CoerceContext` blocks use.
8
+
3
9
  ## 1.6.0
4
10
 
5
11
  ### Minor Changes
@@ -0,0 +1,7 @@
1
+ export { RecipeNodeSchema, RecipeSchema, isBinding, isComponentNode, } from './schema.js';
2
+ export type { Binding, ComponentNode, PrimitiveNode, Recipe, RecipeNode } from './schema.js';
3
+ export { MAX_DEPTH, MAX_NODES, lintRecipe } from './lint.js';
4
+ export type { RecipeIssue } from './lint.js';
5
+ export { RenderRecipe } from './render.js';
6
+ export type { RenderRecipeProps } from './render.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/recipes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC5D,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { RecipeNodeSchema, RecipeSchema, isBinding, isComponentNode, } from './schema.js';
2
+ export { MAX_DEPTH, MAX_NODES, lintRecipe } from './lint.js';
3
+ export { RenderRecipe } from './render.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/recipes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACT,eAAe,GAChB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,18 @@
1
+ import type { Manifest } from '../contract/manifest.js';
2
+ export interface RecipeIssue {
3
+ severity: 'error' | 'warning';
4
+ /** Where: `contract`, `tree`, `tree.slots.children[2].props.gap`. */
5
+ path: string;
6
+ rule: string;
7
+ message: string;
8
+ }
9
+ /** The most deeply nested a node may be, counting the root as 1. */
10
+ export declare const MAX_DEPTH = 6;
11
+ /** The most nodes a recipe may have. */
12
+ export declare const MAX_NODES = 60;
13
+ /**
14
+ * Lints a recipe against the manifest. An empty array is a recipe that may be
15
+ * saved; any error is one that may not.
16
+ */
17
+ export declare function lintRecipe(input: unknown, manifest: Manifest): RecipeIssue[];
18
+ //# sourceMappingURL=lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/recipes/lint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAwBvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,oEAAoE;AACpE,eAAO,MAAM,SAAS,IAAI,CAAA;AAC1B,wCAAwC;AACxC,eAAO,MAAM,SAAS,KAAK,CAAA;AAwB3B;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,WAAW,EAAE,CAwG5E"}
@@ -0,0 +1,242 @@
1
+ import { ComponentContractSchema } from '../contract/schema.js';
2
+ import { RecipeSchema, isBinding, isComponentNode, } from './schema.js';
3
+ /** The most deeply nested a node may be, counting the root as 1. */
4
+ export const MAX_DEPTH = 6;
5
+ /** The most nodes a recipe may have. */
6
+ export const MAX_NODES = 60;
7
+ /** Which content field types each content primitive can show. */
8
+ const SHOWS = {
9
+ text: ['text'],
10
+ richtext: ['richtext'],
11
+ image: ['image'],
12
+ link: ['link'],
13
+ };
14
+ function error(walk, path, rule, message) {
15
+ walk.issues.push({ severity: 'error', path, rule, message });
16
+ }
17
+ /**
18
+ * Lints a recipe against the manifest. An empty array is a recipe that may be
19
+ * saved; any error is one that may not.
20
+ */
21
+ export function lintRecipe(input, manifest) {
22
+ const shape = RecipeSchema.safeParse(input);
23
+ if (!shape.success) {
24
+ return shape.error.issues.map((issue) => ({
25
+ severity: 'error',
26
+ path: issue.path.join('.') || '(recipe)',
27
+ rule: 'shape',
28
+ message: issue.message,
29
+ }));
30
+ }
31
+ const recipe = input;
32
+ const issues = [];
33
+ const contract = ComponentContractSchema.safeParse(recipe.contract);
34
+ if (!contract.success) {
35
+ for (const issue of contract.error.issues) {
36
+ issues.push({
37
+ severity: 'error',
38
+ path: ['contract', ...issue.path].join('.'),
39
+ rule: 'contract',
40
+ message: issue.message,
41
+ });
42
+ }
43
+ return issues;
44
+ }
45
+ const c = contract.data;
46
+ if (c.name in manifest.components || c.name in (manifest.primitives ?? {})) {
47
+ issues.push({
48
+ severity: 'error',
49
+ path: 'contract.name',
50
+ rule: 'contract.name',
51
+ message: `"${c.name}" is already a component or primitive in the design system.`,
52
+ });
53
+ }
54
+ if (c.intent.length < 50) {
55
+ issues.push({
56
+ severity: 'error',
57
+ path: 'contract.intent',
58
+ rule: 'contract.intent',
59
+ message: 'The intent says too little to compose with: at least 50 characters on when to use it.',
60
+ });
61
+ }
62
+ if (c.antiExamples.length === 0) {
63
+ issues.push({
64
+ severity: 'error',
65
+ path: 'contract.antiExamples',
66
+ rule: 'contract.antiExamples',
67
+ message: 'Say at least once when not to use it, and what to use instead.',
68
+ });
69
+ }
70
+ if (c.composition.placement.join() !== 'section') {
71
+ issues.push({
72
+ severity: 'error',
73
+ path: 'contract.composition.placement',
74
+ rule: 'contract.placement',
75
+ message: 'A recipe is a section of a page: its placement is ["section"].',
76
+ });
77
+ }
78
+ const walk = {
79
+ manifest,
80
+ fields: new Map(c.content.fields.map((field) => [field.name, field])),
81
+ used: new Set(),
82
+ issues,
83
+ nodes: 0,
84
+ headings: [],
85
+ };
86
+ const root = manifest.primitives?.[recipe.tree.primitive ?? ''];
87
+ if (!root || root.kind !== 'layout') {
88
+ error(walk, 'tree', 'tree.root', 'The root of a recipe is a layout primitive, usually a Section.');
89
+ }
90
+ lintNode(recipe.tree, 'tree', 1, walk);
91
+ if (walk.nodes > MAX_NODES) {
92
+ error(walk, 'tree', 'tree.size', `${walk.nodes} nodes; a recipe has at most ${MAX_NODES}.`);
93
+ }
94
+ let previous = 1;
95
+ for (const heading of walk.headings) {
96
+ if (heading.level > previous + 1) {
97
+ error(walk, heading.path, 'headings.order', `An h${heading.level} after an h${previous} skips a level.`);
98
+ }
99
+ previous = heading.level;
100
+ }
101
+ for (const field of c.content.fields) {
102
+ if (!walk.used.has(field.name)) {
103
+ error(walk, `contract.content.fields.${field.name}`, 'fields.unused', `"${field.name}" is declared but nothing shows it.`);
104
+ }
105
+ }
106
+ return issues;
107
+ }
108
+ function lintNode(node, path, depth, walk) {
109
+ walk.nodes += 1;
110
+ if (depth > MAX_DEPTH) {
111
+ error(walk, path, 'tree.depth', `Nested ${depth} deep; a recipe is at most ${MAX_DEPTH}.`);
112
+ return;
113
+ }
114
+ if (isComponentNode(node)) {
115
+ lintComponent(node, path, walk);
116
+ return;
117
+ }
118
+ const primitive = walk.manifest.primitives?.[node.primitive];
119
+ if (!primitive) {
120
+ const known = Object.keys(walk.manifest.primitives ?? {}).join(', ');
121
+ error(walk, path, 'primitive.unknown', `There is no primitive "${node.primitive}". Primitives: ${known}.`);
122
+ return;
123
+ }
124
+ lintProps(primitive, node.props ?? {}, path, walk);
125
+ if (primitive.kind === 'content') {
126
+ if (node.slots && Object.keys(node.slots).length > 0) {
127
+ error(walk, `${path}.slots`, 'slots.content', `${primitive.name} shows one field; nothing nests inside it.`);
128
+ }
129
+ lintContentBinding(primitive, node.content, path, walk);
130
+ if (primitive.name === 'Heading') {
131
+ const level = Number(node.props?.['level'] ?? primitive.props['level']?.default ?? 2);
132
+ walk.headings.push({ level, path });
133
+ }
134
+ return;
135
+ }
136
+ if (node.content) {
137
+ error(walk, `${path}.content`, 'content.layout', `${primitive.name} arranges nodes; it does not show a field.`);
138
+ }
139
+ const slots = node.slots ?? {};
140
+ for (const name of Object.keys(slots)) {
141
+ if (!(name in primitive.slots)) {
142
+ error(walk, `${path}.slots.${name}`, 'slots.unknown', `${primitive.name} has no slot "${name}". Slots: ${Object.keys(primitive.slots).join(', ')}.`);
143
+ }
144
+ }
145
+ for (const [name, slot] of Object.entries(primitive.slots)) {
146
+ const children = slots[name] ?? [];
147
+ if (slot.required && children.length === 0) {
148
+ error(walk, `${path}.slots.${name}`, 'slots.required', `${primitive.name}'s "${name}" needs something in it.`);
149
+ }
150
+ if (slot.max !== undefined && children.length > slot.max) {
151
+ error(walk, `${path}.slots.${name}`, 'slots.max', `${primitive.name}'s "${name}" holds at most ${slot.max}.`);
152
+ }
153
+ children.forEach((child, index) => lintNode(child, `${path}.slots.${name}[${index}]`, depth + 1, walk));
154
+ }
155
+ }
156
+ function lintProps(primitive, props, path, walk) {
157
+ for (const [name, value] of Object.entries(props)) {
158
+ const here = `${path}.props.${name}`;
159
+ const prop = primitive.props[name];
160
+ if (!prop) {
161
+ const known = Object.keys(primitive.props);
162
+ error(walk, here, 'props.unknown', `${primitive.name} has no prop "${name}".${known.length ? ` Props: ${known.join(', ')}.` : ' It takes none.'}`);
163
+ continue;
164
+ }
165
+ if (prop.type === 'token' && !prop.allowed.includes(value)) {
166
+ error(walk, here, 'props.token', `"${String(value)}" is not a ${prop.tokenGroup} token ${primitive.name}.${name} allows. Allowed: ${prop.allowed.join(', ')}.`);
167
+ }
168
+ if (prop.type === 'enum' && !prop.values.includes(value)) {
169
+ error(walk, here, 'props.enum', `"${String(value)}" is not one of ${primitive.name}.${name}'s values: ${prop.values.join(', ')}.`);
170
+ }
171
+ if (prop.type === 'boolean' && typeof value !== 'boolean') {
172
+ error(walk, here, 'props.boolean', `${primitive.name}.${name} is true or false.`);
173
+ }
174
+ }
175
+ }
176
+ function lintContentBinding(primitive, binding, path, walk) {
177
+ const kind = primitive.content;
178
+ if (!binding) {
179
+ if (kind.required) {
180
+ error(walk, `${path}.content`, 'content.binding', `${primitive.name} shows a field: bind one with { field: "<name>" }.`);
181
+ }
182
+ return;
183
+ }
184
+ const field = bound(binding.field, `${path}.content`, walk);
185
+ if (!field)
186
+ return;
187
+ if (!SHOWS[kind.type]?.includes(field.type)) {
188
+ error(walk, `${path}.content`, 'content.type', `${primitive.name} shows a ${kind.type} field; "${field.name}" is ${field.type}.`);
189
+ }
190
+ if (field.type === 'image' && !field.required) {
191
+ error(walk, `${path}.content`, 'images.alt', `"${field.name}" is an image a page could show empty. Make it required, so every image on the section is chosen, with its alt text, from the media library.`);
192
+ }
193
+ }
194
+ function lintComponent(node, path, walk) {
195
+ const component = walk.manifest.components[node.component];
196
+ if (!component) {
197
+ error(walk, path, 'component.unknown', `There is no component "${node.component}".`);
198
+ return;
199
+ }
200
+ if (!component.composition.placement.includes('inline')) {
201
+ error(walk, path, 'component.placement', `${node.component} is placed on a ${component.composition.placement.join(' or ')}, not inside a section. Use a primitive, or a component placed inline.`);
202
+ }
203
+ const own = new Map(component.content.fields.map((field) => [field.name, field]));
204
+ const variants = new Set((component.content.variants ?? []).map((variant) => variant.name));
205
+ for (const [name, value] of Object.entries(node.fields)) {
206
+ const here = `${path}.fields.${name}`;
207
+ const field = own.get(name);
208
+ if (!field) {
209
+ error(walk, here, 'component.field', `${node.component} has no field "${name}".`);
210
+ continue;
211
+ }
212
+ if (isBinding(value)) {
213
+ const recipeField = bound(value.field, here, walk);
214
+ if (recipeField && recipeField.type !== field.type) {
215
+ error(walk, here, 'component.fieldType', `${node.component}.${name} is ${field.type}; "${recipeField.name}" is ${recipeField.type}.`);
216
+ }
217
+ continue;
218
+ }
219
+ const literalOk = (field.type === 'boolean' && typeof value === 'boolean') ||
220
+ (field.type === 'select' && typeof value === 'string' && variants.has(value));
221
+ if (!literalOk) {
222
+ error(walk, here, 'component.literal', field.type === 'select'
223
+ ? `"${String(value)}" is not one of ${node.component}'s variants: ${[...variants].join(', ') || 'none declared'}.`
224
+ : `Every word on a recipe comes from its content fields: bind ${node.component}.${name} with { field: "<name>" }.`);
225
+ }
226
+ }
227
+ for (const field of component.content.fields) {
228
+ if (field.required && !(field.name in node.fields)) {
229
+ error(walk, `${path}.fields.${field.name}`, 'component.required', `${node.component}.${field.name} is required.${field.type === 'link' ? ' A link needs a label and a destination.' : ''}`);
230
+ }
231
+ }
232
+ }
233
+ function bound(name, path, walk) {
234
+ const field = walk.fields.get(name);
235
+ if (!field) {
236
+ error(walk, path, 'fields.undeclared', `"${name}" is not one of the recipe's content fields.`);
237
+ return undefined;
238
+ }
239
+ walk.used.add(name);
240
+ return field;
241
+ }
242
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/recipes/lint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type ComponentType, type JSX } from 'react';
2
+ import type { ContentField } from '../generate/fields.js';
3
+ import { type CoerceContext } from '../render/coerce.js';
4
+ import { type Recipe } from './schema.js';
5
+ type AnyComponent = ComponentType<Record<string, unknown>>;
6
+ export interface RenderRecipeProps {
7
+ recipe: Recipe;
8
+ /** The section's content: one value per field the recipe declares. */
9
+ content: Readonly<Record<string, unknown>>;
10
+ /** Primitive name → the component that draws it. */
11
+ primitives: Readonly<Record<string, AnyComponent>>;
12
+ /** Component name → the component, as `RenderBlocks` takes it. */
13
+ registry: Readonly<Record<string, AnyComponent>>;
14
+ /** Component name → its contract's fields, for `coerceBlock`. */
15
+ fields: Readonly<Record<string, ContentField[]>>;
16
+ /** The same context blocks render with: media, links and rich text are the host's. */
17
+ context: CoerceContext;
18
+ }
19
+ export declare function RenderRecipe(props: RenderRecipeProps): JSX.Element | null;
20
+ export {};
21
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/recipes/render.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,aAAa,EAAE,KAAK,GAAG,EAAkB,MAAM,OAAO,CAAA;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,EAA8B,KAAK,MAAM,EAAmB,MAAM,aAAa,CAAA;AAmBtF,KAAK,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAE1D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC1C,oDAAoD;IACpD,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAA;IAClD,kEAAkE;IAClE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAA;IAChD,iEAAiE;IACjE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;IAChD,sFAAsF;IACtF,OAAO,EAAE,aAAa,CAAA;CACvB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAEzE"}
@@ -0,0 +1,58 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Fragment } from 'react';
3
+ import { coerceBlock } from '../render/coerce.js';
4
+ import { isBinding, isComponentNode } from './schema.js';
5
+ export function RenderRecipe(props) {
6
+ return _jsx(_Fragment, { children: draw(props.recipe.tree, props, 'r') });
7
+ }
8
+ function isEmpty(value) {
9
+ return value === undefined || value === null || value === '';
10
+ }
11
+ function draw(node, ctx, key) {
12
+ if (isComponentNode(node)) {
13
+ const Component = ctx.registry[node.component];
14
+ const contractFields = ctx.fields[node.component];
15
+ if (!Component || !contractFields)
16
+ return null;
17
+ const data = {};
18
+ for (const [name, value] of Object.entries(node.fields)) {
19
+ data[name] = isBinding(value) ? ctx.content[value.field] : value;
20
+ }
21
+ return _jsx(Component, { ...coerceBlock(node.component, contractFields, data, ctx.context) }, key);
22
+ }
23
+ const Primitive = ctx.primitives[node.primitive];
24
+ if (!Primitive)
25
+ return null;
26
+ const props = { ...node.props };
27
+ if (node.content) {
28
+ const value = ctx.content[node.content.field];
29
+ if (isEmpty(value))
30
+ return null;
31
+ const field = ctx.recipe.contract.content.fields.find((f) => f.name === node.content.field);
32
+ switch (field?.type) {
33
+ case 'image': {
34
+ const media = ctx.context.resolveMedia(value, {
35
+ component: ctx.recipe.contract.name,
36
+ path: node.content.field,
37
+ });
38
+ if (!media?.url)
39
+ return null;
40
+ return _jsx(Primitive, { ...props, src: media.url, alt: media.alt ?? '' }, key);
41
+ }
42
+ case 'richtext':
43
+ return (_jsx(Primitive, { ...props, children: ctx.context.renderRichText(value) }, key));
44
+ default:
45
+ return _jsx(Primitive, { ...props, children: String(value) }, key);
46
+ }
47
+ }
48
+ const slots = node.slots ?? {};
49
+ for (const [name, children] of Object.entries(slots)) {
50
+ const drawn = children.map((child, index) => draw(child, ctx, `${key}.${name}${index}`));
51
+ if (name === 'children')
52
+ continue;
53
+ props[name] = _jsx(Fragment, { children: drawn });
54
+ }
55
+ const children = (slots['children'] ?? []).map((child, index) => draw(child, ctx, `${key}.c${index}`));
56
+ return (_jsx(Primitive, { ...props, children: children.length > 0 ? children : undefined }, key));
57
+ }
58
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/recipes/render.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ import type { ComponentContractSchema } from '../contract/schema.js';
3
+ /** A reference to one of the recipe's own content fields. */
4
+ export interface Binding {
5
+ field: string;
6
+ }
7
+ export interface PrimitiveNode {
8
+ primitive: string;
9
+ props?: Record<string, string | number | boolean> | undefined;
10
+ /** A layout primitive's children, by slot name; `children` is the unnamed one. */
11
+ slots?: Record<string, RecipeNode[]> | undefined;
12
+ /** A content primitive's field. */
13
+ content?: Binding | undefined;
14
+ }
15
+ export interface ComponentNode {
16
+ component: string;
17
+ /**
18
+ * The component's content fields, by name. A text-like field is always a
19
+ * binding; a `select` or `boolean` may be a literal, because choosing a
20
+ * variant is arrangement rather than content.
21
+ */
22
+ fields: Record<string, Binding | string | number | boolean>;
23
+ }
24
+ export type RecipeNode = PrimitiveNode | ComponentNode;
25
+ export interface Recipe {
26
+ contract: z.input<typeof ComponentContractSchema>;
27
+ tree: RecipeNode;
28
+ }
29
+ export declare const RecipeNodeSchema: z.ZodType<RecipeNode>;
30
+ /**
31
+ * The shape of a recipe. Strict throughout: a key the shape does not name —
32
+ * `className`, `style`, `html` — is refused rather than ignored, because an
33
+ * ignored key is one an author believes did something.
34
+ */
35
+ export declare const RecipeSchema: z.ZodObject<{
36
+ contract: z.ZodRecord<z.ZodString, z.ZodUnknown>;
37
+ tree: z.ZodType<RecipeNode, z.ZodTypeDef, RecipeNode>;
38
+ }, "strict", z.ZodTypeAny, {
39
+ contract: Record<string, unknown>;
40
+ tree: RecipeNode;
41
+ }, {
42
+ contract: Record<string, unknown>;
43
+ tree: RecipeNode;
44
+ }>;
45
+ export declare function isBinding(value: unknown): value is Binding;
46
+ export declare function isComponentNode(node: RecipeNode): node is ComponentNode;
47
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/recipes/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AA2BpE,6DAA6D;AAC7D,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAAA;IAC7D,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,GAAG,SAAS,CAAA;IAChD,mCAAmC;IACnC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CAC5D;AAED,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,aAAa,CAAA;AAEtD,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;IACjD,IAAI,EAAE,UAAU,CAAA;CACjB;AAMD,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAiBlD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY;;;;;;;;;EAMd,CAAA;AAEX,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAE1D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,IAAI,aAAa,CAEvE"}
@@ -0,0 +1,38 @@
1
+ import { z } from 'zod';
2
+ const BindingSchema = z.object({ field: z.string().min(1) }).strict();
3
+ const PropValueSchema = z.union([z.string(), z.number(), z.boolean()]);
4
+ export const RecipeNodeSchema = z.lazy(() => z.union([
5
+ z
6
+ .object({
7
+ primitive: z.string().min(1),
8
+ props: z.record(z.string(), PropValueSchema).optional(),
9
+ slots: z.record(z.string(), z.array(RecipeNodeSchema)).optional(),
10
+ content: BindingSchema.optional(),
11
+ })
12
+ .strict(),
13
+ z
14
+ .object({
15
+ component: z.string().min(1),
16
+ fields: z.record(z.string(), z.union([BindingSchema, PropValueSchema])),
17
+ })
18
+ .strict(),
19
+ ]));
20
+ /**
21
+ * The shape of a recipe. Strict throughout: a key the shape does not name —
22
+ * `className`, `style`, `html` — is refused rather than ignored, because an
23
+ * ignored key is one an author believes did something.
24
+ */
25
+ export const RecipeSchema = z
26
+ .object({
27
+ // Checked in full by the lint, against ComponentContractSchema.
28
+ contract: z.record(z.string(), z.unknown()),
29
+ tree: RecipeNodeSchema,
30
+ })
31
+ .strict();
32
+ export function isBinding(value) {
33
+ return Boolean(value) && typeof value === 'object' && typeof value.field === 'string';
34
+ }
35
+ export function isComponentNode(node) {
36
+ return 'component' in node;
37
+ }
38
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/recipes/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA2DvB,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;AAErE,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;AAEtE,MAAM,CAAC,MAAM,gBAAgB,GAA0B,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CACjE,CAAC,CAAC,KAAK,CAAC;IACN,CAAC;SACE,MAAM,CAAC;QACN,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5B,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,QAAQ,EAAE;QACvD,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE;QACjE,OAAO,EAAE,aAAa,CAAC,QAAQ,EAAE;KAClC,CAAC;SACD,MAAM,EAAE;IACX,CAAC;SACE,MAAM,CAAC;QACN,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC;KACxE,CAAC;SACD,MAAM,EAAE;CACZ,CAAC,CACH,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC;KAC1B,MAAM,CAAC;IACN,gEAAgE;IAChE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3C,IAAI,EAAE,gBAAgB;CACvB,CAAC;KACD,MAAM,EAAE,CAAA;AAEX,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAQ,KAAiB,CAAC,KAAK,KAAK,QAAQ,CAAA;AACpG,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,OAAO,WAAW,IAAI,IAAI,CAAA;AAC5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forumone/throughline-design-system",
3
- "version": "1.6.0",
3
+ "version": "1.7.0-next-e473c47",
4
4
  "description": "The design-system side of Throughline: the component contract and manifest a design system publishes, its linter, and the Payload blocks, rendering and admin components generated from it.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -20,6 +20,10 @@
20
20
  "types": "./dist/render/index.d.ts",
21
21
  "default": "./dist/render/index.js"
22
22
  },
23
+ "./recipes": {
24
+ "types": "./dist/recipes/index.d.ts",
25
+ "default": "./dist/recipes/index.js"
26
+ },
23
27
  "./client": {
24
28
  "types": "./dist/client.d.ts",
25
29
  "default": "./dist/client.js"