@yahoo/uds-create-config 5.54.0 → 5.55.1

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.
Files changed (51) hide show
  1. package/dist/capture/node-ops.js +1 -1
  2. package/dist/configs/system.js +2 -0
  3. package/dist/entities/canvas/node-gestures.js +1 -1
  4. package/dist/entities/canvas/node-patches.js +1 -1
  5. package/dist/entities/system/Component.d.ts +5 -2
  6. package/dist/entities/system/Component.js +142 -16
  7. package/dist/entities/system/LinkedSystem.js +2 -2
  8. package/dist/entities/system/Operation.js +1 -1
  9. package/dist/entities/system/component-forwards.js +1 -1
  10. package/dist/entities/system/component-module.js +9 -9
  11. package/dist/entities/system/component-source-fields.js +1 -1
  12. package/dist/entities/system/component-style-set.js +1 -1
  13. package/dist/entities/system/conditional-defaults.d.ts +45 -0
  14. package/dist/entities/system/conditional-defaults.js +142 -0
  15. package/dist/entities/system/copy-plan.js +1 -1
  16. package/dist/entities/system/css-motion-reach.js +1 -1
  17. package/dist/entities/system/defineComponent.d.ts +11 -2
  18. package/dist/entities/system/defineComponent.js +3 -0
  19. package/dist/entities/system/link-plan.js +1 -1
  20. package/dist/entities/system/motion-convert.js +1 -1
  21. package/dist/entities/system/motion-variants.js +2 -2
  22. package/dist/framework/Config.d.ts +1 -1
  23. package/dist/framework/Config.js +21 -5
  24. package/dist/framework/class-names.js +18 -6
  25. package/dist/framework/default-conditions.d.ts +26 -0
  26. package/dist/framework/default-conditions.js +35 -0
  27. package/dist/framework/defineEntity.js +1 -1
  28. package/dist/framework/inline-sources.js +1 -1
  29. package/dist/framework/layer-styles.js +25 -3
  30. package/dist/framework/memo.d.ts +2 -2
  31. package/dist/framework/memo.js +7 -1
  32. package/dist/framework/plan.js +1 -1
  33. package/dist/framework/projections.js +18 -5
  34. package/dist/framework/prop-surface.d.ts +1 -0
  35. package/dist/framework/prop-surface.js +13 -0
  36. package/dist/framework/render-spec.d.ts +3 -16
  37. package/dist/framework/render-spec.js +10 -2
  38. package/dist/framework/schema-version.d.ts +1 -1
  39. package/dist/framework/schema-version.js +5 -5
  40. package/dist/framework/schemas.js +2 -2
  41. package/dist/framework/source-integrity.js +1 -1
  42. package/dist/index.d.ts +4 -2
  43. package/dist/index.js +5 -3
  44. package/dist/migrations/20261023152030_conditional_defaults.d.ts +6 -0
  45. package/dist/migrations/20261023152030_conditional_defaults.js +8 -0
  46. package/dist/spec/bake.js +14 -6
  47. package/dist/spec/cell.d.ts +2 -1
  48. package/dist/spec/cell.js +14 -6
  49. package/dist/spec/detach.js +5 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +2 -2
@@ -3,6 +3,7 @@ import { RegisteredComponentHandles, RegisteredGroupPaths, RegisteredModifiers,
3
3
  import { Authored } from "../../framework/authoring.js";
4
4
  import { StyleValue } from "./style-bag.js";
5
5
  import { ComponentSourceInput } from "./component-source-fields.js";
6
+ import { ConditionalDefault } from "./conditional-defaults.js";
6
7
  import { VariantValues } from "./Component.js";
7
8
  import { AllHTMLAttributes, ComponentPropsWithRef, ComponentType, ElementType, JSX, ReactElement, ReactNode, Ref } from "react";
8
9
 
@@ -663,6 +664,11 @@ type ForwardedPropsOf<L extends LayerMap, F extends string> = [NonRootForward<L,
663
664
  type DefaultsOf<P extends PropMap> = Partial<DeclaredProps<P>> & {
664
665
  [key: string]: unknown;
665
666
  };
667
+ /** {@link DefaultsOf} as `.defaultProps` takes it: a declared prop other than `as` may also default
668
+ * through {@link conditionalDefault}. */
669
+ type AuthoredDefaultsOf<P extends PropMap> = { [K in keyof DeclaredProps<P>]?: K extends 'as' ? DeclaredProps<P>[K] : DeclaredProps<P>[K] | ConditionalDefault<DeclaredProps<P>[K]> } & {
670
+ [key: string]: unknown;
671
+ };
666
672
  /**
667
673
  * `T` without the keys in `K` — `Omit` that leaves an index signature alone.
668
674
  *
@@ -778,8 +784,11 @@ F extends string = never, /** The states `.states()` declared — what the rende
778
784
  *
779
785
  * `const`, so an `as` keeps its literal type: without it `{ as: 'input' }` widens to `string` and
780
786
  * there's no tag left to resolve the element's props against.
787
+ *
788
+ * A default that depends on another prop is a `conditionalDefault`:
789
+ * `includeScrim: conditionalDefault(true, [{ when: { modal: false }, value: false }])`.
781
790
  */
782
- defaultProps<const D2 extends DefaultsOf<P>>(defaults: D2): ComponentBuilder<L, P, E, D2, F, S>;
791
+ defaultProps<const D2 extends AuthoredDefaultsOf<P>>(defaults: D2): ComponentBuilder<L, P, E, D2, F, S>;
783
792
  /**
784
793
  * The layers whose element props this component ALSO exposes, keyed by layer.
785
794
  *
@@ -892,4 +901,4 @@ interface DefineComponent {
892
901
  declare function definePropsFirst<const P extends PropMap<LayerPath>>(props: P & NoInfer<{ [K in keyof P]: Exact<P[K], AuthoredProp<LayerPath>> }>): ComponentBuilder<LayerMap, P>;
893
902
  declare const defineComponent: DefineComponent;
894
903
  //#endregion
895
- export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, DefaultsOf, GlyphComponent, GlyphValue, LayerElement, LayerMap, LinkedHandle, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, SlotCodeForm, SlotDecl, StateMap, StyleRuleInput, defineComponent };
904
+ export { AsInput, AuthoredComponent, AuthoredDefaultsOf, AuthoredProp, ComponentBuilder, ComponentPropsOf, GlyphComponent, GlyphValue, LayerElement, LayerMap, LinkedHandle, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, SlotCodeForm, SlotDecl, StateMap, StyleRuleInput, defineComponent };
@@ -2,6 +2,7 @@ import { canonicalCondition, isRef, kindOf, memberRef, pathOf } from "../../fram
2
2
  import { glyphIdentityOf, glyphPackageRefOf, isComponentType } from "./glyph-identity.js";
3
3
  import "../../framework/preview-store.js";
4
4
  import { assertComponentSourceInput } from "./component-source-fields.js";
5
+ import { isConditionalDefault, storedDefaults } from "./conditional-defaults.js";
5
6
  import { Component, layerRef, storedStyleRules } from "./Component.js";
6
7
  //#region src/entities/system/defineComponent.ts
7
8
  function build(body) {
@@ -145,6 +146,7 @@ function storableElement(value) {
145
146
  */
146
147
  function storableValue(value) {
147
148
  if (value === null) return null;
149
+ if (isConditionalDefault(value)) throw new Error("`conditionalDefault` is a default, so it goes in `.defaultProps` or `.extend({ defaultProps })`. A preview conditions its props with `.preview({ when: [{ when, props }] })`.");
148
150
  const type = typeof value;
149
151
  if (type === "string" || type === "number" || type === "boolean") return value;
150
152
  if (isComponentType(value)) return storableComponentRef(value);
@@ -388,6 +390,7 @@ function toStored(body) {
388
390
  if (Object.keys(keyed).length === 0) delete stored.preview;
389
391
  else stored.preview = keyed;
390
392
  }
393
+ Object.assign(stored, storedDefaults(body));
391
394
  if (body.props && typeof body.props === "object") {
392
395
  const props = {};
393
396
  for (const [name, decl] of Object.entries(body.props)) props[name] = storedProp(decl, body.layers);
@@ -1,5 +1,5 @@
1
- import { isConfigRejection, rejectData } from "../../framework/rejection.js";
2
1
  import { collectRefs, isMintedRow, ref, sourceOf, splitRef } from "../../framework/utils/refs.js";
2
+ import { isConfigRejection, rejectData } from "../../framework/rejection.js";
3
3
  import { blockedBy } from "../../framework/plan.js";
4
4
  import { excludedCondition } from "../../framework/source-integrity.js";
5
5
  import { PIN_KIND, covers, declarationOf, entriesFor, excludePaths, excludes, includePaths, narrowDeclaration } from "./link-declaration.js";
@@ -1,5 +1,5 @@
1
- import { isRef } from "../../framework/utils/refs.js";
2
1
  import { isPlainObject } from "../../framework/utils/field-path.js";
2
+ import { isRef } from "../../framework/utils/refs.js";
3
3
  import { MOTION_EASING_NAMES, bezierPoints, motionOfCssKeyword } from "./easings.js";
4
4
  import { cssEasing } from "./css-timing.js";
5
5
  import { JS_ONLY_FIELDS } from "./Motion.js";
@@ -1,6 +1,6 @@
1
- import { reject } from "../../framework/rejection.js";
2
- import { isRef, kindOf, pathOf } from "../../framework/utils/refs.js";
3
1
  import { isPlainObject } from "../../framework/utils/field-path.js";
2
+ import { isRef, kindOf, pathOf } from "../../framework/utils/refs.js";
3
+ import { reject } from "../../framework/rejection.js";
4
4
  import { createSliceMemo } from "../../framework/memo.js";
5
5
  import { drawnMotionBody, drawnMotionName } from "./drawn-motion.js";
6
6
  import { webMotionBody } from "./motion-types.js";
@@ -73,7 +73,7 @@ interface RecordedSource {
73
73
  * The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
74
74
  * that replaced it (see `detectedWireVersion`).
75
75
  */
76
- declare const SERIALIZED_CONFIG_VERSION = 20261022184639;
76
+ declare const SERIALIZED_CONFIG_VERSION = 20261023152030;
77
77
  interface SerializedConfig {
78
78
  /** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
79
79
  readonly version: number;
@@ -1,8 +1,8 @@
1
+ import { applyPathDelta, isPlainObject, namedFields, pathSegments, sectionDelta, touchedFields } from "./utils/field-path.js";
2
+ import { MINTED, collectRefs, entityPathProblem, entityRefOf, isExtendedRef, isMintedRow, isRef, kindOf, memberOf, memberRef, pathOf, qualifyRefs, ref, rewriteRefNamespace, rewriteRefSource, rewriteRefs, routedSource, singletonRef, sourceHops, sourceOf, splitRef } from "./utils/refs.js";
1
3
  import "./config-operation-kind.js";
2
4
  import { ConfigFormatError, ConfigRejection, isConfigRejection, issuePath, refusalIssues, reject, rejectData, rejectInvalid, rejectReferrers } from "./rejection.js";
3
- import { MINTED, collectRefs, entityPathProblem, entityRefOf, isExtendedRef, isMintedRow, isRef, kindOf, memberOf, memberRef, pathOf, qualifyRefs, ref, rewriteRefNamespace, rewriteRefSource, rewriteRefs, routedSource, singletonRef, sourceHops, sourceOf, splitRef } from "./utils/refs.js";
4
5
  import { describePatch } from "./plan.js";
5
- import { applyPathDelta, isPlainObject, namedFields, pathSegments, sectionDelta, touchedFields } from "./utils/field-path.js";
6
6
  import { METADATA_KEYS, declaredField } from "./utils/field-schema.js";
7
7
  import { dataSchema, resolveInputSchema } from "./schemas.js";
8
8
  import { isComposedValue } from "../entities/system/composed-values.js";
@@ -280,6 +280,7 @@ function cachesFor(snapshot, sources) {
280
280
  if (found) return found;
281
281
  const bucket = {
282
282
  sources: current,
283
+ entities: /* @__PURE__ */ new Map(),
283
284
  extensions: /* @__PURE__ */ new Map(),
284
285
  overlays: /* @__PURE__ */ new Map(),
285
286
  lists: /* @__PURE__ */ new Map(),
@@ -438,13 +439,18 @@ function storeCollection(def, patch, next) {
438
439
  }
439
440
  /** Drop every member of a declared `orphans` collection that still points at `target` — what a
440
441
  * delete leaves behind when a sibling member exists only to describe the one removed. */
441
- function dropOrphans(sub, def, target, namespace) {
442
+ function dropOrphans(sub, def, target, namespace, keyed) {
442
443
  if (!sub?.orphans?.length) return def;
443
444
  let out = def;
444
445
  for (const field of sub.orphans) {
445
446
  const collection = out[field];
446
447
  if (!isPlainObject(collection)) continue;
448
+ const keyedFields = Object.entries(keyed?.subEntities[field]?.namesMembersOf ?? {}).filter(([, names]) => names === keyed?.collection).map(([record]) => record);
447
449
  const kept = Object.fromEntries(Object.entries(collection).filter(([, member]) => {
450
+ if (keyed && isPlainObject(member) && keyedFields.some((record) => {
451
+ const names = member[record];
452
+ return isPlainObject(names) && Object.hasOwn(names, keyed.key);
453
+ })) return false;
448
454
  const refs = collectRefs(member);
449
455
  if (refs.includes(target)) return false;
450
456
  if (!namespace) return true;
@@ -2401,6 +2407,10 @@ var Config = class Config {
2401
2407
  def = dropOrphans(sub, def, `${leafOf(patch.field).replace(/s$/, "")}:${refPathOf(patch, patch.key)}`, sub?.namespaces ? {
2402
2408
  kind: sub.namespaces,
2403
2409
  path: patch.key
2410
+ } : void 0, patch.owner === void 0 ? {
2411
+ collection: patch.field,
2412
+ key: patch.key,
2413
+ subEntities: entry.subEntities
2404
2414
  } : void 0);
2405
2415
  }
2406
2416
  const next = snapshot.withChanges([itemChange(entry, patch.kind, patch.path, def)]);
@@ -2907,7 +2917,13 @@ var Config = class Config {
2907
2917
  return new Entity(path, withComputed, [...entry.fieldKeys, ...Object.keys(computed)]);
2908
2918
  }
2909
2919
  resolve(kind, path) {
2910
- return this.resolvedIn(kind, path)?.entity;
2920
+ const cache = this.caches.entities;
2921
+ const key = `${kind}\u0000${path}`;
2922
+ const cached = cache.get(key);
2923
+ if (cached !== void 0) return cached ?? void 0;
2924
+ const resolved = this.resolvedIn(kind, path)?.entity;
2925
+ cache.set(key, resolved ?? null);
2926
+ return resolved;
2911
2927
  }
2912
2928
  /**
2913
2929
  * Whether this config holds `target`'s entity, at the path the source spells it, through `slug`.
@@ -6044,7 +6060,7 @@ var Config = class Config {
6044
6060
  * links from the provided sources. A declared `linkedKind` with no provided source throws. */
6045
6061
  hydrateFrom(stored, options) {
6046
6062
  const claimed = schemaVersionOf(stored);
6047
- if (claimed > 20261022184639) throw new SchemaVersionTooNew(claimed);
6063
+ if (claimed > 20261023152030) throw new SchemaVersionTooNew(claimed);
6048
6064
  const wire = upgradeSerializedConfig(stored);
6049
6065
  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.`);
6050
6066
  const leftover = wire.options;
@@ -1,4 +1,5 @@
1
1
  import { isRef, kindOf, memberOf, pathOf, refLeaf, refOfKey, sourceHops, sourceOf } from "./utils/refs.js";
2
+ import { defaultsFor } from "./default-conditions.js";
2
3
  import { forwardsOf, rootLayerOf } from "../entities/system/element.js";
3
4
  import { createSliceMemo } from "./memo.js";
4
5
  import { moduleViewOf } from "./module-views.js";
@@ -658,9 +659,13 @@ function compositeMemberBag({ config, path, markedIn = config }) {
658
659
  function layerRoutedProps(config, path, state = {}) {
659
660
  const component = config.resolve("component", path);
660
661
  if (!component) return {};
661
- const { layers = {}, styles = {}, defaultProps = {} } = component.toJSON();
662
+ const body = component.toJSON();
663
+ const { layers = {}, styles = {} } = body;
662
664
  const resolved = {
663
- ...defaultProps,
665
+ ...defaultsFor({
666
+ body,
667
+ given: state
668
+ }),
664
669
  ...state
665
670
  };
666
671
  const out = {};
@@ -707,9 +712,13 @@ function layerRoutedProps(config, path, state = {}) {
707
712
  * that layer through motion's factory and hands it the preset's props.
708
713
  */
709
714
  function layerJsMotionPresets(config, path, state = {}) {
710
- const { styles = {}, defaultProps = {} } = config.resolve("component", path)?.toJSON() ?? {};
715
+ const body = config.resolve("component", path)?.toJSON();
716
+ const { styles = {} } = body ?? {};
711
717
  const resolved = {
712
- ...defaultProps,
718
+ ...defaultsFor({
719
+ body,
720
+ given: state
721
+ }),
713
722
  ...state
714
723
  };
715
724
  const rules = Object.entries(styles);
@@ -746,9 +755,12 @@ function layerJsMotionPresets(config, path, state = {}) {
746
755
  * here, and one expanded from the config alone needs this.
747
756
  */
748
757
  function styleRuleMotionClasses(config, path, state = {}) {
749
- const { defaultProps = {} } = config.resolve("component", path)?.toJSON() ?? {};
758
+ const body = config.resolve("component", path)?.toJSON();
750
759
  const resolved = {
751
- ...defaultProps,
760
+ ...defaultsFor({
761
+ body,
762
+ given: state
763
+ }),
752
764
  ...state
753
765
  };
754
766
  const out = {};
@@ -0,0 +1,26 @@
1
+ //#region src/framework/default-conditions.d.ts
2
+ /** One `defaultPropsWhen` entry as stored: the condition, and the defaults that hold under it. */
3
+ interface StoredDefaultCondition {
4
+ readonly when: Record<string, unknown>;
5
+ readonly props: Record<string, unknown>;
6
+ }
7
+ /** Every `defaultPropsWhen` entry a body stores, keyed and ordered as stored. Empty for none. */
8
+ declare function storedDefaultConditions(body: unknown): Readonly<Record<string, StoredDefaultCondition>>;
9
+ /**
10
+ * The defaults that apply to a component given the props it was handed: `defaultProps`, with every
11
+ * `defaultPropsWhen` entry whose condition holds laid over it in stored order, so a later entry wins
12
+ * on the prop it sets.
13
+ *
14
+ * A condition reads a prop's given value, else its unconditional default. A prop that is itself
15
+ * conditionally defaulted can't be a condition (the schema refuses it), so one pass decides.
16
+ * Values compare by leaf, so a stored value ref and the bare name a call site writes match.
17
+ */
18
+ declare function defaultsFor({
19
+ body,
20
+ given
21
+ }: {
22
+ body: unknown;
23
+ given: Readonly<Record<string, unknown>>;
24
+ }): Record<string, unknown>;
25
+ //#endregion
26
+ export { StoredDefaultCondition, defaultsFor, storedDefaultConditions };
@@ -0,0 +1,35 @@
1
+ import { isPlainObject } from "./utils/field-path.js";
2
+ import { refLeaf } from "./utils/refs.js";
3
+ //#region src/framework/default-conditions.ts
4
+ /**
5
+ * A component's stored `defaultPropsWhen` collection, read off a body with nothing else in scope:
6
+ * the defaults that hold only under a condition on the component's other props, beside the
7
+ * unconditional `defaultProps` they override.
8
+ */
9
+ /** Every `defaultPropsWhen` entry a body stores, keyed and ordered as stored. Empty for none. */
10
+ function storedDefaultConditions(body) {
11
+ const stored = body?.defaultPropsWhen;
12
+ return isPlainObject(stored) ? stored : {};
13
+ }
14
+ /**
15
+ * The defaults that apply to a component given the props it was handed: `defaultProps`, with every
16
+ * `defaultPropsWhen` entry whose condition holds laid over it in stored order, so a later entry wins
17
+ * on the prop it sets.
18
+ *
19
+ * A condition reads a prop's given value, else its unconditional default. A prop that is itself
20
+ * conditionally defaulted can't be a condition (the schema refuses it), so one pass decides.
21
+ * Values compare by leaf, so a stored value ref and the bare name a call site writes match.
22
+ */
23
+ function defaultsFor({ body, given }) {
24
+ const defaults = body?.defaultProps ?? {};
25
+ const conditions = Object.values(storedDefaultConditions(body));
26
+ if (conditions.length === 0) return defaults;
27
+ const out = { ...defaults };
28
+ for (const { when, props } of conditions) if (Object.entries(when).every(([prop, value]) => {
29
+ const current = given[prop] !== void 0 ? given[prop] : defaults[prop];
30
+ return String(refLeaf(current)) === String(refLeaf(value));
31
+ })) Object.assign(out, props);
32
+ return out;
33
+ }
34
+ //#endregion
35
+ export { defaultsFor, storedDefaultConditions };
@@ -1,5 +1,5 @@
1
- import { CONFIG_OPERATION_KIND } from "./config-operation-kind.js";
2
1
  import { isRef, kindOf, pathOf, ref, refSchema, refTo, sourceOf } from "./utils/refs.js";
2
+ import { CONFIG_OPERATION_KIND } from "./config-operation-kind.js";
3
3
  import { resolveFields } from "./schemas.js";
4
4
  import { buildKind } from "./registry.js";
5
5
  import { brandGroup } from "./utils/group.js";
@@ -1,5 +1,5 @@
1
- import { rejectData } from "./rejection.js";
2
1
  import { MINTED, collectRefs, entityRefOf, isRef, kindOf, pathOf, ref, rewriteRefs, sourceOf } from "./utils/refs.js";
2
+ import { rejectData } from "./rejection.js";
3
3
  import { kindBorrowable } from "./registry.js";
4
4
  //#region src/framework/inline-sources.ts
5
5
  /**
@@ -1,4 +1,5 @@
1
1
  import { isRef, refLeaf } from "./utils/refs.js";
2
+ import { defaultsFor } from "./default-conditions.js";
2
3
  import { authoredBag } from "./projections.js";
3
4
  import { resolveAppliedComposites } from "../entities/system/applied-composites.js";
4
5
  //#region src/framework/layer-styles.ts
@@ -44,6 +45,20 @@ function interactionHolds(state, key, value) {
44
45
  if (on(state[key])) return true;
45
46
  return on(state[`${key}@${value === true || value === "true" ? "root" : String(value)}`]);
46
47
  }
48
+ const authoredBags = /* @__PURE__ */ new WeakMap();
49
+ /** Every cell reads the same authored rules under its own coordinate. */
50
+ function resolvedBag({ config, bag }) {
51
+ let bags = authoredBags.get(config);
52
+ if (!bags) {
53
+ bags = /* @__PURE__ */ new WeakMap();
54
+ authoredBags.set(config, bags);
55
+ }
56
+ const cached = bags.get(bag);
57
+ if (cached) return cached;
58
+ const resolved = authoredBag(config, resolveAppliedComposites(config, bag));
59
+ bags.set(bag, resolved);
60
+ return resolved;
61
+ }
47
62
  /**
48
63
  * A bag's values as declarations, flattening any forced modifier block one specificity step up.
49
64
  *
@@ -83,9 +98,13 @@ function ruleSpecificity(when) {
83
98
  * "styled with nothing".
84
99
  */
85
100
  function layerStyles(config, path, props = {}, options = {}) {
86
- const { styles = {}, defaultProps = {} } = config.resolve("component", path)?.toJSON() ?? {};
101
+ const body = config.resolve("component", path)?.toJSON();
102
+ const { styles = {} } = body ?? {};
87
103
  const state = {
88
- ...defaultProps,
104
+ ...defaultsFor({
105
+ body,
106
+ given: props
107
+ }),
89
108
  ...props
90
109
  };
91
110
  const forced = (options.forced ?? []).map((name) => name.replace(/^_/, ""));
@@ -94,7 +113,10 @@ function layerStyles(config, path, props = {}, options = {}) {
94
113
  Object.values(styles).forEach((rule, index) => {
95
114
  if (!ruleApplies(rule.when, state)) return;
96
115
  for (const [layer, bag] of Object.entries(rule.layers ?? {})) collectBag({
97
- bag: authoredBag(config, resolveAppliedComposites(config, bag ?? {})),
116
+ bag: resolvedBag({
117
+ config,
118
+ bag: bag ?? {}
119
+ }),
98
120
  forced,
99
121
  specificity: ruleSpecificity(rule.when),
100
122
  push: (key, value, specificity) => declarations.push({
@@ -1,8 +1,8 @@
1
1
  import { Snapshot } from "./snapshot.js";
2
2
 
3
3
  //#region src/framework/memo.d.ts
4
- /** The structural shape a memo needs — any config-like that can produce its snapshot. Structural
5
- * rather than an import of `Config`, which would be a cycle (Config memoizes through this). */
4
+ /** An immutable config value. A changed snapshot or source requires a new value.
5
+ * Structural to avoid importing `Config`, which memoizes through this module. */
6
6
  interface HasSnapshot {
7
7
  readonly snapshot: Snapshot;
8
8
  /**
@@ -63,7 +63,9 @@ function sameSliceKeys({ before, after }) {
63
63
  function createSliceMemo({ kinds, compute }) {
64
64
  if (kinds.length === 0) throw new Error("createSliceMemo needs at least one kind — a computation that reads nothing has nothing to key on");
65
65
  const root = /* @__PURE__ */ new WeakMap();
66
+ const byConfig = /* @__PURE__ */ new WeakMap();
66
67
  return (config) => {
68
+ if (byConfig.has(config)) return byConfig.get(config);
67
69
  const keys = sliceKeys({
68
70
  config,
69
71
  kinds
@@ -78,9 +80,13 @@ function createSliceMemo({ kinds, compute }) {
78
80
  level = next;
79
81
  }
80
82
  const cached = level.has(RESULT) ? level.get(RESULT) : MISS;
81
- if (cached !== MISS) return cached;
83
+ if (cached !== MISS) {
84
+ byConfig.set(config, cached);
85
+ return cached;
86
+ }
82
87
  const value = compute(config);
83
88
  level.set(RESULT, value);
89
+ byConfig.set(config, value);
84
90
  return value;
85
91
  };
86
92
  }
@@ -1,5 +1,5 @@
1
- import { isConfigRejection } from "./rejection.js";
2
1
  import { splitRef } from "./utils/refs.js";
2
+ import { isConfigRejection } from "./rejection.js";
3
3
  //#region src/framework/plan.ts
4
4
  /**
5
5
  * One line for a patch, read off the patch itself: its kind, what it did, and where. The payload
@@ -1,4 +1,5 @@
1
1
  import { declarationText, hasRefMarker, isRef, kindOf, memberOf, pathOf, ref, refLeaf, refOfKey, replaceRefMarkers, routedSource, sourceHops, sourceOf, splitRef } from "./utils/refs.js";
2
+ import { defaultsFor } from "./default-conditions.js";
2
3
  import { evaluateCalc, isCalc, leadingNumber } from "../entities/system/calc.js";
3
4
  import { isOklchColor, renderOklch } from "../entities/system/color.js";
4
5
  import { isComposedValue, renderComposedValue } from "../entities/system/composed-values.js";
@@ -1963,12 +1964,18 @@ function validateComponentProps(config, componentPath, props) {
1963
1964
  function previewDefaults(config, path, cell, options = {}) {
1964
1965
  const component = config.resolve("component", path);
1965
1966
  if (!component) return {};
1966
- const { props, defaultProps } = component.body;
1967
+ const { props } = component.body;
1967
1968
  const previewProps = unconditionalPreviewProps(component.body);
1968
1969
  const base = {
1969
1970
  ...options.firstVariants === false ? {} : firstVariantValues(props),
1970
1971
  ...declaredPropDefaults(props),
1971
- ...defaultProps,
1972
+ ...defaultsFor({
1973
+ body: component.body,
1974
+ given: {
1975
+ ...previewProps,
1976
+ ...cell
1977
+ }
1978
+ }),
1972
1979
  ...previewProps
1973
1980
  };
1974
1981
  if (!cell) return base;
@@ -2011,10 +2018,16 @@ function previewConditionalProps(config, path, cell) {
2011
2018
  function holdingPreviewConditions({ config, path, cell }) {
2012
2019
  const component = config.resolve("component", path);
2013
2020
  if (!component) return [];
2014
- const { defaultProps } = component.body;
2021
+ const previewProps = unconditionalPreviewProps(component.body);
2015
2022
  const state = {
2016
- ...defaultProps,
2017
- ...unconditionalPreviewProps(component.body),
2023
+ ...defaultsFor({
2024
+ body: component.body,
2025
+ given: {
2026
+ ...previewProps,
2027
+ ...cell
2028
+ }
2029
+ }),
2030
+ ...previewProps,
2018
2031
  ...cell
2019
2032
  };
2020
2033
  return Object.entries(keyedPreviewConditions(config, path)).flatMap(([key, condition]) => {
@@ -164,6 +164,7 @@ declare function routesContent(config: Config, layers: Readonly<Record<string, u
164
164
  }): boolean;
165
165
  /**
166
166
  * Every prop a component exposes, in declaration order, projected for a control surface.
167
+ * Independent reads reuse the result for this immutable config.
167
168
  *
168
169
  * A `motion` prop is included as a scalar so it can't silently disappear from a surface that lists
169
170
  * what a component accepts; a caller with no editor for it can fall back to a generic one.
@@ -277,13 +277,25 @@ function slotProp(config, componentPath, name, decl) {
277
277
  ...routedKind === "composite" ? { compositeName: refPath(routed?.value) } : {}
278
278
  };
279
279
  }
280
+ const surfaces = /* @__PURE__ */ new WeakMap();
280
281
  /**
281
282
  * Every prop a component exposes, in declaration order, projected for a control surface.
283
+ * Independent reads reuse the result for this immutable config.
282
284
  *
283
285
  * A `motion` prop is included as a scalar so it can't silently disappear from a surface that lists
284
286
  * what a component accepts; a caller with no editor for it can fall back to a generic one.
285
287
  */
286
288
  function surfaceProps(config, componentPath, seen = /* @__PURE__ */ new Set()) {
289
+ let cache;
290
+ if (seen.size === 0) {
291
+ cache = surfaces.get(config);
292
+ if (!cache) {
293
+ cache = /* @__PURE__ */ new Map();
294
+ surfaces.set(config, cache);
295
+ }
296
+ const cached = cache.get(componentPath);
297
+ if (cached) return cached;
298
+ }
287
299
  const declared = resolveComponentProps(config, componentPath);
288
300
  const layers = (Component.get(config, componentPath)?.toJSON())?.layers;
289
301
  const props = [];
@@ -346,6 +358,7 @@ function surfaceProps(config, componentPath, seen = /* @__PURE__ */ new Set()) {
346
358
  required
347
359
  }, when));
348
360
  }
361
+ cache?.set(componentPath, props);
349
362
  return props;
350
363
  }
351
364
  /**
@@ -206,22 +206,9 @@ declare function componentSpec(config: Config, path: string): ComponentSpec;
206
206
  */
207
207
  declare function previewSpec(config: Config, path: string): RenderSpec | undefined;
208
208
  interface ExpandOptions {
209
- /**
210
- * The spec `type`s the renderer already has a component for — in practice the keys of the
211
- * codegen-emitted registry.
212
- *
213
- * A component in this set is LEFT NAMED, deliberately: its emitted module is the real thing, with
214
- * whatever its render closes over (a hook, a context, an event handler) intact, and re-deriving it
215
- * from data would be a worse copy of something that already exists. Expansion is for what the
216
- * registry CAN'T name — which is exactly a component whose structure is data and whose module was
217
- * never built.
218
- *
219
- * Omitted, everything the config describes expands, which is what a test wants and what a caller
220
- * with no registry to consult gets.
221
- *
222
- * Only `has` is asked, so a caller can answer from its registry directly instead of copying every
223
- * key into a `Set` first.
224
- */
209
+ /** Membership lookup for types the renderer can draw. A Set or a registry-backed `has` works.
210
+ * Recognized components keep their compiled modules, including hooks and handlers.
211
+ * Omitted, everything the config describes expands. */
225
212
  readonly rendered?: Pick<ReadonlySet<string>, 'has'>;
226
213
  /** The functions `$computed` values call. Absent, a computed value stays on the element for the
227
214
  * renderer, and a node repeated over a computed list emits once. */
@@ -1,4 +1,5 @@
1
1
  import { isRef, kindOf, memberOf, pathOf, ref, refLeaf, splitRef } from "./utils/refs.js";
2
+ import { defaultsFor } from "./default-conditions.js";
2
3
  import { renderedElement } from "../entities/system/element.js";
3
4
  import { createSliceMemo } from "./memo.js";
4
5
  import { componentStateDefaults, componentStates } from "./component-states.js";
@@ -1264,9 +1265,16 @@ function expandInstance(sink, path, instanceProps, content, hint, instanceSlots)
1264
1265
  for (const [from, fills] of Object.entries(instanceSlots ?? {})) if (fills.length > 0 && instanceProps[from] === void 0) filled[from] = true;
1265
1266
  const declaredDefaults = declaredPropDefaults(body.props);
1266
1267
  const pins = instanceProps["__udsForceStates"] ?? {};
1268
+ const defaults = defaultsFor({
1269
+ body,
1270
+ given: {
1271
+ ...filled,
1272
+ ...instanceProps
1273
+ }
1274
+ });
1267
1275
  const state = {
1268
1276
  ...declaredDefaults,
1269
- ...body.defaultProps,
1277
+ ...defaults,
1270
1278
  ...componentStateDefaults(config, path),
1271
1279
  ...filled,
1272
1280
  ...instanceProps,
@@ -1283,7 +1291,7 @@ function expandInstance(sink, path, instanceProps, content, hint, instanceSlots)
1283
1291
  const forwardsClassName = [...valueRoutes.values()].some((routes) => routes.some((route) => route.from === "className"));
1284
1292
  const supplied = {
1285
1293
  ...declaredDefaults,
1286
- ...body.defaultProps ?? {},
1294
+ ...defaults,
1287
1295
  ...instanceProps
1288
1296
  };
1289
1297
  const stateNames = new Set(componentStates(config, path).map(({ name }) => name));
@@ -15,7 +15,7 @@ import { Patch, SerializedConfig } from "./Config.js";
15
15
  * sets this constant to its own older timestamp; the merge ref then carries a registered migration
16
16
  * newer than the constant, and the assertion fails until the author re-mints.
17
17
  */
18
- declare const CURRENT_SCHEMA_VERSION = 20261022184639;
18
+ declare const CURRENT_SCHEMA_VERSION = 20261023152030;
19
19
  /** What the walkers need to see of an envelope — loose on purpose, so a typed `SerializedConfig`
20
20
  * and a raw old-shape record both flow in without casts. */
21
21
  interface VersionStamped {
@@ -13,7 +13,7 @@ import { ConfigFormatError } from "./rejection.js";
13
13
  * sets this constant to its own older timestamp; the merge ref then carries a registered migration
14
14
  * newer than the constant, and the assertion fails until the author re-mints.
15
15
  */
16
- const CURRENT_SCHEMA_VERSION = 20261022184639;
16
+ const CURRENT_SCHEMA_VERSION = 20261023152030;
17
17
  /** The mint that moved `options` into the `settings` kind — restated here, rather than imported
18
18
  * from the migration that owns it, because that migration imports this module. */
19
19
  const SETTINGS_AS_KIND_VERSION = 20260908171448;
@@ -45,7 +45,7 @@ function registerSchemaMigrations(...migrations) {
45
45
  for (const migration of migrations) {
46
46
  if (migration.version !== 1 && !isMintTimestamp(migration.version)) throw new Error(`Schema migration version ${migration.version} is not a mint timestamp — mint one with \`date +%Y%m%d%H%M%S\`, like a supabase migration.`);
47
47
  if (REGISTERED.has(migration.version)) throw new Error(`A schema migration at version ${migration.version} is already registered.`);
48
- if (migration.version > 20261022184639) throw new Error(`Schema migration ${migration.version} is newer than CURRENT_SCHEMA_VERSION (${CURRENT_SCHEMA_VERSION}) — a stale branch merged, or the mint forgot to bump the constant. Re-mint: set CURRENT_SCHEMA_VERSION to the newest migration version.`);
48
+ if (migration.version > 20261023152030) throw new Error(`Schema migration ${migration.version} is newer than CURRENT_SCHEMA_VERSION (${CURRENT_SCHEMA_VERSION}) — a stale branch merged, or the mint forgot to bump the constant. Re-mint: set CURRENT_SCHEMA_VERSION to the newest migration version.`);
49
49
  REGISTERED.set(migration.version, migration);
50
50
  }
51
51
  }
@@ -102,7 +102,7 @@ function pendingFor(version, target, options) {
102
102
  * Already-current input passes through untouched — same reference, so hot paths pay nothing.
103
103
  */
104
104
  function upgradeSerializedConfig(json, options) {
105
- const target = options?.target ?? 20261022184639;
105
+ const target = options?.target ?? 20261023152030;
106
106
  const version = detectedWireVersion(json);
107
107
  if (version > target) throw new SchemaVersionTooNew(version, target);
108
108
  const raw = json;
@@ -160,7 +160,7 @@ function upgradedSources(json, options) {
160
160
  * meaning under the current schema and replay skips it — reported by the caller, not swallowed here.
161
161
  */
162
162
  function upgradePatch(patch, writtenAt, options) {
163
- const target = options?.target ?? 20261022184639;
163
+ const target = options?.target ?? 20261023152030;
164
164
  if (writtenAt > target) throw new SchemaVersionTooNew(writtenAt, target);
165
165
  let current = patch;
166
166
  for (const migration of pendingFor(writtenAt, target, options)) {
@@ -179,7 +179,7 @@ function upgradePatch(patch, writtenAt, options) {
179
179
  * stamp would cause.
180
180
  */
181
181
  function upgradeDraftEntries(draft, options) {
182
- const target = options?.target ?? 20261022184639;
182
+ const target = options?.target ?? 20261023152030;
183
183
  const entries = [];
184
184
  const held = [];
185
185
  const retired = [];
@@ -1,8 +1,8 @@
1
- import { reject } from "./rejection.js";
1
+ import { sectionDelta } from "./utils/field-path.js";
2
2
  import { ref } from "./utils/refs.js";
3
+ import { reject } from "./rejection.js";
3
4
  import { planDelete, planRename } from "./plan.js";
4
5
  import { compileSchema } from "./utils/compiled-schema.js";
5
- import { sectionDelta } from "./utils/field-path.js";
6
6
  import { deriveCreateSchema, deriveMemberUpdateSchema, deriveUpdateSchema } from "./utils/field-schema.js";
7
7
  import { z } from "zod";
8
8
  //#region src/framework/schemas.ts
@@ -1,5 +1,5 @@
1
- import { collectRefs, isExtendedRef, isRef, kindOf, memberOf, pathOf, sourceOf, splitRef } from "./utils/refs.js";
2
1
  import { isPlainObject, setAtPath } from "./utils/field-path.js";
2
+ import { collectRefs, isExtendedRef, isRef, kindOf, memberOf, pathOf, sourceOf, splitRef } from "./utils/refs.js";
3
3
  import { isComposedValue } from "../entities/system/composed-values.js";
4
4
  import { modifierCategory } from "../entities/system/modifier-axis.js";
5
5
  //#region src/framework/source-integrity.ts