@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
@@ -1,5 +1,5 @@
1
- import { Ref } from "../../framework/utils/refs.js";
2
- import { PathOf } from "../../framework/registered.js";
1
+ import { Ref } from "../../framework/utils/ref-grammar.js";
2
+ import { Ref as Ref$1 } from "../../framework/utils/refs.js";
3
3
  import { EntityClass } from "../../framework/defineEntity.js";
4
4
  import { Config } from "../../framework/Config.js";
5
5
  import { z } from "zod";
@@ -80,9 +80,9 @@ type FormValue<K extends StylePropValueKind> = z.infer<ReturnType<Forms[K]>>;
80
80
  * `true` is the bare prop, an empty leaf so the class is its prefix; `false` is its absence and
81
81
  * authors nothing. A numeric alias is a leaf once it is a string: `6` names `.uds-grid-cols-6`.
82
82
  */
83
- declare const STYLE_PROP_VALUE_LEAVES: { readonly [K in StylePropValueKind]: (entry: FormValue<K>, members: (target: Ref) => readonly RefMember[]) => StylePropLeaf[] };
83
+ declare const STYLE_PROP_VALUE_LEAVES: { readonly [K in StylePropValueKind]: (entry: FormValue<K>, members: (target: Ref$1) => readonly RefMember[]) => StylePropLeaf[] };
84
84
  /** The leaves one stored entry stands for; none for a value no form admits. */
85
- declare function leavesOfStylePropValue(entry: unknown, members: (target: Ref) => readonly RefMember[]): StylePropLeaf[];
85
+ declare function leavesOfStylePropValue(entry: unknown, members: (target: Ref$1) => readonly RefMember[]): StylePropLeaf[];
86
86
  /** The `values` field's member schema against `ctx`: the union of every form. With no context it is
87
87
  * the storage shape; with a config and the owner's properties it is also the judge. */
88
88
  declare function stylePropValueSchema(ctx?: StylePropValueCtx): z.ZodType<StylePropValue>;
@@ -97,9 +97,13 @@ declare function classifyStylePropValue({
97
97
  config: Config;
98
98
  entry: StylePropValue;
99
99
  }): StylePropValueKind;
100
- /** StyleProperty (`flex/direction`) — a prop a component can expose, mapped to a CSS
101
- * property with an allowed value set. Grouping (`flex`) is emergent from the path. */
102
- declare const StylePropertyEntity: EntityClass<z.ZodObject<{
100
+ /**
101
+ * `StyleProperty.ref('bg')` keeps the path literal in its type, so a component prop declared as the
102
+ * ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered `bg`
103
+ * values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather than
104
+ * the name's.
105
+ */
106
+ declare const StyleProperty: EntityClass<z.ZodObject<{
103
107
  properties: z.ZodArray<z.ZodString>;
104
108
  values: z.ZodOptional<z.ZodArray<z.ZodType<StylePropValue, unknown, z.core.$ZodTypeInternals<StylePropValue, unknown>>>>;
105
109
  responsive: z.ZodDefault<z.ZodBoolean>;
@@ -125,18 +129,7 @@ declare const StylePropertyEntity: EntityClass<z.ZodObject<{
125
129
  data: Record<string, unknown>;
126
130
  } | undefined;
127
131
  };
128
- }, Record<never, never>, false>;
129
- /**
130
- * `StyleProperty.ref('bg')` with the path kept literal in its type, so a component prop declared as
131
- * the ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered
132
- * `bg` values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather
133
- * than the name's. The value is the base ref unchanged; only the type narrows.
134
- */
135
- declare const StyleProperty: Omit<typeof StylePropertyEntity, "ref"> & {
136
- ref<const Path extends PathOf<"style-property">>(path: Path): ReturnType<typeof StylePropertyEntity.ref> & {
137
- readonly __ref: `style-property:${Path}`;
138
- };
139
- };
132
+ }, Record<never, never>, false, Record<never, never>, Record<never, never>>;
140
133
  /** `flexDirection` → `flex-direction`. A name already spelled as CSS comes back unchanged. */
141
134
  declare function toCssPropertyName(property: string): string;
142
135
  /**
@@ -406,9 +406,13 @@ function freePath(config, path) {
406
406
  for (let n = 2; config.resolve("style-property", candidate); n += 1) candidate = `${path}${n}`;
407
407
  return candidate;
408
408
  }
409
- /** StyleProperty (`flex/direction`) — a prop a component can expose, mapped to a CSS
410
- * property with an allowed value set. Grouping (`flex`) is emergent from the path. */
411
- const StylePropertyEntity = defineEntity({
409
+ /**
410
+ * `StyleProperty.ref('bg')` keeps the path literal in its type, so a component prop declared as the
411
+ * ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered `bg`
412
+ * values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather than
413
+ * the name's.
414
+ */
415
+ const StyleProperty = defineEntity({
412
416
  kind: "style-property",
413
417
  label: "Style property",
414
418
  labelPlural: "Style properties",
@@ -429,13 +433,6 @@ const StylePropertyEntity = defineEntity({
429
433
  }
430
434
  }
431
435
  });
432
- /**
433
- * `StyleProperty.ref('bg')` with the path kept literal in its type, so a component prop declared as
434
- * the ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered
435
- * `bg` values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather
436
- * than the name's. The value is the base ref unchanged; only the type narrows.
437
- */
438
- const StyleProperty = Object.assign(StylePropertyEntity, {});
439
436
  /** `flexDirection` → `flex-direction`. A name already spelled as CSS comes back unchanged. */
440
437
  function toCssPropertyName(property) {
441
438
  return property.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
@@ -1,4 +1,5 @@
1
- import { Ref } from "../../framework/utils/refs.js";
1
+ import { Ref } from "../../framework/utils/ref-grammar.js";
2
+ import { Ref as Ref$1 } from "../../framework/utils/refs.js";
2
3
  import { SubEntityClass } from "../../framework/defineSubEntity.js";
3
4
  import { EntityClass } from "../../framework/defineEntity.js";
4
5
  import { DerivedColor, Gradient } from "./color.js";
@@ -6,7 +7,7 @@ import { z } from "zod";
6
7
 
7
8
  //#region src/entities/system/Token.d.ts
8
9
  /** An override's `on`: a modifier ref, or a list of them that all hold at once. */
9
- type OverrideCondition = Ref | Ref[];
10
+ type OverrideCondition = Ref$1 | Ref$1[];
10
11
  /** The modifier paths an override's `on` names, in the order written. */
11
12
  declare function overrideModifiers(on: unknown): string[];
12
13
  /**
@@ -62,6 +63,6 @@ declare const Token: EntityClass<z.ZodObject<{
62
63
  readonly create: {
63
64
  readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
64
65
  };
65
- }, Record<never, never>, false>;
66
+ }, Record<never, never>, false, Record<never, never>, Record<never, never>>;
66
67
  //#endregion
67
68
  export { OverrideCondition, Token, TokenBody, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
@@ -3,7 +3,7 @@ import { RegisteredModifiers, RegisteredPaths, RegisteredStyleProps } from "../.
3
3
  import { Authored } from "../../framework/authoring.js";
4
4
  import { StyleValue } from "./style-bag.js";
5
5
  import { VariantValues } from "./Component.js";
6
- import { AllHTMLAttributes, ComponentPropsWithRef, ElementType, JSX, ReactNode, Ref } from "react";
6
+ import { AllHTMLAttributes, ComponentPropsWithRef, ComponentType, ElementType, JSX, ReactNode, Ref } from "react";
7
7
 
8
8
  //#region src/entities/system/defineComponent.d.ts
9
9
  /**
@@ -162,7 +162,7 @@ interface ForwardDecl<Path extends string = string> {
162
162
  */
163
163
  type PropDecl<Path extends string = string> = SlotDecl | ForwardDecl<Path> | {
164
164
  type: 'variant';
165
- value: readonly string[] | VariantValues | Ref$1;
165
+ value: readonly string[] | VariantValues | Ref$1 | readonly Ref$1[];
166
166
  required?: boolean;
167
167
  when?: PropCondition;
168
168
  } | {
@@ -231,8 +231,9 @@ interface StyleRuleInput<L extends LayerMap> {
231
231
  /** Prop → value, all of which must hold. Absent means unconditional. A value is whatever the prop
232
232
  * it names offers, so a boolean prop's condition is a boolean. */
233
233
  readonly when?: Record<string, string | number | boolean>;
234
- /** Per layer — and only a layer this component declared. */
235
- readonly layers: Partial<Record<keyof L & string, Record<string, StyleValue>>>;
234
+ /** Per layer — and only a layer this component declared. A glyph prop of a layer component takes
235
+ * the glyph component; register stores its member. */
236
+ readonly layers: Partial<Record<keyof L & string, Record<string, StyleValue | GlyphComponent>>>;
236
237
  }
237
238
  /** The TS type one prop declaration produces on the rendered component. */
238
239
  type ValueOf<D> = D extends readonly (infer Member)[] ? Member : D extends {
@@ -243,6 +244,14 @@ type ValueOf<D> = D extends readonly (infer Member)[] ? Member : D extends {
243
244
  __ref: `style-property:${infer Target}`;
244
245
  };
245
246
  } ? StylePropValue<Target> : ValueOfDeclaration<D>;
247
+ /**
248
+ * A glyph as a value: the component an icon library exports. Its `displayName` is the member it is,
249
+ * which is what register stores (`icon:phosphor/XIcon`) and what the renderer resolves back to the
250
+ * component. Sizing is the placing component's business, so the one prop every glyph takes is `size`.
251
+ */
252
+ type GlyphComponent = ComponentType<{
253
+ size?: number;
254
+ }>;
246
255
  /**
247
256
  * The members a ref-delegated domain offers, read off the registered paths: a variant whose domain
248
257
  * is `icon:phosphor` takes every registered `phosphor/<Name>` as `<Name>`, so a misspelled glyph is
@@ -262,7 +271,13 @@ type StylePropValue<Target extends string> = Target extends keyof RegisteredStyl
262
271
  type ValueOfDeclaration<D> = D extends {
263
272
  type: 'variant';
264
273
  value: infer V;
265
- } ? V extends readonly (infer Member)[] ? Member : V extends VariantValues ? keyof V & string : V extends {
274
+ } ? V extends readonly {
275
+ __ref: `icon:${string}`;
276
+ }[] ? GlyphComponent : V extends readonly Ref$1[] ? string : V extends readonly (infer Member)[] ? Member : V extends VariantValues ? keyof V & string : V extends {
277
+ __ref: `${string}#${string}`;
278
+ } ? string : V extends {
279
+ __ref: `icon:${string}`;
280
+ } ? GlyphComponent : V extends {
266
281
  __ref: `${infer Kind}:${infer Path}`;
267
282
  } ? RegisteredMembers<Kind, Path> : string : D extends {
268
283
  type: 'string';
@@ -690,4 +705,4 @@ interface DefineComponent {
690
705
  declare function definePropsFirst<const P extends PropMap<LayerPath>>(props: P & NoInfer<{ [K in keyof P]: Exact<P[K], AuthoredProp<LayerPath>> }>): ComponentBuilder<LayerMap, P>;
691
706
  declare const defineComponent: DefineComponent;
692
707
  //#endregion
693
- export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, LayerElement, LayerMap, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, StyleRuleInput, defineComponent };
708
+ export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, GlyphComponent, LayerElement, LayerMap, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, StyleRuleInput, defineComponent };
@@ -1,4 +1,5 @@
1
1
  import { canonicalCondition, isRef, kindOf, memberRef, pathOf } from "../../framework/utils/refs.js";
2
+ import { isComponentType } from "./glyph-identity.js";
2
3
  import "../../framework/preview-store.js";
3
4
  import { Component, canonicalWhen, layerRef } from "./Component.js";
4
5
  //#region src/entities/system/defineComponent.ts
@@ -44,15 +45,14 @@ function build(body) {
44
45
  * only one of them runs for any given config: the build reads `.preview()` from source with ts-morph,
45
46
  * this runs when a config is registered, and a reader can't tell which made the config it holds.
46
47
  *
47
- * A component handed as a VALUE (`icon: PlusIcon`) stores as the bare ref the registry keys it under,
48
+ * A component handed as a value (`icon: PlusIcon`) stores as the bare ref the registry keys it under,
48
49
  * so the receiving component gets the component back rather than a node.
49
50
  *
50
- * What this path CANNOT do is name a component the source could. A tag is its own name and a
51
- * component handle carries its adopted path, but a foreign function (a Phosphor glyph, say) is just
52
- * a function at runtime — its `displayName` is a label, not an identity, and two libraries may both
53
- * export `StarIcon`. Such a prop is DROPPED: an absent default renders nothing, while a guessed ref
54
- * renders the wrong glyph and reads as authored. The build resolves those from the import specifier,
55
- * which is why ~a third of the element-valued preview props in a real system only survive a build.
51
+ * OPEN: a foreign component (a Phosphor glyph) is dropped. Its export is known once its library has
52
+ * handed `register` its namespace (`glyphIdentityOf`), but a preview entry's props are stored open
53
+ * (`Preview.fields`) and never re-bound to a member, so a ref written here would reach the build as
54
+ * a package export the package row never lists. An absent default renders nothing; the build fills
55
+ * the prop from the import specifier (`extractPreviewProps`).
56
56
  */
57
57
  function storableProps(props) {
58
58
  if (props === void 0) return void 0;
@@ -68,11 +68,6 @@ function storableProps(props) {
68
68
  function isReactElement(value) {
69
69
  return typeof value === "object" && value !== null && "$$typeof" in value && "props" in value;
70
70
  }
71
- /** A component TYPE handed as a value: a function, or the object `forwardRef` / `memo` return. */
72
- function isComponentType(value) {
73
- if (typeof value === "function") return true;
74
- return typeof value === "object" && value !== null && "$$typeof" in value && !("props" in value);
75
- }
76
71
  /** A component handed as a value, as the ref that names it — `undefined` for one this path can't name. */
77
72
  function storableComponentRef(value) {
78
73
  return isComponentElement(value) && value.path ? Component.ref(value.path) : void 0;
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+
3
+ //#region src/entities/system/glyph-identity.d.ts
4
+ /** The module namespace an icon library hands `register`: its exports by name. */
5
+ type GlyphNamespace = Readonly<Record<string, unknown>>;
6
+ //#endregion
7
+ export { GlyphNamespace };
@@ -0,0 +1,153 @@
1
+ import { isRef, kindOf, memberOf, pathOf } from "../../framework/utils/refs.js";
2
+ import { spellValues } from "../../framework/value-domain.js";
3
+ import { memberLeaves } from "../../framework/projections.js";
4
+ import { z } from "zod";
5
+ //#region src/entities/system/glyph-identity.ts
6
+ const PACKAGE = "package";
7
+ /**
8
+ * Identities by component reference, for the whole process. A component is one object however many
9
+ * configs import it, and which module exports it under which name is a fact about the module, so the
10
+ * map is keyed on the object and not on any config. It is filled while a config file is evaluated
11
+ * (`Icon.group({ glyphs })` runs before `register` does) and read by the open-bag pass, which has no
12
+ * config in hand.
13
+ */
14
+ const identities = /* @__PURE__ */ new WeakMap();
15
+ /** A component type handed as a value: a function, or the object `forwardRef` / `memo` return. An
16
+ * element (which also carries `$$typeof`) has `props`; a type does not. */
17
+ function isComponentType(value) {
18
+ if (typeof value === "function") return true;
19
+ return typeof value === "object" && value !== null && "$$typeof" in value && !("props" in value);
20
+ }
21
+ /**
22
+ * Record which export each of `names` is in `namespace`, for the components among them.
23
+ *
24
+ * Only the listed names are recorded, so an alias the package also exports (Phosphor's deprecated
25
+ * `X` beside `XIcon`, the same function) resolves to the listed name. A name the namespace does not
26
+ * export as a component is skipped: a virtualized package answers a marker object for every key,
27
+ * and a marker is not a component.
28
+ *
29
+ * One object may be listed under several names of one package: Phosphor keeps a renamed glyph's old
30
+ * name as a second `*Icon` export of the same function (`ArchiveBoxIcon` is `BoxArrowDownIcon`).
31
+ * Both are members, and an author writing either holds the same reference, so one of them has to be
32
+ * the stored name. The name the component carries as its `displayName` wins when it is one of the
33
+ * listed names, since that is the one the package calls current; otherwise the first listed stays.
34
+ * The reference decides which names are in question; `displayName` only picks among them, so a
35
+ * library whose display names are not export names (Tabler) is unaffected.
36
+ *
37
+ * Recording one object under a second package is refused. The map is shared by every config in the
38
+ * process, so a stub component two test fixtures reuse under two libraries would otherwise bind to
39
+ * whichever recorded last. Recording the same identity again is a no-op.
40
+ */
41
+ function recordGlyphIdentities({ namespace, names, specifier }) {
42
+ for (const name of names) {
43
+ const value = namespace[name];
44
+ if (!isComponentType(value)) continue;
45
+ const prior = identities.get(value);
46
+ if (prior && prior.specifier !== specifier) throw new Error(`A glyph component is already recorded as \`${prior.name}\` of \`${prior.specifier}\`; it cannot also be \`${name}\` of \`${specifier}\`. Each library's glyphs have to be distinct objects.`);
47
+ if (prior && prior.name !== name && displayNameOf(value) !== name) continue;
48
+ identities.set(value, {
49
+ specifier,
50
+ name
51
+ });
52
+ }
53
+ }
54
+ /** The identity a component was recorded under, or `undefined` for one no library handed over. */
55
+ function glyphIdentityOf(value) {
56
+ return isComponentType(value) ? identities.get(value) : void 0;
57
+ }
58
+ /** An identity as the ref that spells it: `package:<specifier>#<name>`, the same spelling a
59
+ * virtualized package's marker carries for a member. */
60
+ function glyphPackageRefOf(identity) {
61
+ return { __ref: `${PACKAGE}:${identity.specifier}#${identity.name}` };
62
+ }
63
+ /** The identity a `package:<specifier>#<name>` ref spells, or `undefined` for any other value. */
64
+ function glyphIdentityOfRef(value) {
65
+ if (!isRef(value) || kindOf(value) !== PACKAGE) return void 0;
66
+ const name = memberOf(value);
67
+ return name === void 0 ? void 0 : {
68
+ specifier: pathOf(value),
69
+ name
70
+ };
71
+ }
72
+ function displayNameOf(value) {
73
+ const named = value.displayName;
74
+ return typeof named === "string" ? named : void 0;
75
+ }
76
+ function spellComponent(value) {
77
+ const named = displayNameOf(value);
78
+ return named === void 0 ? "A glyph component" : `\`${named}\``;
79
+ }
80
+ /**
81
+ * The open-bag form of an authored glyph: the imported component, read as the package export it is.
82
+ *
83
+ * The open bag runs before any owner is known, so this cannot bind the glyph to a library; it writes
84
+ * the export as a `package:` ref and the owner-bound pass binds that to the member. A component no
85
+ * library recorded is refused here, since nothing later could name it either: the library did not
86
+ * hand `register` its namespace, the package is not installed (the namespace is then a marker with
87
+ * no components), or the value is a wrapper (`memo(StarIcon)`) rather than the export.
88
+ */
89
+ const glyphPackageRef = z.custom(isComponentType).transform((value, ctx) => {
90
+ const identity = glyphIdentityOf(value);
91
+ if (!identity) {
92
+ ctx.addIssue({
93
+ code: "custom",
94
+ message: `${spellComponent(value)} is not a glyph of any icon library this config declares. Declare the library's exports on its group (\`Icon.group({ glyphs: <Namespace>, … })\`) and check the package is installed; a wrapped glyph (\`memo(StarIcon)\`) is a different object from the export and cannot be bound.`
95
+ });
96
+ return z.NEVER;
97
+ }
98
+ return glyphPackageRefOf(identity);
99
+ });
100
+ glyphPackageRef._zod.toJSONSchema = () => ({ not: {} });
101
+ /**
102
+ * The owner-bound form of a glyph for one library: a component or its `package:` ref, answered as
103
+ * the member name so the domain binds it like a written leaf. Refused, naming the reason, when the
104
+ * export belongs to another module than the library draws from or is not one of its members.
105
+ */
106
+ function glyphOfLibrary({ library, module, leaves }) {
107
+ return z.custom((value) => isComponentType(value) || glyphIdentityOfRef(value) !== void 0, { error: () => `a glyph component of the library (one of ${spellValues(leaves)})` }).transform((value, ctx) => {
108
+ const identity = isRef(value) ? glyphIdentityOfRef(value) : glyphIdentityOf(value);
109
+ if (!identity) {
110
+ ctx.addIssue({
111
+ code: "custom",
112
+ message: `${spellComponent(value)} is not a glyph of any icon library this config declares.`
113
+ });
114
+ return z.NEVER;
115
+ }
116
+ if (identity.specifier !== module) {
117
+ ctx.addIssue({
118
+ code: "custom",
119
+ message: `\`${identity.name}\` is an export of \`${identity.specifier}\`, which the library \`${library}\` does not draw from${module === void 0 ? " (it names no package)" : ` (its package is \`${module}\`)`}.`
120
+ });
121
+ return z.NEVER;
122
+ }
123
+ if (!leaves.includes(identity.name)) {
124
+ ctx.addIssue({
125
+ code: "custom",
126
+ message: `\`${identity.name}\` is not a member of the library \`${library}\`${leaves.length === 0 ? ", which has no members; the package may not be installed" : ""}.`
127
+ });
128
+ return z.NEVER;
129
+ }
130
+ return identity.name;
131
+ });
132
+ }
133
+ /**
134
+ * The glyph form for a key that reaches a library through a forward (`icon` → `icon/name`): the
135
+ * component or its `package:` ref, answered as the bare member name. The forwarded key stores the
136
+ * leaf rather than a member ref, and the emitter and the renderer resolve it through the same
137
+ * libraries (`glyphPropsOf`), taking the first that holds it. Each library's arm refuses by cause,
138
+ * so a glyph of a library the key does not reach says so.
139
+ */
140
+ function glyphLeafOf({ config, libraries }) {
141
+ const arms = libraries.map((library) => {
142
+ const named = config.groupMeta("icon", library)?.package;
143
+ return glyphOfLibrary({
144
+ library,
145
+ module: isRef(named) ? pathOf(named) : void 0,
146
+ leaves: memberLeaves(config, `icon:${library}`)
147
+ });
148
+ });
149
+ if (arms.length === 1) return arms[0];
150
+ return z.union(arms);
151
+ }
152
+ //#endregion
153
+ export { glyphLeafOf, glyphOfLibrary, glyphPackageRef, isComponentType, recordGlyphIdentities };
@@ -0,0 +1,11 @@
1
+ import type { Config } from '../../framework/Config';
2
+ /**
3
+ * A prop whose domain is a list of icon-library refs resolves a value by leaf alone, so one leaf
4
+ * held by two of its libraries would draw whichever the host looked up first. This refuses that
5
+ * state, naming the component, the prop and both libraries.
6
+ *
7
+ * Declared from both `Component` and `Icon`, since `assertInvariants` runs only the patched kind's
8
+ * rules: a component binding two libraries is a component write, and a glyph added to one of them
9
+ * later is an icon write, and either can create the collision.
10
+ */
11
+ export declare function iconDomainInvariants(config: Config): string[];
@@ -0,0 +1,178 @@
1
+ import { isRef, kindOf, memberOf, pathOf } from "../../framework/utils/refs.js";
2
+ import { collectBag, isPlainRecord } from "./style-bag.js";
3
+ import { createSliceMemo } from "../../framework/memo.js";
4
+ import { glyphLibrariesOf, memberLeaves, refListOf } from "../../framework/projections.js";
5
+ //#region src/entities/system/icon-domain-invariants.ts
6
+ /**
7
+ * A prop whose domain is a list of icon-library refs resolves a value by leaf alone, so one leaf
8
+ * held by two of its libraries would draw whichever the host looked up first. This refuses that
9
+ * state, naming the component, the prop and both libraries.
10
+ *
11
+ * Declared from both `Component` and `Icon`, since `assertInvariants` runs only the patched kind's
12
+ * rules: a component binding two libraries is a component write, and a glyph added to one of them
13
+ * later is an icon write, and either can create the collision.
14
+ */
15
+ function iconDomainInvariants(config) {
16
+ return [...collisions(config), ...unboundGlyphs(config)];
17
+ }
18
+ /**
19
+ * A glyph authored as its component leaves the open-bag pass as a `package:` export ref, and the
20
+ * owner-bound pass binds that to the member when the key's domain is a library drawing from that
21
+ * module. One left in a bag is a glyph nothing bound, and this refuses it by what went wrong: no
22
+ * library draws from its module, the library does but does not list the export (a library with no
23
+ * members is usually an uninstalled package), or the library lists it and the key's domain is not
24
+ * that library. Only the open-bag pass writes a `package:` ref into a bag value, so a leftover is
25
+ * always one of these. Without the check the ref reaches the build as a package export the package
26
+ * row never lists, and fails there with the wrong cause.
27
+ */
28
+ const unboundGlyphs = createSliceMemo({
29
+ kinds: ["component", "icon"],
30
+ compute: (config) => {
31
+ const problems = [];
32
+ const librariesByModule = librariesByModuleOf(config);
33
+ const leafSetOf = leafSetsOf(config);
34
+ for (const item of config.list("component")) {
35
+ const body = config.resolve("component", item.path)?.toJSON();
36
+ for (const site of glyphBagSites({
37
+ config,
38
+ path: item.path,
39
+ body
40
+ })) for (const [key, value] of Object.entries(site.bag)) {
41
+ const glyph = packageExportOf(value);
42
+ if (!glyph) continue;
43
+ const holders = librariesByModule.get(glyph.specifier) ?? [];
44
+ const at = `Component "${item.path}" key "${key}"`;
45
+ if (holders.length === 0) {
46
+ problems.push(`${at} holds the glyph \`${glyph.name}\` of \`${glyph.specifier}\`, and no icon library draws from that package; declare the library, or hand its exports to the one that does (\`Icon.group({ glyphs })\`).`);
47
+ continue;
48
+ }
49
+ const listing = holders.filter((library) => leafSetOf(`icon:${library}`).has(glyph.name));
50
+ if (listing.length === 0) {
51
+ const empty = holders.every((library) => leafSetOf(`icon:${library}`).size === 0);
52
+ problems.push(`${at} holds \`${glyph.name}\`, which is not a member of ${spellLibraries(holders)}${empty ? "; the library has no members, so the package may not be installed" : ""}.`);
53
+ continue;
54
+ }
55
+ problems.push(`${at} holds \`${glyph.name}\`, a glyph of ${spellLibraries(listing)}, which that key does not draw from; a glyph belongs on a prop whose domain names its library, or a forward onto one.`);
56
+ }
57
+ }
58
+ return problems;
59
+ }
60
+ });
61
+ /**
62
+ * A component's bags with the element each one is props for: a rule's layer bags with the layer's
63
+ * element, an anatomy node's props with the node's element, `defaultProps` with the component itself.
64
+ * A bag whose element is a component ref that resolves to nothing is left out: the owner-bound pass
65
+ * had no declaration to bind against, and the dangling element is the ref graph's complaint, not a
66
+ * glyph on the wrong prop.
67
+ */
68
+ function glyphBagSites({ config, path, body }) {
69
+ const owner = body;
70
+ const sites = [];
71
+ const keep = () => {};
72
+ for (const rule of Object.values(owner?.styles ?? {})) {
73
+ if (!isPlainRecord(rule?.layers)) continue;
74
+ for (const [layer, bag] of Object.entries(rule.layers)) collectBag(bag, keep, sites, owner?.layers?.[layer]);
75
+ }
76
+ for (const node of Object.values(owner?.anatomy ?? {})) {
77
+ if (!isPlainRecord(node)) continue;
78
+ collectBag(node.props, keep, sites, node.element);
79
+ }
80
+ collectBag(owner?.defaultProps, keep, sites, { __ref: `component:${path}` });
81
+ return sites.filter((site) => !(isRef(site.element) && kindOf(site.element) === "component" && !config.resolveRef(site.element.__ref)));
82
+ }
83
+ /** The library paths drawing from each package specifier, off every icon group's `package` ref. */
84
+ function librariesByModuleOf(config) {
85
+ const byModule = /* @__PURE__ */ new Map();
86
+ for (const group of config.groups("icon")) {
87
+ const named = group.meta.package;
88
+ if (!isRef(named)) continue;
89
+ const specifier = pathOf(named);
90
+ byModule.set(specifier, [...byModule.get(specifier) ?? [], group.path]);
91
+ }
92
+ return byModule;
93
+ }
94
+ /** The export a `package:<specifier>#<name>` ref spells, or `undefined` for any other value. */
95
+ function packageExportOf(value) {
96
+ if (!isRef(value) || kindOf(value) !== "package") return void 0;
97
+ const name = memberOf(value);
98
+ return name === void 0 ? void 0 : {
99
+ specifier: pathOf(value),
100
+ name
101
+ };
102
+ }
103
+ function spellLibraries(libraries) {
104
+ const named = libraries.map((library) => `"${library}"`);
105
+ return libraries.length === 1 ? `the library ${named[0]}` : `the libraries ${named.join(", ")}`;
106
+ }
107
+ /**
108
+ * Keyed on the two slices the check reads, so the second kind's run within one registration is a
109
+ * hit, and a write to any other kind never pays for it. A library's leaf set is keyed on the icon
110
+ * slice alone: a component edit re-walks the list-valued props against sets that are already built.
111
+ */
112
+ const collisions = createSliceMemo({
113
+ kinds: ["component", "icon"],
114
+ compute: (config) => {
115
+ const problems = [];
116
+ const leafSetOf = leafSetsOf(config);
117
+ for (const item of config.list("component")) {
118
+ const body = config.resolve("component", item.path)?.toJSON();
119
+ for (const [prop, decl] of Object.entries(body?.props ?? {})) {
120
+ const refs = listedLibraryRefsOf(decl);
121
+ if (!refs) continue;
122
+ const heldBy = /* @__PURE__ */ new Map();
123
+ const shared = /* @__PURE__ */ new Map();
124
+ for (const target of refs) for (const leaf of leafSetOf(target)) {
125
+ const other = heldBy.get(leaf);
126
+ if (other === void 0) {
127
+ heldBy.set(leaf, target);
128
+ continue;
129
+ }
130
+ if (other === target) continue;
131
+ const pair = `${other} + ${target}`;
132
+ shared.set(pair, [...shared.get(pair) ?? [], leaf]);
133
+ }
134
+ for (const [pair, leaves] of shared) {
135
+ const [first, second] = pair.split(" + ");
136
+ problems.push(`Component "${item.path}" prop "${prop}" binds icon libraries "${first}" and "${second}", which both ship ${spellLeaves(leaves)}; a leaf has to name one glyph.`);
137
+ }
138
+ }
139
+ }
140
+ return problems;
141
+ }
142
+ });
143
+ /** One leaf set per referenced library, filled on demand and kept while the icon slice stands. */
144
+ const leafSetsOf = createSliceMemo({
145
+ kinds: ["icon"],
146
+ compute: (config) => {
147
+ const sets = /* @__PURE__ */ new Map();
148
+ return (target) => {
149
+ const hit = sets.get(target);
150
+ if (hit) return hit;
151
+ const built = new Set(memberLeaves(config, target));
152
+ sets.set(target, built);
153
+ return built;
154
+ };
155
+ }
156
+ });
157
+ /** The colliding names, capped so two libraries sharing hundreds refuse in one sentence. */
158
+ function spellLeaves(leaves) {
159
+ const shown = leaves.slice(0, 8).map((leaf) => `"${leaf}"`);
160
+ const rest = leaves.length - shown.length;
161
+ const named = rest > 0 ? `${shown.join(", ")} and ${rest} more` : shown.join(", ");
162
+ return leaves.length === 1 ? `a glyph named ${named}` : `glyphs named ${named}`;
163
+ }
164
+ /**
165
+ * The icon-library refs of a variant prop whose domain is a list of refs, else `undefined`. Each
166
+ * element is put through `glyphLibrariesOf` on its own, so the ref comes back whole: the leaf sets
167
+ * are read at the source the ref names, which a group path alone cannot say. A facet ref
168
+ * (`icon:a#variants`) is not a library, and a ref of another kind is left to that kind's rules.
169
+ */
170
+ function listedLibraryRefsOf(decl) {
171
+ if (decl === null || typeof decl !== "object") return void 0;
172
+ const { type, value } = decl;
173
+ if (type !== "variant") return void 0;
174
+ const listed = refListOf(value)?.filter((target) => glyphLibrariesOf({ __ref: target }).length > 0);
175
+ return listed && listed.length > 0 ? listed : void 0;
176
+ }
177
+ //#endregion
178
+ export { iconDomainInvariants };