@yahoo/uds-create-config 4.10.2 → 4.10.4

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 (103) hide show
  1. package/dist/configs/CanvasConfig.d.ts +165 -77
  2. package/dist/configs/documentation.d.ts +1 -1
  3. package/dist/configs/react-native-system.d.ts +111 -52
  4. package/dist/configs/system.d.ts +121 -87
  5. package/dist/configs/system.js +4 -0
  6. package/dist/entities/documentation/Documentation.d.ts +1 -1
  7. package/dist/entities/native/NativeModifier.d.ts +1 -1
  8. package/dist/entities/native/NativeSettings.d.ts +1 -1
  9. package/dist/entities/native/NativeStyleProperty.d.ts +2 -2
  10. package/dist/entities/native/NativeToken.d.ts +4 -3
  11. package/dist/entities/system/CanvasRole.d.ts +1 -1
  12. package/dist/entities/system/Component.d.ts +23 -13
  13. package/dist/entities/system/Component.js +41 -15
  14. package/dist/entities/system/Composite.d.ts +1 -1
  15. package/dist/entities/system/Device.d.ts +1 -1
  16. package/dist/entities/system/File.d.ts +2 -2
  17. package/dist/entities/system/Font.d.ts +1 -1
  18. package/dist/entities/system/GlobalStyle.d.ts +1 -1
  19. package/dist/entities/system/Guidance.d.ts +1 -1
  20. package/dist/entities/system/GuidanceStyle.d.ts +1 -1
  21. package/dist/entities/system/Icon.d.ts +63 -11
  22. package/dist/entities/system/Icon.js +70 -14
  23. package/dist/entities/system/LinkedSystem.d.ts +1 -1
  24. package/dist/entities/system/Modifier.d.ts +2 -2
  25. package/dist/entities/system/Motion.d.ts +1 -1
  26. package/dist/entities/system/Package.d.ts +1 -1
  27. package/dist/entities/system/Settings.d.ts +1 -1
  28. package/dist/entities/system/StyleProperty.d.ts +12 -19
  29. package/dist/entities/system/StyleProperty.js +7 -10
  30. package/dist/entities/system/Token.d.ts +4 -3
  31. package/dist/entities/system/defineComponent.d.ts +21 -6
  32. package/dist/entities/system/defineComponent.js +7 -12
  33. package/dist/entities/system/glyph-identity.d.ts +7 -0
  34. package/dist/entities/system/glyph-identity.js +153 -0
  35. package/dist/entities/system/icon-domain-invariants.d.ts +11 -0
  36. package/dist/entities/system/icon-domain-invariants.js +178 -0
  37. package/dist/entities/system/icon-library.d.ts +93 -34
  38. package/dist/entities/system/icon-library.js +218 -50
  39. package/dist/entities/system/icon-metadata.d.ts +10 -4
  40. package/dist/entities/system/icon-metadata.js +45 -5
  41. package/dist/entities/system/runtime-modules.js +5 -5
  42. package/dist/entities/system/style-bag.js +14 -2
  43. package/dist/framework/Config.d.ts +1 -1
  44. package/dist/framework/Config.js +34 -5
  45. package/dist/framework/defineConfig.d.ts +6 -3
  46. package/dist/framework/defineEntity.d.ts +54 -15
  47. package/dist/framework/defineEntity.js +56 -2
  48. package/dist/framework/projections.d.ts +40 -2
  49. package/dist/framework/projections.js +99 -5
  50. package/dist/framework/prop-surface.d.ts +6 -5
  51. package/dist/framework/prop-surface.js +12 -14
  52. package/dist/framework/registry.d.ts +17 -1
  53. package/dist/framework/registry.js +2 -1
  54. package/dist/framework/render-spec.d.ts +6 -5
  55. package/dist/framework/render-spec.js +16 -11
  56. package/dist/framework/schema-version.d.ts +1 -1
  57. package/dist/framework/schema-version.js +5 -5
  58. package/dist/framework/signature.js +5 -1
  59. package/dist/framework/utils/group.d.ts +15 -1
  60. package/dist/framework/utils/ref-grammar.d.ts +35 -0
  61. package/dist/framework/utils/ref-grammar.js +76 -0
  62. package/dist/framework/utils/refs.d.ts +13 -45
  63. package/dist/framework/utils/refs.js +31 -94
  64. package/dist/framework/value-domain.js +53 -8
  65. package/dist/index.d.ts +6 -6
  66. package/dist/index.js +5 -5
  67. package/dist/migrations/2.0.0/v1-artifact.d.ts +62 -45
  68. package/dist/migrations/2.0.0/v1-artifact.js +5 -7
  69. package/dist/migrations/20260921044042_icon_library_without_component.d.ts +19 -0
  70. package/dist/migrations/20260921044042_icon_library_without_component.js +86 -0
  71. package/dist/migrations/20260922013625_icon_libraries.d.ts +18 -0
  72. package/dist/migrations/20260922013625_icon_libraries.js +8 -0
  73. package/dist/renderer/UdsRenderer.d.ts +8 -1
  74. package/dist/renderer/UdsRenderer.js +7 -3
  75. package/dist/renderer/glyphEntries.d.ts +32 -0
  76. package/dist/renderer/glyphEntries.js +48 -0
  77. package/dist/renderer/glyphRenderable.d.ts +10 -0
  78. package/dist/renderer/glyphRenderable.js +40 -0
  79. package/dist/renderer/icon-library.d.ts +18 -11
  80. package/dist/renderer/index.d.ts +4 -2
  81. package/dist/renderer/index.js +4 -2
  82. package/dist/renderer/resolveGlyph.d.ts +47 -0
  83. package/dist/renderer/resolveGlyph.js +78 -0
  84. package/dist/renderer/wrapRegistry.d.ts +13 -4
  85. package/dist/renderer/wrapRegistry.js +7 -5
  86. package/dist/renderer/wrappers/component-refs.js +6 -2
  87. package/dist/renderer/wrappers/glyph-props.d.ts +15 -0
  88. package/dist/renderer/wrappers/glyph-props.js +65 -0
  89. package/dist/spec/glyph-jsx.d.ts +41 -0
  90. package/dist/spec/{asset-jsx.js → glyph-jsx.js} +7 -25
  91. package/dist/spec/index.d.ts +2 -2
  92. package/dist/spec/index.js +2 -2
  93. package/dist/spec/jsxToSpec.d.ts +10 -4
  94. package/dist/spec/jsxToSpec.js +21 -4
  95. package/dist/spec/specToJsx.d.ts +20 -9
  96. package/dist/spec/specToJsx.js +77 -16
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/package.json +2 -2
  99. package/dist/renderer/assetEntries.d.ts +0 -37
  100. package/dist/renderer/assetEntries.js +0 -38
  101. package/dist/renderer/assetRenderable.d.ts +0 -25
  102. package/dist/renderer/assetRenderable.js +0 -19
  103. package/dist/spec/asset-jsx.d.ts +0 -61
@@ -3,7 +3,8 @@ import { defineSubEntity } from "../../framework/defineSubEntity.js";
3
3
  import { derivedColor, gradient } from "./color.js";
4
4
  import { isCssProperty, kebabCase } from "../../css/values.js";
5
5
  import { valueSchemaOf } from "../../framework/value-domain.js";
6
- import { stylePropTokenPath } from "../../framework/projections.js";
6
+ import { glyphLeafOf, glyphPackageRef } from "./glyph-identity.js";
7
+ import { glyphPropsOfBody, refListOf, stylePropTokenPath } from "../../framework/projections.js";
7
8
  import { z } from "zod";
8
9
  //#region src/entities/system/style-bag.ts
9
10
  /** The explicit type annotation breaks the `z.lazy` self-reference cycle and keeps the
@@ -15,6 +16,7 @@ const styleValue = z.lazy(() => z.union([
15
16
  refSchema,
16
17
  derivedColor,
17
18
  gradient,
19
+ glyphPackageRef,
18
20
  z.record(z.string(), styleValue)
19
21
  ]));
20
22
  const anatomyPropValue = z.lazy(() => z.union([
@@ -63,7 +65,17 @@ const Style = defineSubEntity({
63
65
  if (key.startsWith("_")) return openBag(config);
64
66
  const bound = (config, declaration) => z.union([valueSchemaOf(declaration, config), styleValue]);
65
67
  const declared = parent?.props?.[key];
66
- if (isRef(declared?.value)) return bound(config, declared);
68
+ if (isRef(declared?.value) || refListOf(declared?.value) !== void 0) return bound(config, declared);
69
+ if (declared?.type === "forward") {
70
+ const libraries = glyphPropsOfBody({
71
+ config,
72
+ body: parent
73
+ })[key];
74
+ if (libraries && libraries.length > 0) return z.union([glyphLeafOf({
75
+ config,
76
+ libraries
77
+ }), styleValue]);
78
+ }
67
79
  if (declared) return styleValue;
68
80
  if (!config.resolve("style-property", key)) return styleValue;
69
81
  return bound(config, {
@@ -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 = 20260920161309;
75
+ declare const SERIALIZED_CONFIG_VERSION = 20260922013625;
76
76
  interface SerializedConfig {
77
77
  /** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
78
78
  readonly version: number;
@@ -526,14 +526,36 @@ const isCopiedRow = (data) => isPlainObject(data) && typeof data.copiedFrom ===
526
526
  /** Whether a refusal is authored invariants only — the issues a replay and a hydrate let stand. */
527
527
  const customOnly = (error) => error.issues.every((issue) => issue.code === "custom");
528
528
  function coerceFieldwise(schema, member) {
529
- if (!(schema instanceof z.ZodObject) || !isPlainObject(member)) return member;
529
+ const object = objectSchemaOf(schema);
530
+ if (!object || !isPlainObject(member)) return member;
530
531
  const out = { ...member };
531
- for (const [field, fieldSchema] of Object.entries(schema.shape)) {
532
+ const shape = object.shape;
533
+ const catchall = object._zod.def.catchall;
534
+ for (const field of new Set([...Object.keys(shape), ...Object.keys(out)])) {
535
+ const fieldSchema = shape[field] ?? catchall;
536
+ if (!fieldSchema) continue;
532
537
  const parsed = fieldSchema.safeParse(out[field]);
533
- if (parsed.success && parsed.data !== void 0) out[field] = parsed.data;
538
+ if (parsed.success) {
539
+ if (parsed.data !== void 0) out[field] = parsed.data;
540
+ continue;
541
+ }
542
+ if (out[field] !== void 0) out[field] = coerceFieldwise(fieldSchema, out[field]);
534
543
  }
535
544
  return out;
536
545
  }
546
+ /** The object schema a field is, once the wrappers that add nothing to its keys are taken off: an
547
+ * optional layer bag, a nullable condition, a defaulted field, a lazy self-reference. */
548
+ function objectSchemaOf(schema) {
549
+ let current = schema;
550
+ for (let depth = 0; depth < 8; depth += 1) {
551
+ if (current instanceof z.ZodObject) return current;
552
+ if (current instanceof z.ZodOptional || current instanceof z.ZodNullable || current instanceof z.ZodDefault || current instanceof z.ZodLazy) {
553
+ current = current.unwrap();
554
+ continue;
555
+ }
556
+ return;
557
+ }
558
+ }
537
559
  var Config = class Config {
538
560
  /** All of this config's state. `protected`, not a `#private` field, so a derived config can be
539
561
  * built with `Object.create` — JS private fields are installed by a constructor, and an O(1)
@@ -3492,7 +3514,14 @@ var Config = class Config {
3492
3514
  if (!theirs && !overlay) return body;
3493
3515
  const inherited = {};
3494
3516
  const local = new Set(this.entityOf(kind)?.localGroupFields ?? []);
3495
- for (const [field, value] of Object.entries(theirs ?? {})) if (!local.has(field)) inherited[field] = value;
3517
+ for (const [field, value] of Object.entries(theirs ?? {})) {
3518
+ if (local.has(field)) continue;
3519
+ inherited[field] = qualifyRefs({
3520
+ value,
3521
+ source: slug,
3522
+ owns: (refKind) => from.entityOf(refKind) !== void 0
3523
+ });
3524
+ }
3496
3525
  return {
3497
3526
  ...inherited,
3498
3527
  ...overlay?.override,
@@ -4807,7 +4836,7 @@ var Config = class Config {
4807
4836
  * links from the provided sources. A declared `linkedKind` with no provided source throws. */
4808
4837
  hydrateFrom(stored, options) {
4809
4838
  const claimed = schemaVersionOf(stored);
4810
- if (claimed > 20260920161309) throw new SchemaVersionTooNew(claimed);
4839
+ if (claimed > 20260922013625) throw new SchemaVersionTooNew(claimed);
4811
4840
  const wire = upgradeSerializedConfig(stored);
4812
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\`.`);
4813
4842
  const leftover = wire.options;
@@ -7,7 +7,9 @@ import { Config, HydrationOptions, ListOptions, StoredConfig, TreeNode } from ".
7
7
  import { z } from "zod";
8
8
 
9
9
  //#region src/framework/defineConfig.d.ts
10
- type AnyEntityClass = EntityClass<any, any, string, any, any, boolean> | DerivedEntityClass<any, any>;
10
+ type AnyEntityClass = (Omit<EntityClass<any, any, string, any, any, boolean>, 'ref'> & {
11
+ readonly ref: unknown;
12
+ }) | DerivedEntityClass<any, any>;
11
13
  /** A named map of entity classes — the shape of `entities` / `derivedEntities`. */
12
14
  type EntityMap = Record<string, AnyEntityClass>;
13
15
  /** One entity class's kind, as the literal it declared. Distributive on purpose, so a UNION of
@@ -41,7 +43,8 @@ type ConfigKind<C> = C extends {
41
43
  * which is the one shape nothing downstream can resolve. An array is a leaf too: a partial of its
42
44
  * elements describes no gesture anyone makes, and a caller replacing one writes the whole list.
43
45
  */
44
- type PartialBodyOf<E> = E extends EntityClass<infer F extends FieldsSchema, FieldsSchema> ? DeepPartial<CreateInput<F>> : never;
46
+ type Matched = any;
47
+ type PartialBodyOf<E> = E extends EntityClass<infer F extends FieldsSchema, FieldsSchema, infer _K, Matched, Matched, Matched> ? DeepPartial<CreateInput<F>> : never;
45
48
  /** Optional at every depth, stopping at refs, arrays and primitives. */
46
49
  type DeepPartial<T> = T extends Ref | readonly unknown[] ? T : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
47
50
  /**
@@ -65,7 +68,7 @@ declare function kindsOf<T extends readonly {
65
68
  * mean. */
66
69
  type RecordFor<E> = E extends {
67
70
  readonly singleton: true;
68
- } ? E extends EntityClass<infer F, FieldsSchema, string, any, any, true> ? Partial<z.input<F>> : never : E extends EntityClass<infer F, infer G> ? Record<string, AuthoredEntry<F, G>> : never;
71
+ } ? E extends EntityClass<infer F, FieldsSchema, infer _K, Matched, Matched, true> ? Partial<z.input<F>> : never : E extends EntityClass<infer F, infer G, infer _K, Matched, Matched, Matched> ? Record<string, AuthoredEntry<F, G>> : never;
69
72
  /**
70
73
  * What a config DRAWS ON — keyed by the accessor name of the kind whose items ARE the sources, valued
71
74
  * by a thunk yielding the config type each one resolves to.
@@ -5,9 +5,9 @@ import { CreateInput, FieldsSchema } from "./utils/field-schema.js";
5
5
  import { FieldsCtx, UserSchemas } from "./schemas.js";
6
6
  import { SubEntityClass } from "./defineSubEntity.js";
7
7
  import { BulkOverlayWrite, BulkQuery, SourceOverlay, SourceResolutionSchemas } from "./sources.js";
8
- import { ComputedFields, EntityKind, Linkable } from "./registry.js";
8
+ import { AuthoredValueForm, ComputedFields, EntityKind, Linkable } from "./registry.js";
9
9
  import { PathOf } from "./registered.js";
10
- import { GroupBody } from "./utils/group.js";
10
+ import { GroupBody, GroupInputValues, GroupInputs } from "./utils/group.js";
11
11
  import { Config, ListOptions, TreeNode } from "./Config.js";
12
12
  import { z } from "zod";
13
13
 
@@ -49,6 +49,26 @@ type ItemOf<E> = E extends {
49
49
  } ? NonNullable<R> : never;
50
50
  /** An entity as a read returns it — its declared fields, plus the return type of every computed. */
51
51
  type Read<F extends FieldsSchema, C extends ComputedFields> = Entity & z.infer<F> & { readonly [K in keyof C]: ReturnType<C[K]> };
52
+ /** What an entity's `ref` hands back: the extendable marker, with kind and path as literals. */
53
+ type KindRef<F extends FieldsSchema, K extends string, P extends string> = ExtendableRef<Partial<CreateInput<F>>> & {
54
+ readonly __ref: `${K}:${P}`;
55
+ };
56
+ /** What a group computed reads: the config, the group's path and its stored meta. */
57
+ interface GroupComputedInput {
58
+ readonly config: Config;
59
+ readonly path: string;
60
+ readonly meta: Record<string, unknown>;
61
+ }
62
+ /** A group node's virtual fields: `({ config, path, meta }) => value`, recomputed on every read of
63
+ * `groups` and never serialized — the group-level twin of an item's `computed`. */
64
+ type GroupComputedFields = Record<string, (input: GroupComputedInput) => unknown>;
65
+ /** What a kind's group computeds add to a group's meta on read. */
66
+ type GroupComputedValues<GC extends GroupComputedFields> = { readonly [K in keyof GC]: ReturnType<GC[K]> };
67
+ /** A group node as `groups` reads it: its stored meta with the kind's group computeds attached. */
68
+ type GroupRead<F extends FieldsSchema, G extends FieldsSchema, GC extends GroupComputedFields = Record<never, never>> = {
69
+ path: string;
70
+ meta: GroupBody<F, G> & GroupComputedValues<GC>;
71
+ };
52
72
  type EntityClass<F extends FieldsSchema, G extends FieldsSchema,
53
73
  /** The kind as a literal, so `ref` can narrow to the paths a consumer's config declares. Defaults
54
74
  * to `string`, which is what every 2-argument use of this type keeps getting. */
@@ -61,7 +81,13 @@ S extends UserSchemas = UserSchemas,
61
81
  C extends ComputedFields = Record<never, never>,
62
82
  /** Whether the kind has exactly one item — see `singleton` on `defineEntity`. Read off the spec so
63
83
  * `get` can drop its path argument for exactly the kinds whose one item needs none. */
64
- Single extends boolean = false> = Omit<RefSchema, 'extend'> & {
84
+ Single extends boolean = false,
85
+ /** The kind's `groupComputed` map as written, so `Icon.groups(...)[n].meta.members` is typed off
86
+ * the function that produces it. Defaults to none. */
87
+ GC extends GroupComputedFields = Record<never, never>,
88
+ /** The kind's `groupInputs` as written, so `group()` takes each input key at its handler's declared
89
+ * type. Defaults to none. */
90
+ GI extends GroupInputs = Record<never, never>> = Omit<RefSchema, 'extend'> & {
65
91
  readonly kind: K;
66
92
  /** Whether the kind has exactly one item — see `singleton` on `defineEntity`. A property rather than
67
93
  * only a type parameter so a conditional type can tell the two apart: `get`'s signature differs by
@@ -81,13 +107,14 @@ Single extends boolean = false> = Omit<RefSchema, 'extend'> & {
81
107
  /** The kind-bound member-ref maker — `Component.ref('Card')` → `{ __ref: 'component:Card' }`.
82
108
  * Called on the entity itself; a kind never re-exports it under a second, lowercase name. A
83
109
  * singleton's one item sits at the kind's own name, so its path may be left out: `Settings.ref()`. */
84
- ref(...path: Single extends true ? [path?: PathOf<K>] : [path: PathOf<K>]): ExtendableRef<Partial<CreateInput<F>>>;
110
+ readonly ref: Single extends true ? <P extends PathOf<K>>(path?: P) => KindRef<F, K, P> : <P extends PathOf<K>>(path: P) => KindRef<F, K, P>;
85
111
  /** Typed constructor for one item — a full body, or a bare value/ref that's shorthand
86
112
  * for the kind's primary field. Returned as-is; `register` normalizes the shorthand. */
87
113
  item(def: CreateInput<F> | string | number | Ref): CreateInput<F> | string | number | Ref;
88
114
  /** Typed constructor for a group node — branded so `register` routes it to the group
89
- * store; input is the group fields' create body. */
90
- group(meta: GroupBody<F, G>): GroupBody<F, G>;
115
+ * store; input is the group fields' create body, plus any registration-time input the kind
116
+ * declares (`groupInputs`), which is consumed here and never stored. */
117
+ group(meta: GroupBody<F, G> & GroupInputValues<GI>): GroupBody<F, G>;
91
118
  /** Resolve one item, typed off the fields — `Component.get(config, 'Button')`. Ref-aware: also
92
119
  * accepts a `Ref` (`Node.component` → `Component.get(config, node.component)`), rejecting a
93
120
  * wrong-kind ref. A singleton's one item sits at the kind's own name, so its path may be left out:
@@ -101,10 +128,7 @@ Single extends boolean = false> = Omit<RefSchema, 'extend'> & {
101
128
  * depends on. The enumeration `list`/`tree` can't give: both derive from item paths, so a group
102
129
  * declared with no members yet (a package the host supplies, which no render names) is invisible
103
130
  * to them. */
104
- groups(config: Config, opts?: ListOptions): {
105
- path: string;
106
- meta: GroupBody<F, G>;
107
- }[];
131
+ groups(config: Config, opts?: ListOptions): GroupRead<F, G, GC>[];
108
132
  /** What references a given item (`kind:path` strings) — the reverse dependency graph.
109
133
  * `{ transitive: true }` returns the whole closure rather than the direct edges, which is what
110
134
  * a chain like `font → token → composite → component` needs: every hop is a different kind, so
@@ -152,9 +176,9 @@ Single extends boolean = false> = Omit<RefSchema, 'extend'> & {
152
176
  * from the kind can name it — and a source writer that guesses emits the base's class into a
153
177
  * Config that never imported it. See {@link EntityKind.entityName}. */
154
178
  entityName?: string;
155
- }): EntityClass<ExtendedFields<F, F2>, ExtendedFields<G, G2>, K, S2, C, Single>;
179
+ }): EntityClass<ExtendedFields<F, F2>, ExtendedFields<G, G2>, K, S2, C, Single, GC, GI>;
156
180
  };
157
- declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z.ZodObject<Record<never, never>>, K extends string = string, const S extends UserSchemas = Record<never, never>, const C extends ComputedFields = Record<never, never>, Single extends boolean = false>(spec: {
181
+ declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z.ZodObject<Record<never, never>>, K extends string = string, const S extends UserSchemas = Record<never, never>, const C extends ComputedFields = Record<never, never>, Single extends boolean = false, const GC extends GroupComputedFields = Record<never, never>, const GI extends GroupInputs = Record<never, never>>(spec: {
158
182
  kind: K;
159
183
  /** The kind's fields, or a factory over the write's context (`config`, `body`) when a field's
160
184
  * schema depends on the config or on its sibling fields. See {@link FieldsDeclaration}. */
@@ -191,6 +215,12 @@ declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z
191
215
  * into an artifact and then contradict the value it was derived from.
192
216
  */
193
217
  computed?: C;
218
+ /**
219
+ * Virtual fields of a GROUP node, recomputed on every `groups` read and never serialized —
220
+ * `({ config, path, meta }) => value`. For a fact about the set that derives from what the node
221
+ * and the config already hold: an icon library's members, or the module its package names.
222
+ */
223
+ groupComputed?: GC;
194
224
  /**
195
225
  * For a kind whose items name an external source: the fields that decide which source.
196
226
  *
@@ -251,7 +281,16 @@ declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z
251
281
  * mean the ref has to say which.
252
282
  */
253
283
  bareMember?: string; /** Whether a leaf names an item on its own — see {@link EntityKind.uniqueLeaves}. */
254
- uniqueLeaves?: boolean; /** Cross-item rules the kind holds — see {@link EntityKind.invariants}. */
284
+ uniqueLeaves?: boolean;
285
+ /**
286
+ * Registration-time inputs `group()` takes beside the group's fields, keyed by body key. Each is
287
+ * handed to its handler with the rest of the body and stripped before the body is branded, so the
288
+ * stored node, `groupFields` and the schema hash never see it. For a value that exists only while
289
+ * the config file is evaluated: a module namespace a library reads identities off, which no
290
+ * artifact can hold. See {@link GroupInputHandler}.
291
+ */
292
+ groupInputs?: GI; /** An extra authored form for a value whose domain is this kind — see {@link EntityKind.authoredValue}. */
293
+ authoredValue?: AuthoredValueForm; /** Cross-item rules the kind holds — see {@link EntityKind.invariants}. */
255
294
  invariants?: (config: Config) => string[];
256
295
  /**
257
296
  * For a source kind that can be resolved: the two schemas its resolutions are validated against —
@@ -302,7 +341,7 @@ declare function defineEntity<F extends FieldsSchema, G extends FieldsSchema = z
302
341
  element?: unknown;
303
342
  }[];
304
343
  rewriteName?: (data: unknown, from: string, to: string) => unknown;
305
- }): EntityClass<F, G, K, S, C, Single>;
344
+ }): EntityClass<F, G, K, S, C, Single, GC, GI>;
306
345
  /**
307
346
  * The input type of one of an entity's operations — `OpInput<typeof Component, 'element-insert'>`.
308
347
  *
@@ -316,4 +355,4 @@ type OpInput<E extends {
316
355
  input: infer I extends z.ZodTypeAny;
317
356
  } ? z.input<I> : never;
318
357
  //#endregion
319
- export { EntityClass, ItemOf, OpInput, defineEntity };
358
+ export { EntityClass, GroupComputedInput, ItemOf, OpInput, defineEntity };
@@ -54,10 +54,51 @@ function extendSubEntities(base, override) {
54
54
  else if (value !== void 0) merged[key] = value;
55
55
  return merged;
56
56
  }
57
+ /**
58
+ * A kind's group computeds, attached to the groups a config lists. Lazy getters, so a field nobody
59
+ * reads is never paid for; memoized per config and per list, since `config.groups` hands back the
60
+ * same array while nothing changed.
61
+ */
62
+ function groupsWithComputedFor(computed) {
63
+ if (!computed || Object.keys(computed).length === 0) return (_config, groups) => groups;
64
+ const memo = /* @__PURE__ */ new WeakMap();
65
+ return (config, groups) => {
66
+ const hit = memo.get(groups);
67
+ if (hit) return hit;
68
+ const decorated = groups.map(({ path, meta }) => {
69
+ const read = { ...meta };
70
+ for (const [field, compute] of Object.entries(computed)) {
71
+ let value;
72
+ let done = false;
73
+ Object.defineProperty(read, field, {
74
+ enumerable: true,
75
+ get: () => {
76
+ if (!done) {
77
+ value = compute({
78
+ config,
79
+ path,
80
+ meta
81
+ });
82
+ done = true;
83
+ }
84
+ return value;
85
+ }
86
+ });
87
+ }
88
+ return {
89
+ path,
90
+ meta: read
91
+ };
92
+ });
93
+ memo.set(groups, decorated);
94
+ return decorated;
95
+ };
96
+ }
57
97
  function defineEntity(spec) {
58
98
  if (spec.kind === "config") throw new Error(`Entity kind "${CONFIG_OPERATION_KIND}" is reserved: it is the address segment of the config's own operations (\`config/info\`). Name the kind for what it holds.`);
59
99
  const declaredGroupFields = spec.groupFields ?? z.object({});
60
100
  const groupFields = declaredGroupFields instanceof z.ZodObject ? declaredGroupFields.extend({ ref: refSchema.optional() }) : declaredGroupFields;
101
+ const groupsWithComputed = groupsWithComputedFor(spec.groupComputed);
61
102
  const kindDef = buildKind({
62
103
  name: spec.kind,
63
104
  fields: spec.fields,
@@ -85,6 +126,7 @@ function defineEntity(spec) {
85
126
  styleBags: spec.styleBags,
86
127
  rewriteName: spec.rewriteName,
87
128
  uniqueLeaves: spec.uniqueLeaves,
129
+ authoredValue: spec.authoredValue,
88
130
  invariants: spec.invariants
89
131
  });
90
132
  const schema = z.object({ __ref: z.string().refine((raw) => kindOf(raw) === spec.kind) }).meta({
@@ -113,7 +155,19 @@ function defineEntity(spec) {
113
155
  return ref(spec.kind, path ?? spec.kind);
114
156
  },
115
157
  item: (def) => def,
116
- group: (meta) => brandGroup(meta),
158
+ group: (meta) => {
159
+ const body = { ...meta };
160
+ for (const [key, consume] of Object.entries(spec.groupInputs ?? {})) {
161
+ if (!(key in body)) continue;
162
+ const input = body[key];
163
+ delete body[key];
164
+ consume({
165
+ input,
166
+ body
167
+ });
168
+ }
169
+ return brandGroup(body);
170
+ },
117
171
  singleton: spec.singleton ?? false,
118
172
  get: (config, pathOrRef) => {
119
173
  if (pathOrRef === void 0) {
@@ -127,7 +181,7 @@ function defineEntity(spec) {
127
181
  },
128
182
  list: (config, opts) => config.list(spec.kind, opts),
129
183
  tree: (config) => config.tree(spec.kind),
130
- groups: (config, opts) => config.groups(spec.kind, opts),
184
+ groups: (config, opts) => groupsWithComputed(config, config.groups(spec.kind, opts)),
131
185
  dependents: (config, path, opts) => config.dependentsOf(spec.kind, path, opts),
132
186
  create: (config, path, data) => config.apply({
133
187
  kind: spec.kind,
@@ -62,6 +62,9 @@ declare function memberLeaves(config: Config, target: string | Ref): string[];
62
62
  /** Resolve a group-bound leaf back to its qualified path (`display2` → `textVariant/display2`).
63
63
  * `undefined` when the leaf isn't a member — what validation rejects. */
64
64
  declare function memberFromLeaf(config: Config, target: string | Ref, leaf: string): string | undefined;
65
+ /** The refs of a domain written as a list of them, else `undefined`. A string list, a single ref
66
+ * and a keyed collection all answer `undefined`, so this is the one test for the list form. */
67
+ declare function refListOf(domain: unknown): string[] | undefined;
65
68
  declare function stylePropEntries(config: Config, path: string): StylePropEntry[];
66
69
  /**
67
70
  * A style prop's leaf value domain — what a consumer/model actually authors (`warning`, `carbon/2`),
@@ -405,6 +408,42 @@ declare function propValueDomain(config: Config, decl: PropDeclaration): {
405
408
  leaves: string[];
406
409
  accepts: (value: string) => boolean;
407
410
  } | null;
411
+ /**
412
+ * The icon libraries a prop's domain delegates to, in declaration order: the group path of each
413
+ * `icon:` ref in the domain, whether the domain is one ref or a list of them. A facet ref
414
+ * (`icon:phosphor#variants`) offers the set's variants rather than its glyphs and is left out. Empty
415
+ * for a domain that names no library. The one reading of the edge from a component to the libraries
416
+ * it draws from; {@link glyphPropsOf} and {@link glyphComponentOf} both walk it.
417
+ */
418
+ declare function glyphLibrariesOf(domain: unknown): string[];
419
+ /**
420
+ * The props of a component that hold a glyph, each mapped to the icon libraries that are its
421
+ * domain, in declaration order (`{ name: ['phosphor'] }` for a component whose `name` declares
422
+ * `Icon.ref('phosphor')`; two entries when it declares a list). A forward onto a layer component's
423
+ * glyph prop (`icon` → `icon/name`, the layer an `Icon`) is one too, under the libraries that prop
424
+ * names. A facet ref (`icon:phosphor#variants`) offers the set's variants, not its glyphs, so it is
425
+ * not one.
426
+ *
427
+ * A glyph is authored as the member's component and stored as its leaf, so every surface that
428
+ * hands a stored value to a render — the module's routed rule props, the renderer's registry —
429
+ * resolves these keys back to a component through the libraries named here, taking the first that
430
+ * holds the leaf.
431
+ */
432
+ declare function glyphPropsOf(config: Config, path: string, seen?: ReadonlySet<string>): Record<string, readonly string[]>;
433
+ /**
434
+ * The component that draws a library's glyphs on their own — the one whose own prop declares the
435
+ * library as its domain (`name: { type: 'variant', value: Icon.ref('phosphor') }`, or a list of
436
+ * libraries that includes it), with that prop. Derived from the components, never declared on the
437
+ * set: the edge runs from the component to the library. `undefined` when no component draws from
438
+ * it, in which case a placed glyph is the member drawn raw.
439
+ *
440
+ * Asked per library, so a system may have one drawer binding several libraries or one drawer per
441
+ * library. Two components binding the same library resolve to the first in registration order.
442
+ */
443
+ declare function glyphComponentOf(config: Config, library: string): {
444
+ path: string;
445
+ prop: string;
446
+ } | undefined;
408
447
  /** Validate a prop bag against a component's declared props — unknown props + per-value domain
409
448
  * checks. Generic: it resolves each prop's domain via {@link propValueDomain} and checks membership;
410
449
  * no per-prop-type logic here. Returns the error strings (empty = valid). Lives in config so the
@@ -443,7 +482,6 @@ declare function validateComponentProps(config: Config, componentPath: string, p
443
482
  declare function previewDefaults(config: Config, path: string,
444
483
  /** The cell's own prop values. Given, every `preview({ when })` entry that holds for the cell —
445
484
  * tested against the defaults under these values, as a rule's `when` is — adds its props. */
446
-
447
485
  cell?: Record<string, unknown>): Record<string, unknown>;
448
486
  /**
449
487
  * Only what the `preview({ when })` entries add for a cell — the props of every condition that holds
@@ -518,4 +556,4 @@ declare function propValueFromAxis(config: Config, path: string, prop: string, v
518
556
  /** The `value` a string, number or boolean prop declares as its default, per prop. */
519
557
  declare function declaredPropDefaults(props: Record<string, unknown> | undefined): Record<string, unknown>;
520
558
  //#endregion
521
- export { IndexedPreviewCondition, PreviewMatrix, PropDeclaration, StylePropEntry, StylePropertyItem, authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, holdingPreviewConditions, keyedPreviewConditions, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, validateComponentProps };
559
+ export { IndexedPreviewCondition, PreviewMatrix, PropDeclaration, StylePropEntry, StylePropertyItem, authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, holdingPreviewConditions, 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 };
@@ -187,6 +187,12 @@ function memberLeaves(config, target) {
187
187
  function memberFromLeaf(config, target, leaf) {
188
188
  return memberEntries(config, target).find((entry) => entry.leaf === leaf)?.path;
189
189
  }
190
+ /** The refs of a domain written as a list of them, else `undefined`. A string list, a single ref
191
+ * and a keyed collection all answer `undefined`, so this is the one test for the list form. */
192
+ function refListOf(domain) {
193
+ if (!Array.isArray(domain) || domain.length === 0 || !domain.every(isRef)) return;
194
+ return domain.map((entry) => entry.__ref);
195
+ }
190
196
  function stylePropEntries(config, path) {
191
197
  const cache = domainCache(stylePropEntryCache, config);
192
198
  const hit = cache.get(path);
@@ -843,9 +849,10 @@ function propOwner(config, path, prop, seen = /* @__PURE__ */ new Set()) {
843
849
  function propValueDomain(config, decl) {
844
850
  const domainRef = () => decl.value?.__ref;
845
851
  if (decl.type === "variant") {
846
- const target = domainRef();
847
- if (target) {
848
- const leaves = memberLeaves(config, target);
852
+ const single = domainRef();
853
+ const targets = refListOf(decl.value) ?? (single ? [single] : []);
854
+ if (targets.length > 0) {
855
+ const leaves = [...new Set(targets.flatMap((target) => memberLeaves(config, target)))];
849
856
  if (leaves.length === 0) return null;
850
857
  return {
851
858
  leaves,
@@ -881,6 +888,93 @@ function propValueDomain(config, decl) {
881
888
  }
882
889
  return null;
883
890
  }
891
+ /**
892
+ * The icon libraries a prop's domain delegates to, in declaration order: the group path of each
893
+ * `icon:` ref in the domain, whether the domain is one ref or a list of them. A facet ref
894
+ * (`icon:phosphor#variants`) offers the set's variants rather than its glyphs and is left out. Empty
895
+ * for a domain that names no library. The one reading of the edge from a component to the libraries
896
+ * it draws from; {@link glyphPropsOf} and {@link glyphComponentOf} both walk it.
897
+ */
898
+ function glyphLibrariesOf(domain) {
899
+ return (refListOf(domain) ?? (isRef(domain) ? [domain.__ref] : [])).filter((ref) => kindOf(ref) === "icon" && memberOf(ref) === void 0).map(pathOf);
900
+ }
901
+ /**
902
+ * The props of a component that hold a glyph, each mapped to the icon libraries that are its
903
+ * domain, in declaration order (`{ name: ['phosphor'] }` for a component whose `name` declares
904
+ * `Icon.ref('phosphor')`; two entries when it declares a list). A forward onto a layer component's
905
+ * glyph prop (`icon` → `icon/name`, the layer an `Icon`) is one too, under the libraries that prop
906
+ * names. A facet ref (`icon:phosphor#variants`) offers the set's variants, not its glyphs, so it is
907
+ * not one.
908
+ *
909
+ * A glyph is authored as the member's component and stored as its leaf, so every surface that
910
+ * hands a stored value to a render — the module's routed rule props, the renderer's registry —
911
+ * resolves these keys back to a component through the libraries named here, taking the first that
912
+ * holds the leaf.
913
+ */
914
+ function glyphPropsOf(config, path, seen = /* @__PURE__ */ new Set()) {
915
+ return glyphPropsOfBody({
916
+ config,
917
+ body: config.resolve("component", path)?.toJSON(),
918
+ seen: new Set([...seen, path])
919
+ });
920
+ }
921
+ /**
922
+ * {@link glyphPropsOf} over a body in hand rather than a stored path: what a member schema has while
923
+ * the owner is being written, before or without a path to resolve it by. `seen` holds the paths
924
+ * already on the way down, the owner's own included.
925
+ */
926
+ function glyphPropsOfBody({ config, body, seen = /* @__PURE__ */ new Set() }) {
927
+ const { props = {}, layers = {} } = body ?? {};
928
+ const out = {};
929
+ for (const [prop, declaration] of Object.entries(props)) {
930
+ const domain = declaration?.value;
931
+ if (isRef(domain) || refListOf(domain) !== void 0) {
932
+ const libraries = glyphLibrariesOf(domain);
933
+ if (libraries.length > 0) out[prop] = libraries;
934
+ continue;
935
+ }
936
+ const target = declaration?.target;
937
+ if (declaration?.type !== "forward" || !isRef(target) || kindOf(target) !== "layer") continue;
938
+ const element = layers[pathOf(target)];
939
+ if (!isRef(element) || kindOf(element) !== "component") continue;
940
+ const receiver = pathOf(element);
941
+ if (seen.has(receiver)) continue;
942
+ const libraries = glyphPropsOf(config, receiver, seen)[forwardIntoName(declaration.into, prop)];
943
+ if (libraries) out[prop] = libraries;
944
+ }
945
+ return out;
946
+ }
947
+ /**
948
+ * The component that draws a library's glyphs on their own — the one whose own prop declares the
949
+ * library as its domain (`name: { type: 'variant', value: Icon.ref('phosphor') }`, or a list of
950
+ * libraries that includes it), with that prop. Derived from the components, never declared on the
951
+ * set: the edge runs from the component to the library. `undefined` when no component draws from
952
+ * it, in which case a placed glyph is the member drawn raw.
953
+ *
954
+ * Asked per library, so a system may have one drawer binding several libraries or one drawer per
955
+ * library. Two components binding the same library resolve to the first in registration order.
956
+ */
957
+ function glyphComponentOf(config, library) {
958
+ for (const component of config.list("component")) {
959
+ const { props = {} } = component.toJSON();
960
+ for (const [prop, declaration] of Object.entries(props)) {
961
+ if (declaration?.type !== "variant") continue;
962
+ if (!glyphLibrariesOf(declaration.value).includes(library)) continue;
963
+ return {
964
+ path: component.path,
965
+ prop
966
+ };
967
+ }
968
+ }
969
+ }
970
+ /** The receiver prop a forward lands in: as spelled, the leaf of the member ref it names
971
+ * (`component:Icon#props/name` → `name`), or the forward's own name when it names none. */
972
+ function forwardIntoName(into, own) {
973
+ if (typeof into === "string") return into;
974
+ if (!isRef(into)) return own;
975
+ const member = memberOf(into) ?? into.__ref;
976
+ return member.slice(member.indexOf("/") + 1);
977
+ }
884
978
  /** Validate a prop bag against a component's declared props — unknown props + per-value domain
885
979
  * checks. Generic: it resolves each prop's domain via {@link propValueDomain} and checks membership;
886
980
  * no per-prop-type logic here. Returns the error strings (empty = valid). Lives in config so the
@@ -1045,7 +1139,7 @@ function previewMatrix(config, path) {
1045
1139
  }
1046
1140
  if (decl?.type === "boolean" && props[name]) props[name] = declaredFirst(["false", "true"], props[name] ?? []);
1047
1141
  if (decl?.type === "variant") {
1048
- const values = declaredFirst(isRef(decl.value) ? [] : listedIn(decl.value) ?? [], props[name] ?? []);
1142
+ const values = declaredFirst(isRef(decl.value) || refListOf(decl.value) !== void 0 ? [] : listedIn(decl.value) ?? [], props[name] ?? []);
1049
1143
  if (values.length > 0) props[name] = values;
1050
1144
  }
1051
1145
  }
@@ -1087,4 +1181,4 @@ function declaredPropDefaults(props) {
1087
1181
  return out;
1088
1182
  }
1089
1183
  //#endregion
1090
- export { authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, holdingPreviewConditions, keyedPreviewConditions, memberFromLeaf, memberLeaves, negatedCssValue, opacityPercentage, previewConditionalProps, previewConditions, previewDefaults, previewMatrix, propOwner, propValueDomain, propValueFromAxis, renderStyleValue, resolveComponentProps, resolveFieldValue, resolveTokenValue, resolveTokenValueUnder, routedBag, sourceVarPrefix, styleAliasesOf, styleDeclarations, stylePropEntries, stylePropLeafForToken, stylePropNegates, stylePropTokenGroup, stylePropTokenPath, stylePropTokenValues, stylePropValueFromLeaf, stylePropValueLeaves, stylePropValues, stylePropertiesWriting, stylePropertyAccepts, stylePropertyFor, stylePropertyPathFor, validateComponentProps };
1184
+ export { authoredBag, authoredValue, className, cssPropValue, cssVar, cssVarRef, declaredPropDefaults, declaredPropRouting, glyphComponentOf, glyphLibrariesOf, glyphPropsOf, glyphPropsOfBody, holdingPreviewConditions, 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 };
@@ -16,11 +16,12 @@ interface SurfaceVariantProp extends SurfacePropBase {
16
16
  readonly kind: 'variant';
17
17
  /** The values it offers, as the leaves a consumer authors. */
18
18
  readonly values: string[];
19
- /** An icon LIBRARY the values delegate to, when the domain is the group itself — a surface
20
- * offers the glyph picker rather than a select over every member. A FACET ref
21
- * (`icon:phosphor#variants`) stays a plain list: its leaves are the group node's own field
22
- * values, a menu-sized set. Mirrors {@link SurfaceSlotProp.acceptsIconLibrary}. */
23
- readonly iconLibrary?: string;
19
+ /** The icon libraries the values delegate to, when the domain is a group itself or a list of
20
+ * groups — a surface offers the glyph picker rather than a select over every member, with a
21
+ * library switcher when there is more than one. A facet ref (`icon:phosphor#variants`) stays a
22
+ * plain list: its leaves are the group node's own field values, a menu-sized set. Mirrors
23
+ * {@link SurfaceSlotProp.acceptsIconLibrary}. */
24
+ readonly iconLibraries?: readonly string[];
24
25
  }
25
26
  interface SurfaceStylePropertyProp extends SurfacePropBase {
26
27
  readonly kind: 'styleProperty';