@yahoo/uds-create-config 4.12.1 → 4.14.0

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.
@@ -1,5 +1,5 @@
1
- import { canonicalCondition, isRef, kindOf, memberOf, pathOf, ref, refLeaf, refSchema, splitRef } from "../../framework/utils/refs.js";
2
- import { childrenPolicy } from "./element.js";
1
+ import { canonicalCondition, isRef, kindOf, pathOf, ref, refLeaf, refSchema, splitRef } from "../../framework/utils/refs.js";
2
+ import { childrenPolicy, forwardedPropName } from "./element.js";
3
3
  import { reject, rejectData } from "../../framework/rejection.js";
4
4
  import { isPlainObject } from "../../framework/utils/field-path.js";
5
5
  import { pathInput } from "../../framework/schemas.js";
@@ -213,14 +213,6 @@ const Layer = defineSubEntity({
213
213
  * name this component, so a rename cascades and a delete is refused. `'*'` holds no ref and so
214
214
  * cannot rot — it tracks a rename for free.
215
215
  */
216
- /** The prop name a forwarding member ref identifies, or `undefined` for any other ref shape. */
217
- function forwardedPropName(value) {
218
- if (!isRef(value) || kindOf(value.__ref) !== "component") return void 0;
219
- const member = memberOf(value);
220
- if (!member?.startsWith("props/")) return void 0;
221
- const name = member.slice(6);
222
- return name && !name.includes("/") ? name : void 0;
223
- }
224
216
  const forwardedPropRef = refSchema.check((ctx) => {
225
217
  if (forwardedPropName(ctx.value) !== void 0) return;
226
218
  ctx.issues.push({
@@ -1033,7 +1025,22 @@ const stateCondition = z.strictObject({
1033
1025
  ...test,
1034
1026
  $state: test.$state.startsWith("/") ? test.$state : `/${test.$state}`
1035
1027
  }));
1036
- const orCondition = z.strictObject({ $or: z.array(z.union([stateCondition, z.array(stateCondition)])) });
1028
+ const itemCondition = z.strictObject({
1029
+ $item: z.string(),
1030
+ ...comparisonOperands,
1031
+ not: z.literal(true).optional()
1032
+ });
1033
+ const indexCondition = z.strictObject({
1034
+ $index: z.literal(true),
1035
+ ...comparisonOperands,
1036
+ not: z.literal(true).optional()
1037
+ });
1038
+ const singleCondition = z.union([
1039
+ stateCondition,
1040
+ itemCondition,
1041
+ indexCondition
1042
+ ]);
1043
+ const orCondition = z.strictObject({ $or: z.array(z.union([singleCondition, z.array(singleCondition)])) });
1037
1044
  /**
1038
1045
  * The flat condition an editor collects — `{ variant: 'solid' }` — compiled to the grammar above.
1039
1046
  *
@@ -1072,9 +1079,9 @@ const flatCondition = z.record(z.string(), z.union([
1072
1079
  return conditions.length === 1 ? conditions[0] : conditions;
1073
1080
  });
1074
1081
  const visibility = z.union([
1075
- stateCondition,
1082
+ singleCondition,
1076
1083
  orCondition,
1077
- z.array(z.union([stateCondition, orCondition])),
1084
+ z.array(z.union([singleCondition, orCondition])),
1078
1085
  z.literal(false),
1079
1086
  flatCondition
1080
1087
  ], { error: "a condition is `{ \"$state\": \"<prop>\" }` for a prop that is set, `{ \"$state\": \"<prop>\", \"eq\": \"<value>\" }` for one value of it (`neq`, `gt`, `gte`, `lt`, `lte` and `not: true` also work), `{ \"$or\": [ … ] }` for any of several, a list of conditions that must all hold, or `false` for a position only an interaction shows" });
@@ -1232,6 +1239,10 @@ const AnatomyNode = defineSubEntity({
1232
1239
  values: anatomyPropValue
1233
1240
  }) : z.record(z.string(), anatomyPropValue)).optional().describe("Props passed to what the node renders, keyed by prop or style-property name, as literal values. Content is not set here: a `slot` prop's content lands where a `{ \"element\": \"Slot\", \"props\": { \"name\": \"<prop>\" } }` node sits among a parent's `children` (on the root when there is none), and a `forward` prop (`{ \"type\": \"forward\", \"target\": \"label/children\" }`) routes it into that layer with no node entry. `$state` belongs to `visible` and to an `element` a prop chooses."),
1234
1241
  text: z.string().optional().describe("Literal text content, for a node that renders a fixed string."),
1242
+ repeat: z.strictObject({
1243
+ source: z.unknown(),
1244
+ group: z.string().optional()
1245
+ }).optional().describe("The node renders once per item of a list: `source` is a prop binding (`{ \"$state\": \"/stops\" }`) or a computed value (`{ \"$computed\": \"<function ref>\", \"args\": { … } }`). Inside the node, `{ \"$item\": \"<field>\" }` reads the current item (`\"\"` for the whole item) and `{ \"$index\": true }` its position."),
1235
1246
  on: nodeActions.optional().describe("What an event on the node does to the state model, in json-render's `on` grammar: `{ \"press\": [{ \"action\": \"setState\", \"params\": { \"statePath\": \"/checked\", \"value\": … } }] }`. Read off the render's own handlers; a preview runs them so the composition responds as the component does.")
1236
1247
  };
1237
1248
  if (!config || !body) return z.object({
@@ -1,17 +1,6 @@
1
1
  import { Config } from "../../framework/Config.js";
2
2
 
3
3
  //#region src/entities/system/element.d.ts
4
- /**
5
- * A component's forwarded layers, as `{ [layer]: '*' | Ref[] }` — the ONE reader of the field.
6
- *
7
- * A field read directly is a field that has to be found again when its shape changes; there were six
8
- * such callers once, and three read it raw.
9
- *
10
- * It used to fall back to `inherits`, the pre-migration spelling, read as `{ [layer]: '*' }`. That
11
- * reader is gone: `v1-artifact` folds a v1 config straight to `forwards`, so nothing mints the old
12
- * field, and a stored config still carrying it now forwards nothing rather than being quietly
13
- * translated. Deliberate — one name for one concept beats two where the second is invisible.
14
- */
15
4
  declare function forwardsOf(body: {
16
5
  forwards?: Record<string, unknown> | undefined;
17
6
  }): Record<string, unknown>;
@@ -1,4 +1,4 @@
1
- import { memberOf, splitRef } from "../../framework/utils/refs.js";
1
+ import { isRef, kindOf, memberOf, splitRef } from "../../framework/utils/refs.js";
2
2
  //#region src/entities/system/element.ts
3
3
  /** The path a value points at when it's a ref to a component, else `undefined`. */
4
4
  function componentPathOf(value) {
@@ -18,6 +18,15 @@ function componentPathOf(value) {
18
18
  * field, and a stored config still carrying it now forwards nothing rather than being quietly
19
19
  * translated. Deliberate — one name for one concept beats two where the second is invisible.
20
20
  */
21
+ /** The prop name a forwarding member ref identifies (`component:Input#props/disabled` → `disabled`),
22
+ * or `undefined` for any other ref shape. */
23
+ function forwardedPropName(value) {
24
+ if (!isRef(value) || kindOf(value.__ref) !== "component") return void 0;
25
+ const member = memberOf(value);
26
+ if (!member?.startsWith("props/")) return void 0;
27
+ const name = member.slice(6);
28
+ return name && !name.includes("/") ? name : void 0;
29
+ }
21
30
  function forwardsOf(body) {
22
31
  return body.forwards ?? {};
23
32
  }
@@ -170,4 +179,4 @@ function partsOf(config, path) {
170
179
  return config.list("component").filter((item) => ownerOf(config, item.path) === path).map((item) => item.path);
171
180
  }
172
181
  //#endregion
173
- export { VOID_ELEMENTS, childrenPolicy, forwardsOf, ownerOf, partsOf, renderedElement, renderedTarget, rootLayerOf };
182
+ export { VOID_ELEMENTS, childrenPolicy, forwardedPropName, forwardsOf, ownerOf, partsOf, renderedElement, renderedTarget, rootLayerOf };
@@ -72,7 +72,7 @@ interface RecordedSource {
72
72
  * The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
73
73
  * that replaced it (see `detectedWireVersion`).
74
74
  */
75
- declare const SERIALIZED_CONFIG_VERSION = 20260923005318;
75
+ declare const SERIALIZED_CONFIG_VERSION = 20260923112910;
76
76
  interface SerializedConfig {
77
77
  /** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
78
78
  readonly version: number;
@@ -4836,7 +4836,7 @@ var Config = class Config {
4836
4836
  * links from the provided sources. A declared `linkedKind` with no provided source throws. */
4837
4837
  hydrateFrom(stored, options) {
4838
4838
  const claimed = schemaVersionOf(stored);
4839
- if (claimed > 20260923005318) throw new SchemaVersionTooNew(claimed);
4839
+ if (claimed > 20260923112910) throw new SchemaVersionTooNew(claimed);
4840
4840
  const wire = upgradeSerializedConfig(stored);
4841
4841
  if (!Array.isArray(wire.ownedKinds) || typeof wire.items !== "object" || wire.items === null) throw new ConfigFormatError(`Config "${stored.name ?? "system-config"}" cannot be hydrated: this is not a serialized config-v2 config (no "ownedKinds"/"items"). A pre-cutover build artifact converts through the registered cutover migration — import the config type's module (e.g. \`configs/system\`) before hydrating, or port the repo once with \`uds migrate\`.`);
4842
4842
  const leftover = wire.options;
@@ -1,5 +1,5 @@
1
1
  import { isRef, kindOf, memberOf, pathOf, ref, refLeaf, sourceOf, splitRef } from "./utils/refs.js";
2
- import { forwardsOf } from "../entities/system/element.js";
2
+ import { forwardedPropName, forwardsOf } from "../entities/system/element.js";
3
3
  import { classPrefixOf, varPrefixOf } from "../entities/system/Settings.js";
4
4
  import { isDerivedColor, isGradient, renderDerivedColor, renderGradient } from "../entities/system/color.js";
5
5
  import { fontFamilyStack } from "../entities/system/Font.js";
@@ -799,13 +799,22 @@ function computeComponentProps(config, path, seen) {
799
799
  const element = body.layers?.[layer];
800
800
  if (!isRef(element)) continue;
801
801
  const exposed = resolveComponentProps(config, pathOf(element.__ref), new Set(seen));
802
- const taken = entry === "*" ? exposed : Object.fromEntries((Array.isArray(entry) ? entry : []).map((r) => isRef(r) ? memberOf(r)?.split("/").pop() : void 0).filter((name) => !!name && name in exposed).map((name) => [name, exposed[name]]));
803
- for (const [name, decl] of Object.entries(taken)) if (!(name in merged)) merged[name] = decl;
802
+ for (const [name, decl] of Object.entries(takenByForward({
803
+ entry,
804
+ exposed
805
+ }))) if (!(name in merged)) merged[name] = decl;
804
806
  }
805
807
  for (const [name, prop] of Object.entries(body.props ?? {})) if (prop === null) delete merged[name];
806
808
  else merged[name] = prop;
807
809
  return merged;
808
810
  }
811
+ /** The props a `forwards` entry takes from what its element exposes: `'*'` is the whole surface, a
812
+ * list is an allowlist of the names it spells. A list never subtracts, which is what a `null` own
813
+ * prop is for. */
814
+ function takenByForward({ entry, exposed }) {
815
+ if (entry === "*") return exposed;
816
+ return Object.fromEntries((Array.isArray(entry) ? entry : []).map(forwardedPropName).filter((name) => !!name && name in exposed).map((name) => [name, exposed[name]]));
817
+ }
809
818
  /**
810
819
  * Which component actually declared one of a component's resolved props — its ultimate owner.
811
820
  *
@@ -835,7 +844,7 @@ function propOwner(config, path, prop, seen = /* @__PURE__ */ new Set()) {
835
844
  for (const [layer, entry] of Object.entries(forwardsOf(body))) {
836
845
  const element = body.layers?.[layer];
837
846
  if (!isRef(element)) continue;
838
- if (Array.isArray(entry) && !entry.some((r) => isRef(r) && memberOf(r)?.split("/").pop() === prop)) continue;
847
+ if (Array.isArray(entry) && !entry.some((r) => forwardedPropName(r) === prop)) continue;
839
848
  const owner = propOwner(config, pathOf(element.__ref), prop, new Set(seen));
840
849
  if (owner) return owner;
841
850
  }
@@ -1210,4 +1219,4 @@ function declaredPropDefaults(props) {
1210
1219
  return out;
1211
1220
  }
1212
1221
  //#endregion
1213
- export { authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, glyphPropsOfBody, holdingPreviewConditions, iconToolAxisOptions, iconToolOf, keyedPreviewConditions, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, refListOf, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, validateComponentProps };
1222
+ export { authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, glyphPropsOfBody, holdingPreviewConditions, iconToolAxisOptions, iconToolOf, keyedPreviewConditions, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, refListOf, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, takenByForward, validateComponentProps };
@@ -147,7 +147,30 @@ interface ForwardClaims {
147
147
  readonly own: ReadonlySet<string>;
148
148
  /** Forwarded name → the layer whose entry delivers it. The first entry in key order wins an overlap. */
149
149
  readonly forwarded: ReadonlyMap<string, string>;
150
+ /** Own declarations that change the type of a name a forwards entry offers, one row per prop. */
151
+ readonly shadowed: readonly ShadowedForward[];
152
+ }
153
+ interface ShadowedForward {
154
+ readonly component: string;
155
+ readonly prop: string;
156
+ /** The layer whose entry would have delivered the prop, and that layer's element with its source. */
157
+ readonly layer: string;
158
+ readonly element: string;
159
+ /** How this component declares the prop (`variant`, `string`, …). */
160
+ readonly declared: string;
161
+ /** How the layer's element declares it. */
162
+ readonly forwarded: string;
150
163
  }
151
164
  declare function forwardClaims(config: Config, componentPath: string): ForwardClaims;
165
+ /** A component's own props that redeclare, as another type, a name a `forwards` entry offers. The
166
+ * own declaration wins, so the element's prop of that name is unreachable through this component
167
+ * and a call site's value means something else than it does on the element. */
168
+ declare function shadowedForwards({
169
+ config,
170
+ path
171
+ }: {
172
+ readonly config: Config;
173
+ readonly path: string;
174
+ }): ShadowedForward[];
152
175
  //#endregion
153
- export { ForwardClaims, SurfaceCompositeProp, SurfaceProp, SurfacePropCondition, SurfacePropKind, SurfaceScalarProp, SurfaceSlotProp, SurfaceStylePropertyProp, SurfaceVariantProp, forwardClaims, forwardedLayers, intoName, propApplies, propRegistryRef, routedProp, routedPropIn, routesContent, surfaceProp, surfaceProps };
176
+ export { ForwardClaims, ShadowedForward, SurfaceCompositeProp, SurfaceProp, SurfacePropCondition, SurfacePropKind, SurfaceScalarProp, SurfaceSlotProp, SurfaceStylePropertyProp, SurfaceVariantProp, forwardClaims, forwardedLayers, intoName, propApplies, propRegistryRef, routedProp, routedPropIn, routesContent, shadowedForwards, surfaceProp, surfaceProps };
@@ -1,6 +1,6 @@
1
- import { isRef, kindOf, memberOf, pathOf, refLeaf } from "./utils/refs.js";
1
+ import { isRef, kindOf, memberOf, pathOf, refLeaf, splitRef } from "./utils/refs.js";
2
2
  import { forwardsOf, rootLayerOf } from "../entities/system/element.js";
3
- import { propValueDomain, refListOf, resolveComponentProps } from "./projections.js";
3
+ import { propValueDomain, refListOf, resolveComponentProps, takenByForward } from "./projections.js";
4
4
  import { Component } from "../entities/system/Component.js";
5
5
  //#region src/framework/prop-surface.ts
6
6
  /**
@@ -302,9 +302,12 @@ function computeForwardClaims(config, componentPath) {
302
302
  const body = Component.get(config, componentPath)?.toJSON();
303
303
  const own = /* @__PURE__ */ new Set();
304
304
  const forwarded = /* @__PURE__ */ new Map();
305
+ const shadowed = [];
306
+ const seenShadow = /* @__PURE__ */ new Set();
305
307
  if (!body) return {
306
308
  own,
307
309
  forwarded,
310
+ shadowed,
308
311
  rootLayer: void 0
309
312
  };
310
313
  const { layers = {}, props = {}, forwards } = body;
@@ -313,19 +316,42 @@ function computeForwardClaims(config, componentPath) {
313
316
  for (const [layer, entry] of Object.entries(forwardsOf({ forwards }))) {
314
317
  const element = layers[layer];
315
318
  if (!isRef(element)) continue;
316
- const exposed = resolveComponentProps(config, pathOf(element.__ref));
317
- const names = entry === "*" ? Object.keys(exposed) : (Array.isArray(entry) ? entry : []).map((ref) => isRef(ref) ? memberOf(ref)?.split("/").pop() : void 0).filter((name) => !!name && name in exposed);
318
- for (const name of names) {
319
- if (own.has(name) || forwarded.has(name)) continue;
319
+ const taken = takenByForward({
320
+ entry,
321
+ exposed: resolveComponentProps(config, pathOf(element.__ref))
322
+ });
323
+ for (const [name, theirs] of Object.entries(taken)) {
324
+ if (own.has(name)) {
325
+ if (seenShadow.has(name)) continue;
326
+ seenShadow.add(name);
327
+ const declared = declaredTypeOf(props[name]);
328
+ const forwardedType = declaredTypeOf(theirs);
329
+ if (props[name] === null || declared === "forward" || declared === forwardedType) continue;
330
+ shadowed.push({
331
+ component: componentPath,
332
+ prop: name,
333
+ layer,
334
+ element: splitRef(element.__ref).qualifiedPath,
335
+ declared,
336
+ forwarded: forwardedType
337
+ });
338
+ continue;
339
+ }
340
+ if (forwarded.has(name)) continue;
320
341
  forwarded.set(name, layer);
321
342
  }
322
343
  }
323
344
  return {
324
345
  own,
325
346
  forwarded,
347
+ shadowed,
326
348
  rootLayer
327
349
  };
328
350
  }
351
+ function declaredTypeOf(decl) {
352
+ const type = decl?.type;
353
+ return typeof type === "string" ? type : "unknown";
354
+ }
329
355
  /**
330
356
  * Each non-root layer a component forwards to, and the sorted prop names each one receives — the
331
357
  * emitter's view, which writes a literal key set per layer. Root is absent on purpose: it takes
@@ -344,11 +370,18 @@ function forwardedLayers(config, componentPath) {
344
370
  return out;
345
371
  }
346
372
  function forwardClaims(config, componentPath) {
347
- const { own, forwarded } = computeForwardClaims(config, componentPath);
373
+ const { own, forwarded, shadowed } = computeForwardClaims(config, componentPath);
348
374
  return {
349
375
  own,
350
- forwarded
376
+ forwarded,
377
+ shadowed
351
378
  };
352
379
  }
380
+ /** A component's own props that redeclare, as another type, a name a `forwards` entry offers. The
381
+ * own declaration wins, so the element's prop of that name is unreachable through this component
382
+ * and a call site's value means something else than it does on the element. */
383
+ function shadowedForwards({ config, path }) {
384
+ return computeForwardClaims(config, path).shadowed;
385
+ }
353
386
  //#endregion
354
- export { forwardClaims, forwardedLayers, intoName, propApplies, propRegistryRef, routedProp, routedPropIn, routesContent, routesIntoText, surfaceProp, surfaceProps };
387
+ export { forwardClaims, forwardedLayers, intoName, propApplies, propRegistryRef, routedProp, routedPropIn, routesContent, routesIntoText, shadowedForwards, surfaceProp, surfaceProps };
@@ -1,5 +1,5 @@
1
1
  import { Config } from "./Config.js";
2
- import { StateCondition } from "@json-render/core";
2
+ import { ComputedFunction, StateCondition } from "@json-render/core";
3
3
 
4
4
  //#region src/framework/render-spec.d.ts
5
5
  /** A node in a flat render spec — json-render's own shape, which is what a canvas paints.
@@ -30,6 +30,44 @@ interface ComponentSpecs {
30
30
  readonly render?: RenderSpec;
31
31
  readonly preview?: RenderSpec;
32
32
  }
33
+ /**
34
+ * A node the render writes once per item of a list (`visiblePages.map((item, index) => …)`).
35
+ * `source` is the list: a prop's binding (`{ $state: '/stops' }`) or a value a module-scope function
36
+ * computes from props (`{ $computed, args }`). `group` names the `.map` the node came from, so the
37
+ * arms of one map (`item === 'ellipsis' ? <Gap/> : <Cell/>`) unroll together in item order. Inside
38
+ * the node, `{ $item: '<field>' }` reads the current item (`''` for the whole item) and
39
+ * `{ $index: true }` its position, in json-render's own grammar. Where the expansion cannot settle
40
+ * the list, the node emits once as the example row.
41
+ */
42
+ interface RepeatSpec {
43
+ readonly source: unknown;
44
+ readonly group?: string;
45
+ }
46
+ /** A value a module-scope function computes at paint time from values the cell supplies: json-render's
47
+ * `$computed`, naming the function by ref (`file:src/…/pagination.tsx#computeVisiblePages`). `args`
48
+ * are keyed by the function's parameter names and resolved before the call. */
49
+ interface ComputedValue {
50
+ readonly $computed: string;
51
+ readonly args?: Record<string, unknown>;
52
+ }
53
+ /** The functions `$computed` values call, by ref — what a preview bundle registers and an
54
+ * expansion or renderer is handed. */
55
+ type ComputedFunctions = Record<string, ComputedFunction>;
56
+ /** The current item of a repeated node while its row is emitted. */
57
+ interface RepeatRow {
58
+ readonly item: unknown;
59
+ readonly index: number;
60
+ }
61
+ /** What settling a value needs beyond the prop bag: the functions `$computed` may call and, inside
62
+ * a repeated node, the current row. */
63
+ interface ResolveContext {
64
+ readonly functions?: ComputedFunctions;
65
+ readonly row?: RepeatRow;
66
+ /** Read a bare `$state` binding as its value. Off for an element's props, where a binding is
67
+ * left for the renderer (and where fill routing reads it); on inside a `$computed`'s arguments
68
+ * and a repeat's source, which have to be values before the call. */
69
+ readonly settle?: boolean;
70
+ }
33
71
  /**
34
72
  * The registry key of an element rendered THROUGH a package factory — `motion.create(Box)`, spelled
35
73
  * `package:motion/react#motion(component:Box)`.
@@ -165,6 +203,9 @@ interface ExpandOptions {
165
203
  * with no registry to consult gets.
166
204
  */
167
205
  readonly rendered?: ReadonlySet<string>;
206
+ /** The functions `$computed` values call. Absent, a computed value stays on the element for the
207
+ * renderer, and a node repeated over a computed list emits once. */
208
+ readonly functions?: ComputedFunctions;
168
209
  }
169
210
  interface ExpansionResult {
170
211
  readonly spec: RenderSpec;
@@ -190,7 +231,8 @@ interface ExpansionResult {
190
231
  * A condition that does not resolve is left exactly as it was, so nothing is claimed about it.
191
232
  */
192
233
  declare function specWithResolvedVisibility(spec: RenderSpec, state: Record<string, unknown>, options?: {
193
- /** Props the renderer keeps deciding at paint time — a choice reading one is left as it is. */readonly live?: ReadonlySet<string>;
234
+ /** Props the renderer keeps deciding at paint time — a choice reading one is left as it is. */readonly live?: ReadonlySet<string>; /** The functions `$computed` values call; absent, a computed value is left for the renderer. */
235
+ readonly functions?: ComputedFunctions;
194
236
  }): RenderSpec;
195
237
  /** The comparison a `$state` test can make: json-render's own operator set, minus the state pointer
196
238
  * and the `not` modifier. Typed off the library so an operator it adds is a typecheck failure here
@@ -254,7 +296,14 @@ declare function anatomyElementComponent({
254
296
  }): string | undefined;
255
297
  /** A value in json-render's `visible` grammar, resolved against a prop bag. `undefined` when this
256
298
  * reader can't settle it, which leaves the condition on the element for the renderer to judge. */
257
- declare function resolveVisibility(condition: unknown, state: Record<string, unknown>): boolean | undefined;
299
+ declare function resolveVisibility(condition: unknown, state: Record<string, unknown>, context?: ResolveContext): boolean | undefined;
300
+ /**
301
+ * A value settled against a prop bag as far as this reader can: a `$state` reads the bag, `$item`
302
+ * and `$index` the current row, `$cond` picks its arm, `$computed` calls its function when the
303
+ * expansion has it and every argument settled, and a plain object or list settles member by member.
304
+ * Whatever cannot be settled is returned as written, for the renderer.
305
+ */
306
+ declare function resolveValue(value: unknown, state: Record<string, unknown>, context?: ResolveContext): unknown;
258
307
  /**
259
308
  * Which PROPS a `visible` condition reads, in the order they appear.
260
309
  *
@@ -424,4 +473,4 @@ declare function expandSpec(config: Config, spec: RenderSpec, options?: ExpandOp
424
473
  */
425
474
  declare function expandComponent(config: Config, path: string, props?: Record<string, unknown>, options?: ExpandOptions): ExpansionResult;
426
475
  //#endregion
427
- export { ANATOMY_MARKER, ComponentSpec, ComponentSpecs, ExpandOptions, ExpansionResult, RenderElement, RenderSpec, SlotTarget, VisibilityOperator, VisibilityTerm, anatomyElementComponent, anatomyTextProp, boundElementType, componentSpec, deriveSpec, entryKeyOf, expandComponent, expandSpec, instanceSpec, layerTextProp, liveStateProps, previewSpec, propInto, propsIntoLayer, readStatePointer, resolveVisibility, routeOf, slotTargetsOf, specWithResolvedVisibility, statePointerProp, valueForwardsOf, visibilityStateProps, visibilityTerms, withoutTruthyTerm, wrappedElementKey };
476
+ export { ANATOMY_MARKER, ComponentSpec, ComponentSpecs, ComputedFunctions, ComputedValue, ExpandOptions, ExpansionResult, RenderElement, RenderSpec, RepeatRow, RepeatSpec, ResolveContext, SlotTarget, VisibilityOperator, VisibilityTerm, anatomyElementComponent, anatomyTextProp, boundElementType, componentSpec, deriveSpec, entryKeyOf, expandComponent, expandSpec, instanceSpec, layerTextProp, liveStateProps, previewSpec, propInto, propsIntoLayer, readStatePointer, resolveValue, resolveVisibility, routeOf, slotTargetsOf, specWithResolvedVisibility, statePointerProp, valueForwardsOf, visibilityStateProps, visibilityTerms, withoutTruthyTerm, wrappedElementKey };