@yahoo/uds-create-config 2.44.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. package/README.md +129 -0
  2. package/dist/ai-lanes.d.ts +23 -0
  3. package/dist/ai-lanes.js +34 -0
  4. package/dist/authoring/define-component.d.ts +85 -0
  5. package/dist/configs/CanvasConfig.d.ts +3237 -0
  6. package/dist/configs/CanvasConfig.js +332 -0
  7. package/dist/configs/build-options.d.ts +10 -0
  8. package/dist/configs/build-options.js +68 -0
  9. package/dist/configs/platform.d.ts +15 -0
  10. package/dist/configs/platform.js +17 -0
  11. package/dist/configs/react-native-system.d.ts +3026 -0
  12. package/dist/configs/react-native-system.js +64 -0
  13. package/dist/configs/system.d.ts +2771 -0
  14. package/dist/configs/system.js +74 -0
  15. package/dist/css/properties.generated.d.ts +14 -0
  16. package/dist/css/properties.generated.js +6260 -0
  17. package/dist/css/value-type.d.ts +48 -0
  18. package/dist/css/value-type.js +131 -0
  19. package/dist/css/values.d.ts +33 -0
  20. package/dist/css/values.js +116 -0
  21. package/dist/entities/native/NativeModifier.d.ts +25 -0
  22. package/dist/entities/native/NativeModifier.js +41 -0
  23. package/dist/entities/native/NativeSettings.d.ts +44 -0
  24. package/dist/entities/native/NativeSettings.js +39 -0
  25. package/dist/entities/native/NativeStyleProperty.d.ts +355 -0
  26. package/dist/entities/native/NativeStyleProperty.js +28 -0
  27. package/dist/entities/native/NativeToken.d.ts +31 -0
  28. package/dist/entities/native/NativeToken.js +49 -0
  29. package/dist/entities/system/CanvasRole.d.ts +34 -0
  30. package/dist/entities/system/CanvasRole.js +45 -0
  31. package/dist/entities/system/Component.d.ts +1287 -0
  32. package/dist/entities/system/Component.js +1840 -0
  33. package/dist/entities/system/Composite.d.ts +12 -0
  34. package/dist/entities/system/Composite.js +26 -0
  35. package/dist/entities/system/Device.d.ts +11 -0
  36. package/dist/entities/system/Device.js +18 -0
  37. package/dist/entities/system/File.d.ts +72 -0
  38. package/dist/entities/system/File.js +136 -0
  39. package/dist/entities/system/Font.d.ts +48 -0
  40. package/dist/entities/system/Font.js +56 -0
  41. package/dist/entities/system/GlobalStyle.d.ts +26 -0
  42. package/dist/entities/system/GlobalStyle.js +54 -0
  43. package/dist/entities/system/Guidance.d.ts +85 -0
  44. package/dist/entities/system/Guidance.js +59 -0
  45. package/dist/entities/system/GuidanceStyle.d.ts +14 -0
  46. package/dist/entities/system/GuidanceStyle.js +8 -0
  47. package/dist/entities/system/Icon.d.ts +46 -0
  48. package/dist/entities/system/Icon.js +46 -0
  49. package/dist/entities/system/LinkedSystem.d.ts +504 -0
  50. package/dist/entities/system/LinkedSystem.js +814 -0
  51. package/dist/entities/system/Modifier.d.ts +92 -0
  52. package/dist/entities/system/Modifier.js +120 -0
  53. package/dist/entities/system/Motion.d.ts +28 -0
  54. package/dist/entities/system/Motion.js +48 -0
  55. package/dist/entities/system/Operation.d.ts +100 -0
  56. package/dist/entities/system/Operation.js +236 -0
  57. package/dist/entities/system/Package.d.ts +42 -0
  58. package/dist/entities/system/Package.js +75 -0
  59. package/dist/entities/system/Settings.d.ts +94 -0
  60. package/dist/entities/system/Settings.js +166 -0
  61. package/dist/entities/system/StyleProperty.d.ts +135 -0
  62. package/dist/entities/system/StyleProperty.js +438 -0
  63. package/dist/entities/system/Token.d.ts +67 -0
  64. package/dist/entities/system/Token.js +129 -0
  65. package/dist/entities/system/Tool.d.ts +134 -0
  66. package/dist/entities/system/Tool.js +124 -0
  67. package/dist/entities/system/color.d.ts +64 -0
  68. package/dist/entities/system/color.js +123 -0
  69. package/dist/entities/system/component-module.d.ts +63 -0
  70. package/dist/entities/system/component-module.js +258 -0
  71. package/dist/entities/system/copy-plan.d.ts +98 -0
  72. package/dist/entities/system/copy-plan.js +451 -0
  73. package/dist/entities/system/defineComponent.d.ts +428 -0
  74. package/dist/entities/system/defineComponent.js +337 -0
  75. package/dist/entities/system/element.d.ts +101 -0
  76. package/dist/entities/system/element.js +171 -0
  77. package/dist/entities/system/icon-library.d.ts +64 -0
  78. package/dist/entities/system/icon-library.js +112 -0
  79. package/dist/entities/system/icon-metadata.d.ts +72 -0
  80. package/dist/entities/system/icon-metadata.js +111 -0
  81. package/dist/entities/system/link-declaration.d.ts +27 -0
  82. package/dist/entities/system/link-declaration.js +64 -0
  83. package/dist/entities/system/link-plan.d.ts +160 -0
  84. package/dist/entities/system/link-plan.js +770 -0
  85. package/dist/entities/system/runtime-modules.d.ts +25 -0
  86. package/dist/entities/system/runtime-modules.js +38 -0
  87. package/dist/entities/system/style-bag.d.ts +20 -0
  88. package/dist/entities/system/style-bag.js +246 -0
  89. package/dist/entities/system/token-index.d.ts +89 -0
  90. package/dist/entities/system/token-index.js +112 -0
  91. package/dist/framework/Config.d.ts +2472 -0
  92. package/dist/framework/Config.js +5023 -0
  93. package/dist/framework/Entity.d.ts +127 -0
  94. package/dist/framework/Entity.js +125 -0
  95. package/dist/framework/authoring.d.ts +24 -0
  96. package/dist/framework/authoring.js +7 -0
  97. package/dist/framework/change-hooks.d.ts +119 -0
  98. package/dist/framework/change-hooks.js +131 -0
  99. package/dist/framework/changes.d.ts +88 -0
  100. package/dist/framework/changes.js +246 -0
  101. package/dist/framework/class-names.d.ts +260 -0
  102. package/dist/framework/class-names.js +630 -0
  103. package/dist/framework/coalesce.d.ts +25 -0
  104. package/dist/framework/coalesce.js +60 -0
  105. package/dist/framework/config-op.d.ts +76 -0
  106. package/dist/framework/config-op.js +43 -0
  107. package/dist/framework/config-operation-kind.d.ts +14 -0
  108. package/dist/framework/config-operation-kind.js +14 -0
  109. package/dist/framework/config-ops.d.ts +34 -0
  110. package/dist/framework/config-ops.js +187 -0
  111. package/dist/framework/defineConfig.d.ts +196 -0
  112. package/dist/framework/defineConfig.js +100 -0
  113. package/dist/framework/defineDerivedEntity.d.ts +74 -0
  114. package/dist/framework/defineDerivedEntity.js +83 -0
  115. package/dist/framework/defineEntity.d.ts +318 -0
  116. package/dist/framework/defineEntity.js +183 -0
  117. package/dist/framework/defineSubEntity.d.ts +148 -0
  118. package/dist/framework/defineSubEntity.js +121 -0
  119. package/dist/framework/derived-mutations.d.ts +20 -0
  120. package/dist/framework/derived-mutations.js +38 -0
  121. package/dist/framework/entity-search.d.ts +29 -0
  122. package/dist/framework/entity-search.js +117 -0
  123. package/dist/framework/inline-sources.d.ts +49 -0
  124. package/dist/framework/inline-sources.js +388 -0
  125. package/dist/framework/layer-styles.d.ts +25 -0
  126. package/dist/framework/layer-styles.js +49 -0
  127. package/dist/framework/memo.d.ts +48 -0
  128. package/dist/framework/memo.js +84 -0
  129. package/dist/framework/overlay.d.ts +49 -0
  130. package/dist/framework/overlay.js +17 -0
  131. package/dist/framework/plan.d.ts +49 -0
  132. package/dist/framework/plan.js +73 -0
  133. package/dist/framework/projections.d.ts +480 -0
  134. package/dist/framework/projections.js +974 -0
  135. package/dist/framework/prop-surface.d.ts +132 -0
  136. package/dist/framework/prop-surface.js +278 -0
  137. package/dist/framework/ref-graph.d.ts +71 -0
  138. package/dist/framework/ref-graph.js +158 -0
  139. package/dist/framework/ref-integrity.d.ts +55 -0
  140. package/dist/framework/ref-integrity.js +171 -0
  141. package/dist/framework/registered.d.ts +95 -0
  142. package/dist/framework/registry-dir.d.ts +42 -0
  143. package/dist/framework/registry-dir.js +48 -0
  144. package/dist/framework/registry.d.ts +300 -0
  145. package/dist/framework/registry.js +104 -0
  146. package/dist/framework/rejection.d.ts +58 -0
  147. package/dist/framework/rejection.js +111 -0
  148. package/dist/framework/rename-inference.d.ts +26 -0
  149. package/dist/framework/rename-inference.js +50 -0
  150. package/dist/framework/render-spec.d.ts +296 -0
  151. package/dist/framework/render-spec.js +809 -0
  152. package/dist/framework/schema-version.d.ts +140 -0
  153. package/dist/framework/schema-version.js +214 -0
  154. package/dist/framework/schemas.d.ts +109 -0
  155. package/dist/framework/schemas.js +615 -0
  156. package/dist/framework/session.d.ts +196 -0
  157. package/dist/framework/session.js +367 -0
  158. package/dist/framework/signature.d.ts +15 -0
  159. package/dist/framework/signature.js +83 -0
  160. package/dist/framework/snapshot.d.ts +115 -0
  161. package/dist/framework/snapshot.js +277 -0
  162. package/dist/framework/source-integrity.d.ts +226 -0
  163. package/dist/framework/source-integrity.js +478 -0
  164. package/dist/framework/sources.d.ts +211 -0
  165. package/dist/framework/sources.js +89 -0
  166. package/dist/framework/utils/compiled-schema.d.ts +23 -0
  167. package/dist/framework/utils/compiled-schema.js +46 -0
  168. package/dist/framework/utils/content-key.d.ts +15 -0
  169. package/dist/framework/utils/content-key.js +33 -0
  170. package/dist/framework/utils/enumerated.d.ts +13 -0
  171. package/dist/framework/utils/enumerated.js +19 -0
  172. package/dist/framework/utils/field-path.d.ts +95 -0
  173. package/dist/framework/utils/field-path.js +136 -0
  174. package/dist/framework/utils/field-schema.d.ts +55 -0
  175. package/dist/framework/utils/field-schema.js +183 -0
  176. package/dist/framework/utils/group.d.ts +37 -0
  177. package/dist/framework/utils/group.js +13 -0
  178. package/dist/framework/utils/package-path.d.ts +37 -0
  179. package/dist/framework/utils/package-path.js +61 -0
  180. package/dist/framework/utils/refs.d.ts +194 -0
  181. package/dist/framework/utils/refs.js +403 -0
  182. package/dist/framework/validate-spec.d.ts +27 -0
  183. package/dist/framework/validate-spec.js +45 -0
  184. package/dist/framework/value-domain.d.ts +26 -0
  185. package/dist/framework/value-domain.js +202 -0
  186. package/dist/framework/views-facade.d.ts +15 -0
  187. package/dist/framework/views-facade.js +19 -0
  188. package/dist/index.d.ts +90 -42
  189. package/dist/index.js +86 -37
  190. package/dist/migrations/2.0.0/raw-css.d.ts +11 -0
  191. package/dist/migrations/2.0.0/raw-css.js +72 -0
  192. package/dist/migrations/2.0.0/v1-artifact.d.ts +1559 -0
  193. package/dist/migrations/2.0.0/v1-artifact.js +1018 -0
  194. package/dist/migrations/20260908171448_settings_as_kind.d.ts +22 -0
  195. package/dist/migrations/20260908171448_settings_as_kind.js +89 -0
  196. package/dist/migrations/20260908194245_visible_state_pointer.d.ts +19 -0
  197. package/dist/migrations/20260908194245_visible_state_pointer.js +115 -0
  198. package/dist/migrations/20260909183001_icon_knockout_variants.d.ts +18 -0
  199. package/dist/migrations/20260909183001_icon_knockout_variants.js +8 -0
  200. package/dist/migrations/20260910212453_linked_system_link_ops.d.ts +13 -0
  201. package/dist/migrations/20260910212453_linked_system_link_ops.js +8 -0
  202. package/dist/migrations/index.d.ts +2 -0
  203. package/dist/migrations/index.js +2 -0
  204. package/dist/react-native/style-keys.generated.d.ts +10 -0
  205. package/dist/react-native/style-keys.generated.js +167 -0
  206. package/dist/renderer/RendererErrorBoundary.d.ts +32 -5
  207. package/dist/renderer/RendererErrorBoundary.js +59 -10
  208. package/dist/renderer/UdsRenderer.d.ts +11 -10
  209. package/dist/renderer/UdsRenderer.js +1 -1
  210. package/dist/renderer/assetEntries.d.ts +37 -0
  211. package/dist/renderer/assetEntries.js +39 -0
  212. package/dist/renderer/assetRenderable.d.ts +14 -6
  213. package/dist/renderer/assetRenderable.js +8 -2
  214. package/dist/renderer/componentEntry.d.ts +58 -0
  215. package/dist/renderer/componentEntry.js +71 -0
  216. package/dist/renderer/icon-library.d.ts +20 -0
  217. package/dist/renderer/index.d.ts +7 -7
  218. package/dist/renderer/index.js +5 -5
  219. package/dist/renderer/primitives/SlotRenderer.d.ts +1 -1
  220. package/dist/renderer/primitives/SlotRenderer.js +1 -1
  221. package/dist/renderer/runtimeComponent.d.ts +33 -0
  222. package/dist/renderer/runtimeComponent.js +113 -0
  223. package/dist/renderer/spec-content.d.ts +43 -0
  224. package/dist/renderer/spec-content.js +95 -0
  225. package/dist/renderer/wrapRegistry.d.ts +22 -37
  226. package/dist/renderer/wrapRegistry.js +10 -20
  227. package/dist/renderer/wrappers/event-bridge.js +6 -5
  228. package/dist/renderer/wrappers/hex-normalize.js +6 -5
  229. package/dist/renderer/wrappers/html-aliases.d.ts +7 -2
  230. package/dist/renderer/wrappers/html-aliases.js +36 -19
  231. package/dist/renderer/wrappers/inline-styles.js +7 -6
  232. package/dist/spec/asset-jsx.d.ts +18 -5
  233. package/dist/spec/asset-jsx.js +27 -16
  234. package/dist/spec/collapse-text-labels.d.ts +5 -15
  235. package/dist/spec/collapse-text-labels.js +26 -19
  236. package/dist/spec/empty-node-slots.d.ts +56 -34
  237. package/dist/spec/empty-node-slots.js +87 -47
  238. package/dist/spec/index.d.ts +21 -10
  239. package/dist/spec/index.js +21 -10
  240. package/dist/spec/jsxToSpec.d.ts +14 -9
  241. package/dist/spec/jsxToSpec.js +20 -3
  242. package/dist/spec/preview-elements.d.ts +63 -0
  243. package/dist/spec/preview-elements.js +89 -0
  244. package/dist/spec/rewrite-refs.d.ts +58 -0
  245. package/dist/spec/rewrite-refs.js +250 -0
  246. package/dist/spec/specToJsx.js +18 -29
  247. package/dist/tsconfig.tsbuildinfo +1 -1
  248. package/package.json +39 -52
  249. package/dist/AssetGroup.d.ts +0 -77
  250. package/dist/AssetGroup.js +0 -125
  251. package/dist/Component.d.ts +0 -333
  252. package/dist/Component.js +0 -1061
  253. package/dist/ComponentGroup.d.ts +0 -22
  254. package/dist/ComponentGroup.js +0 -51
  255. package/dist/CompositeStyle.d.ts +0 -30
  256. package/dist/CompositeStyle.js +0 -59
  257. package/dist/Config.d.ts +0 -528
  258. package/dist/Config.js +0 -1712
  259. package/dist/Mode.d.ts +0 -41
  260. package/dist/Mode.js +0 -81
  261. package/dist/Modifier.d.ts +0 -51
  262. package/dist/Modifier.js +0 -97
  263. package/dist/MotionDef.d.ts +0 -81
  264. package/dist/MotionDef.js +0 -212
  265. package/dist/Props.d.ts +0 -332
  266. package/dist/Props.js +0 -35
  267. package/dist/Provider.d.ts +0 -20
  268. package/dist/Provider.js +0 -14
  269. package/dist/StyleProp.d.ts +0 -145
  270. package/dist/StyleProp.js +0 -239
  271. package/dist/Token.d.ts +0 -76
  272. package/dist/Token.js +0 -133
  273. package/dist/TokenGroup.d.ts +0 -33
  274. package/dist/TokenGroup.js +0 -69
  275. package/dist/asset-kind.d.ts +0 -55
  276. package/dist/asset-kind.js +0 -29
  277. package/dist/asset-value.d.ts +0 -23
  278. package/dist/asset-value.js +0 -40
  279. package/dist/brands.d.ts +0 -30
  280. package/dist/brands.js +0 -20
  281. package/dist/captureCallerPath.d.ts +0 -48
  282. package/dist/captureCallerPath.js +0 -95
  283. package/dist/colorExpressions.d.ts +0 -131
  284. package/dist/colorExpressions.js +0 -148
  285. package/dist/component-referrers.d.ts +0 -30
  286. package/dist/component-referrers.js +0 -40
  287. package/dist/config-diagnostics.d.ts +0 -21
  288. package/dist/config-diagnostics.js +0 -12
  289. package/dist/defineAssetGroup.d.ts +0 -200
  290. package/dist/defineAssetGroup.js +0 -323
  291. package/dist/defineProvider.d.ts +0 -29
  292. package/dist/defineProvider.js +0 -60
  293. package/dist/element-marker.d.ts +0 -63
  294. package/dist/element-marker.js +0 -113
  295. package/dist/entity-utils.d.ts +0 -56
  296. package/dist/entity-utils.js +0 -105
  297. package/dist/extension.d.ts +0 -55
  298. package/dist/extension.js +0 -214
  299. package/dist/factories.d.ts +0 -863
  300. package/dist/factories.js +0 -475
  301. package/dist/font-face.d.ts +0 -25
  302. package/dist/font-face.js +0 -72
  303. package/dist/foreign-component-name.d.ts +0 -21
  304. package/dist/foreign-component-name.js +0 -42
  305. package/dist/interpolate.d.ts +0 -20
  306. package/dist/interpolate.js +0 -10
  307. package/dist/jsx/__fixtures__/cross-component-preview.d.ts +0 -3
  308. package/dist/jsx/__fixtures__/cross-component-preview.js +0 -15
  309. package/dist/jsx/jsx-dev-runtime.d.ts +0 -15
  310. package/dist/jsx/jsx-dev-runtime.js +0 -11
  311. package/dist/jsx/jsx-runtime.d.ts +0 -48
  312. package/dist/jsx/jsx-runtime.js +0 -305
  313. package/dist/linked-system-pins.d.ts +0 -30
  314. package/dist/linked-system-pins.js +0 -45
  315. package/dist/linkedNodePrefixes.d.ts +0 -73
  316. package/dist/linkedNodePrefixes.js +0 -99
  317. package/dist/markers.d.ts +0 -235
  318. package/dist/markers.js +0 -70
  319. package/dist/motion-constants.d.ts +0 -37
  320. package/dist/motion-constants.js +0 -61
  321. package/dist/native/NativeConfig.d.ts +0 -56
  322. package/dist/native/NativeConfig.js +0 -162
  323. package/dist/native/assets.d.ts +0 -20
  324. package/dist/native/assets.js +0 -17
  325. package/dist/native/components.d.ts +0 -139
  326. package/dist/native/components.js +0 -67
  327. package/dist/native/index.d.ts +0 -18
  328. package/dist/native/index.js +0 -17
  329. package/dist/native/modes.d.ts +0 -10
  330. package/dist/native/modes.js +0 -11
  331. package/dist/native/types.d.ts +0 -95
  332. package/dist/native/types.js +0 -0
  333. package/dist/native/validation.d.ts +0 -8
  334. package/dist/native/validation.js +0 -885
  335. package/dist/native/values.d.ts +0 -7
  336. package/dist/native/values.js +0 -15
  337. package/dist/native.d.ts +0 -12
  338. package/dist/native.js +0 -10
  339. package/dist/refs.d.ts +0 -204
  340. package/dist/refs.js +0 -129
  341. package/dist/registry-aliases.d.ts +0 -33
  342. package/dist/registry-aliases.js +0 -35
  343. package/dist/registry-dir.d.ts +0 -22
  344. package/dist/registry-dir.js +0 -10
  345. package/dist/renderer/makeRegistry.d.ts +0 -34
  346. package/dist/renderer/makeRegistry.js +0 -52
  347. package/dist/renderer/makeUdsRenderer.d.ts +0 -13
  348. package/dist/renderer/makeUdsRenderer.js +0 -51
  349. package/dist/renderer/wrappers/component-slots.d.ts +0 -41
  350. package/dist/renderer/wrappers/component-slots.js +0 -66
  351. package/dist/renderer/wrappers/slot-resolution.d.ts +0 -25
  352. package/dist/renderer/wrappers/slot-resolution.js +0 -68
  353. package/dist/renderer/wrappers/void-elements.d.ts +0 -37
  354. package/dist/renderer/wrappers/void-elements.js +0 -40
  355. package/dist/spec/layer-props.d.ts +0 -52
  356. package/dist/spec/layer-props.js +0 -149
  357. package/dist/spec/preview-controls.d.ts +0 -44
  358. package/dist/spec/preview-controls.js +0 -139
  359. package/dist/spec/slot-refs.d.ts +0 -39
  360. package/dist/spec/slot-refs.js +0 -56
  361. package/dist/token-override-rows.d.ts +0 -68
  362. package/dist/token-override-rows.js +0 -233
  363. package/dist/tokenValueType.d.ts +0 -34
  364. package/dist/tokenValueType.js +0 -138
  365. package/dist/types/css-properties.d.ts +0 -233
  366. package/dist/types/css-properties.js +0 -14
  367. package/dist/types/css-property-keywords.d.ts +0 -156
  368. package/dist/types/css-property-keywords.js +0 -616
  369. package/dist/types/css-values.d.ts +0 -63
  370. package/dist/types/css-values.js +0 -16
  371. package/dist/types.d.ts +0 -1138
  372. package/dist/types.js +0 -12
  373. package/dist/units.d.ts +0 -14
  374. package/dist/units.js +0 -16
  375. package/dist/utils/index.d.ts +0 -4
  376. package/dist/utils/index.js +0 -4
package/dist/Config.js DELETED
@@ -1,1712 +0,0 @@
1
- import "./brands.js";
2
- import { makeTokenRefToCss } from "./entity-utils.js";
3
- import { AssetGroup } from "./AssetGroup.js";
4
- import { attachSourcePath } from "./captureCallerPath.js";
5
- import { isAssetGroupRef, isModeRef, isTokenRef } from "./refs.js";
6
- import { ASSET_GROUP_SLUG_KEY, readAssetGroupSlug } from "./defineAssetGroup.js";
7
- import { FOREIGN_NAMESPACE } from "./foreign-component-name.js";
8
- import { Component, assertDefinitionAcceptsPlaceable } from "./Component.js";
9
- import { ComponentGroup } from "./ComponentGroup.js";
10
- import { CompositeStyle } from "./CompositeStyle.js";
11
- import { assertLinkedTokenPinned, assertLinkedValuePinned, validateLinkedSystemPin } from "./linked-system-pins.js";
12
- import { assignLinkedNodePrefixes } from "./linkedNodePrefixes.js";
13
- import { Mode } from "./Mode.js";
14
- import { Modifier } from "./Modifier.js";
15
- import { CssMotionDef, JsMotionDef } from "./MotionDef.js";
16
- import { Provider } from "./Provider.js";
17
- import { StyleProp } from "./StyleProp.js";
18
- import { TokenGroup } from "./TokenGroup.js";
19
- //#region src/Config.ts
20
- /**
21
- * `Config` — top-level container for a UDS design system.
22
- *
23
- * Authored data lives on bare properties (Maps for collections, scalars
24
- * for prefix/preflight). Derived data lives under `.derived`, a memoized
25
- * lazy getter rebuilt whenever a `register*` chain method runs. The
26
- * single dot to `.derived` is the boundary between "stored" and
27
- * "computed"; consumers can tell at a glance which side of the line
28
- * they're reading.
29
- */
30
- /** Compile-time exhaustiveness backstop for the `dependentsOf`/`dependenciesOf`
31
- * switches: once every kind is handled, `query` narrows to `never` here; a new,
32
- * unhandled kind makes the call a type error at the `default` branch. */
33
- function assertNever(value) {
34
- throw new Error(`Unhandled dependency kind: ${JSON.stringify(value)}`);
35
- }
36
- var Config = class Config {
37
- static SCHEMA_VERSION = 1;
38
- platform = "web";
39
- prefix = "uds";
40
- /**
41
- * Registry namespace — a stable, `package.json`-style identifier
42
- * (`uds`, `@acme/system`) that namespaces every component's
43
- * `registryKey` as `<namespace>:<ComponentName>` (the spec `type`).
44
- * Declared in `uds.config.ts` via `configure({ namespace })` so it's
45
- * available at build time; the push pipeline validates it for
46
- * uniqueness / ownership. `undefined` ⇒ components keep bare-name
47
- * registry keys. See Linear doc "RFC: Registry-namespaced component
48
- * types".
49
- */
50
- namespace;
51
- preflight = true;
52
- globalStyles = {};
53
- rawCss = [];
54
- designPrinciples = [];
55
- buildOptions = {};
56
- /** Root-config playground settings authored via `configure({ playgroundOptions })`. */
57
- playgroundOptions = {};
58
- tokenGroups = /* @__PURE__ */ new Map();
59
- styleProps = /* @__PURE__ */ new Map();
60
- modifiers = /* @__PURE__ */ new Map();
61
- modes = /* @__PURE__ */ new Map();
62
- compositeStyles = /* @__PURE__ */ new Map();
63
- components = /* @__PURE__ */ new Map();
64
- componentGroups = /* @__PURE__ */ new Map();
65
- assetGroups = /* @__PURE__ */ new Map();
66
- providers = /* @__PURE__ */ new Map();
67
- motion = /* @__PURE__ */ new Map();
68
- canvasRoles = /* @__PURE__ */ new Map();
69
- /**
70
- * Source systems pinned via {@link registerLinkedSystems}, keyed by the
71
- * source system's database UUID. A pin anchors every linked value from that
72
- * system to one exact published version — registration validates each
73
- * link-marked value against it.
74
- */
75
- linkedSystems = /* @__PURE__ */ new Map();
76
- /** Build-supplied node CSS prefixes (`{systemId}@{version}` → prefix). */
77
- #linkedNodePrefixes;
78
- /**
79
- * Supply the closure-accurate node prefix assignment (build sites know the
80
- * full transitive closure; the pins-derived fallback below only sees direct
81
- * pins). Affects how system-scoped refs without consumer entries derive.
82
- */
83
- setLinkedNodePrefixes(prefixes) {
84
- this.#linkedNodePrefixes = prefixes;
85
- this.#invalidate();
86
- return this;
87
- }
88
- /**
89
- * The CSS prefix a pinned system's regenerated node sheet uses — the var
90
- * namespace a system-scoped ref resolves into when no consumer entry
91
- * exists. Explicit build-supplied assignment wins; otherwise derived from
92
- * the direct pins with the same pure assignment the build uses (identical
93
- * for directly pinned systems, which are the only ones payload refs may
94
- * name). Null when the system isn't pinned.
95
- */
96
- linkedNodeVarPrefix(systemId, version) {
97
- if (this.#linkedNodePrefixes) {
98
- if (version) {
99
- const exact = this.#linkedNodePrefixes.get(`${systemId}@${version}`);
100
- if (exact) return exact;
101
- }
102
- for (const [key, prefix] of this.#linkedNodePrefixes) if (key.startsWith(`${systemId}@`)) return prefix;
103
- return null;
104
- }
105
- const pin = this.linkedSystems.get(systemId);
106
- if (!pin) return null;
107
- return assignLinkedNodePrefixes([...this.linkedSystems.entries()].map(([id, p]) => ({
108
- systemId: id,
109
- version: p.version,
110
- name: p.name
111
- })), this.prefix).get(`${systemId}@${pin.version}`)?.cssPrefix ?? null;
112
- }
113
- #derived;
114
- /**
115
- * Set top-level config metadata in one call — the single scalar-setter,
116
- * replacing the former `withPrefix` / `withPreflight` / `withName` /
117
- * `withDesignPrinciples` / `withBuildOptions` chain. Partial: only
118
- * provided keys are applied, so it can run once or be merged across
119
- * calls. `globalStyles` stays on `defineGlobalStyles` — its callback
120
- * form needs the derived token refs, so it doesn't fit a plain bag.
121
- *
122
- * - `namespace` — the registry namespace (`uds`, `@acme/system`) that
123
- * namespaces every component's `registryKey` as `<namespace>:<Component>`.
124
- * Declared in `uds.config.ts` (the `package.json`-`name` model) so it's
125
- * available at build time; the push pipeline additionally validates it
126
- * for uniqueness / ownership. Throws on a structurally invalid value
127
- * (the `:` separator, whitespace, or empty).
128
- * - `prefix` — class-name + CSS-variable prefix (default `uds`); invalidates
129
- * derived. Pass `''` or `false` to opt out entirely — classes and vars
130
- * emit unprefixed (`bg-primary`, `--color-brand`). `false` is sugar for
131
- * `''`, normalized to the empty string here so the value stays a string.
132
- * - `preflight` — include Tailwind's reset (default `true`).
133
- * - `designPrinciples` — freeform strings surfaced in the AI prompt.
134
- * - `buildOptions` — shallow-merged into existing build options.
135
- * - `playgroundOptions` — pages directory + settings for `uds dev --playground`
136
- * (replaces the value wholesale, like `designPrinciples`).
137
- */
138
- configure(options) {
139
- if (options.namespace !== void 0) this.namespace = validateNamespace(options.namespace);
140
- if (options.prefix !== void 0) {
141
- this.prefix = options.prefix === false ? "" : options.prefix;
142
- this.#invalidate();
143
- }
144
- if (options.preflight !== void 0) this.preflight = options.preflight;
145
- if (options.designPrinciples !== void 0) this.designPrinciples = [...options.designPrinciples];
146
- if (options.buildOptions !== void 0) this.buildOptions = {
147
- ...this.buildOptions,
148
- ...options.buildOptions
149
- };
150
- if (options.playgroundOptions !== void 0) this.playgroundOptions = options.playgroundOptions;
151
- return this;
152
- }
153
- /**
154
- * Resolve a spec element `type` (or any component identifier) to its
155
- * {@link Component}, namespace-aware and backwards-compatible.
156
- *
157
- * `components` is keyed by the bare `name`, so:
158
- * - an exact match is tried first — a bare `'Badge'` resolves directly,
159
- * keeping every existing bare-name lookup working;
160
- * - otherwise a namespaced registry key (`'<namespace>:<name>'`, the spec
161
- * `type`) resolves by its bare name **only when the namespace is this
162
- * registry's own**. A key from a different registry (`'other:Badge'`)
163
- * deliberately does not resolve, so specs from multiple registries can
164
- * coexist without colliding on a shared bare name.
165
- *
166
- * Returns `undefined` for unknown types and foreign (`foreign:`) sentinels.
167
- * This is the single resolution path consumers should use instead of
168
- * `config.components.get(type)` when `type` may be a spec/registry key.
169
- */
170
- getComponent(type) {
171
- const direct = this.components.get(type);
172
- if (direct) return direct;
173
- const { namespace, name } = parseRegistryKey(type);
174
- if (namespace !== void 0 && namespace === this.namespace) return this.components.get(name);
175
- }
176
- /**
177
- * The bare, human-readable component name for a spec `type` — strips this
178
- * registry's `<namespace>:` prefix (`'uds:Text'` → `'Text'`) for display, and
179
- * the `foreign:` sentinel third-party leaves carry (`'foreign:CopyIcon'` →
180
- * `'CopyIcon'`) — that prefix marks "not a UDS component," not a peer system,
181
- * so the bare name is what every display surface wants. A bare type, an
182
- * unknown type, or a key from another *peer* registry (`@acme/system:Button`)
183
- * is returned unchanged — there the prefix is meaningful disambiguation.
184
- */
185
- componentDisplayName(type) {
186
- const { namespace, name } = parseRegistryKey(type);
187
- if (namespace === "foreign") return name;
188
- return namespace !== void 0 && namespace === this.namespace ? name : type;
189
- }
190
- /**
191
- * Components whose catalog `metadata.label` matches `label` (case-insensitive)
192
- * — the display-name lookup that complements `getComponent` (registry key).
193
- * Returns an array because labels aren't unique: a collision-dodging export
194
- * (`StudioListItem` with `label: 'ListItem'`) can share a label with another
195
- * component's key, so callers must handle 0, 1, or many. Empty when no label
196
- * matches.
197
- */
198
- getComponentsByLabel(label) {
199
- const lower = label.toLowerCase();
200
- const out = [];
201
- for (const component of this.components.values()) if (component.metadata?.label?.toLowerCase() === lower) out.push(component);
202
- return out;
203
- }
204
- /**
205
- * Style props that emit a given CSS property — the reverse of
206
- * `StyleProp.cssProperty`. A property can be served by more than one prop
207
- * (e.g. `border-color` by `borderColor`, `divideColor`; `margin-top` by both
208
- * the positive `marginTop` and the `negative` `offsetTop`). Match is exact on
209
- * the CSS property name; a prop targeting several properties matches any.
210
- *
211
- * `negative` props are ordered LAST, so a caller taking the first match
212
- * (`getStylePropsByCssProperty(css)[0]`) always gets the primary, positive
213
- * prop — the negative variant is only the first result when it's the sole
214
- * prop for that property. Registration order is preserved within each group.
215
- *
216
- * Pass `opts.negative` to filter explicitly: `{ negative: false }` for only
217
- * positive props, `{ negative: true }` for only the negative variant(s) (e.g.
218
- * the `offsetTop` behind `margin-top`). Omit it to get every match.
219
- */
220
- getStylePropsByCssProperty(cssProperty, opts) {
221
- const out = [];
222
- for (const styleProp of this.styleProps.values()) {
223
- const css = styleProp.cssProperty;
224
- if (!(Array.isArray(css) ? css.includes(cssProperty) : css === cssProperty)) continue;
225
- if (opts?.negative !== void 0 && styleProp.negative !== opts.negative) continue;
226
- out.push(styleProp);
227
- }
228
- return out.sort((a, b) => Number(a.negative) - Number(b.negative));
229
- }
230
- /**
231
- * Resolve the `StyleProp` behind a component's JSX prop, following any
232
- * re-alias. A component can expose a style prop under a different JSX name
233
- * than the registry key — `Box`'s `backgroundColor` prop binds
234
- * `styleProp('bg')` — so this maps the on-component name back to the
235
- * registered `StyleProp`. Returns `undefined` if the component has no such
236
- * style prop.
237
- */
238
- getComponentStyleProp(componentName, jsxProp) {
239
- const component = this.getComponent(componentName);
240
- if (!component) return void 0;
241
- for (const info of component.derived.props) if (info.kind === "styleProp" && info.name === jsxProp) return this.styleProps.get(info.stylePropName);
242
- }
243
- /**
244
- * Components that render `name` as one of their layers — the reverse of
245
- * `Layer.renders`. Layer composition only; subcomponent parentage is a
246
- * separate edge (`Component.derived.subcomponentOf`). The component slice of
247
- * the public {@link Config.dependentsOf} read surface.
248
- */
249
- #componentDependents(name) {
250
- const out = [];
251
- for (const component of this.components.values()) {
252
- if (component.name === name) continue;
253
- for (const layer of component.layers.values()) {
254
- const renders = layer.renders;
255
- if (renders?.kind === "component" && renders.ref === name) {
256
- out.push(component.name);
257
- break;
258
- }
259
- }
260
- }
261
- return out;
262
- }
263
- /**
264
- * Components that expose a composite style as a prop (bound via
265
- * `composite('<name>')`) — e.g. which components offer an `elevation`
266
- * dropdown. The composite-style slice of {@link Config.dependentsOf}.
267
- */
268
- #compositeStyleDependents(name) {
269
- const out = [];
270
- for (const component of this.components.values()) for (const info of component.derived.props) if (info.kind === "composite" && info.compositeName === name) {
271
- out.push(component.name);
272
- break;
273
- }
274
- return out;
275
- }
276
- /**
277
- * Components that expose a given style prop. Matches on the registered
278
- * style-prop key (`info.stylePropName`), not the on-component JSX name, so a
279
- * component that re-aliases the prop under a different name (a
280
- * `backgroundColor` prop bound to `styleProp('bg')`) is still found; `as`
281
- * carries that on-component name when it differs from the registry key. The
282
- * style-prop slice of {@link Config.dependentsOf} (which keeps only the
283
- * component name).
284
- */
285
- #stylePropDependents(name) {
286
- const out = [];
287
- for (const component of this.components.values()) for (const info of component.derived.props) if (info.kind === "styleProp" && info.stylePropName === name) {
288
- out.push({
289
- component: component.name,
290
- as: info.name
291
- });
292
- break;
293
- }
294
- return out;
295
- }
296
- /**
297
- * Style props that draw their values from a given token group — the group's
298
- * "defined in" relation: a style prop references a group through its `values`
299
- * array (`bg` ← `tokenGroup('color')`). Only style props reference token
300
- * groups today, so the result is style-prop names; empty for an unknown
301
- * group. The token-group slice of {@link Config.dependentsOf}; the *member*
302
- * usage ("used in") is the separate {@link Config.getTokenGroupUsage}.
303
- */
304
- #tokenGroupDependents(namespace) {
305
- const group = this.tokenGroups.get(namespace);
306
- return group ? [...group.derived.styleProperties] : [];
307
- }
308
- /**
309
- * Components that reference a given token anywhere in their styling. Reads
310
- * the structural `Component.derived.tokenRefs` (which captures both
311
- * `token(...)` refs and shorthand style-prop values, across base / variants /
312
- * compound), so the match is on token *identity*, not CSS-var or substring
313
- * matching. `name` may be qualified (`color/brand`) or bare (`brand`) — a
314
- * bare name matches the token part in any group. The component half of a
315
- * token's referrers in {@link Config.dependentsOf}; the token half is
316
- * {@link Config.#tokenTokenDependents}.
317
- */
318
- #tokenComponentDependents(name) {
319
- const qualified = name.includes("/");
320
- const out = [];
321
- for (const component of this.components.values()) {
322
- const refs = component.derived.tokenRefs;
323
- if (qualified ? refs.includes(name) : refs.some((ref) => ref.slice(ref.indexOf("/") + 1) === name)) out.push(component.name);
324
- }
325
- return out;
326
- }
327
- /**
328
- * Other tokens that reference a given token — the "which tokens alias or
329
- * mode-override this one?" lookup. Walks each token's value + modifiers for
330
- * `token()` refs (the same structural channel `tokenUsageStats` counts).
331
- * Returns qualified token names; `name` may be qualified or bare. Excludes
332
- * self. The token half of a token's referrers in {@link Config.dependentsOf}.
333
- */
334
- #tokenTokenDependents(name) {
335
- const qualified = name.includes("/");
336
- const out = [];
337
- for (const [qualifiedName, token] of this.derived.tokens) {
338
- if (qualifiedName === name) continue;
339
- const refs = /* @__PURE__ */ new Set();
340
- this.#collectStyleTokenRefs(token.value, refs, /* @__PURE__ */ new WeakSet());
341
- if (token.modifiers) this.#collectStyleTokenRefs(token.modifiers, refs, /* @__PURE__ */ new WeakSet());
342
- if (qualified ? refs.has(name) : [...refs].some((ref) => ref.slice(ref.indexOf("/") + 1) === name)) out.push(qualifiedName);
343
- }
344
- return out;
345
- }
346
- /**
347
- * Every qualified token name referenced *anywhere* in the system — the
348
- * "used somewhere" set. Three reference channels, so a token reached only
349
- * through one of them (e.g. a raw spectrum step a semantic token aliases,
350
- * never touched by a component directly) still counts as used:
351
- * - **components** — the structural `tokenRefs` union;
352
- * - **token → token** — a token's value or a mode-override referencing
353
- * another via `token()` (aliases, `_dark` swaps);
354
- * - **composite styles** — `token()` refs and shorthand values in each bag.
355
- */
356
- #referencedTokenNames() {
357
- const refs = /* @__PURE__ */ new Set();
358
- for (const component of this.components.values()) for (const ref of component.derived.tokenRefs) refs.add(ref);
359
- for (const token of this.derived.tokens.values()) {
360
- this.#collectStyleTokenRefs(token.value, refs, /* @__PURE__ */ new WeakSet());
361
- if (token.modifiers) this.#collectStyleTokenRefs(token.modifiers, refs, /* @__PURE__ */ new WeakSet());
362
- }
363
- for (const composite of this.compositeStyles.values()) for (const bag of composite.styles.values()) this.#collectStyleTokenRefs(bag, refs, /* @__PURE__ */ new WeakSet());
364
- return refs;
365
- }
366
- /**
367
- * Walk an arbitrary value (a token value, modifier map, or composite-style
368
- * bag), collecting every token it references into `refs`: structured
369
- * `token()` refs by identity (including ones nested in a color expression),
370
- * and bare style-prop shorthand values resolved through the registry.
371
- */
372
- #collectStyleTokenRefs(node, refs, seen) {
373
- if (node === null || typeof node !== "object") return;
374
- if (isTokenRef(node)) {
375
- refs.add(node.ref);
376
- return;
377
- }
378
- if (seen.has(node)) return;
379
- seen.add(node);
380
- if (Array.isArray(node)) {
381
- for (const item of node) this.#collectStyleTokenRefs(item, refs, seen);
382
- return;
383
- }
384
- for (const [key, value] of Object.entries(node)) {
385
- if (key.startsWith("__")) continue;
386
- if (typeof value === "string") {
387
- const ref = this.#resolveStylePropValueToken(key, value);
388
- if (ref) refs.add(ref);
389
- } else this.#collectStyleTokenRefs(value, refs, seen);
390
- }
391
- }
392
- /**
393
- * Aggregate token-usage stats across the whole registry — how many tokens
394
- * are referenced *somewhere* (components, other tokens, or composite styles;
395
- * see {@link Config.#referencedTokenNames}) vs not. `usagePercent` is
396
- * rounded; `0` total tokens reports `0%`.
397
- */
398
- tokenUsageStats() {
399
- const referenced = this.#referencedTokenNames();
400
- const tokens = [...this.derived.tokens.keys()];
401
- const total = tokens.length;
402
- const used = tokens.filter((name) => referenced.has(name)).length;
403
- const usagePercent = total === 0 ? 0 : Math.round(used / total * 100);
404
- return {
405
- total,
406
- used,
407
- unused: total - used,
408
- usagePercent
409
- };
410
- }
411
- /**
412
- * Token values ranked by how often they appear across component base styles —
413
- * the aggregate "what's load-bearing?" companion to `dependentsOf('token')`.
414
- * `opts.property` narrows to one CSS property. Descends into `_<modifier>`
415
- * sub-objects so a value used only in a hover/dark state still counts.
416
- */
417
- tokenValueUsage(opts) {
418
- const property = opts?.property;
419
- const counts = /* @__PURE__ */ new Map();
420
- const collect = (styles) => {
421
- for (const [prop, value] of Object.entries(styles)) if (typeof value === "string") {
422
- if (property && prop.toLowerCase() !== property.toLowerCase()) continue;
423
- const key = `${prop}:${value}`;
424
- const existing = counts.get(key);
425
- if (existing) existing.count++;
426
- else counts.set(key, {
427
- count: 1,
428
- property: prop
429
- });
430
- } else if (isStyleRecord(value) && prop.startsWith("_")) collect(value);
431
- };
432
- for (const component of this.components.values()) {
433
- if (!component.base) continue;
434
- for (const styles of Object.values(component.base)) if (isStyleRecord(styles)) collect(styles);
435
- }
436
- return [...counts.entries()].map(([key, data]) => ({
437
- value: key.slice(key.indexOf(":") + 1),
438
- count: data.count,
439
- property: data.property
440
- })).sort((a, b) => b.count - a.count);
441
- }
442
- /**
443
- * The reverse-dependency read surface — "what references this entity, and
444
- * would break if I delete or rename it?" — uniform across every kind. One
445
- * public entry point: callers dispatch by `kind` and get a single tagged
446
- * list (the affected entities, each with its own kind), never a per-kind
447
- * bespoke shape. The relation is always *references to the entity*; the
448
- * per-kind walks behind it are private slices.
449
- *
450
- * A `token` resolves to BOTH the components that reference it AND the tokens
451
- * that alias or mode-override it — a token's full referrer set, not split
452
- * across two methods. A `modifier` flattens its components, composite styles,
453
- * and token-group namespaces into the one tagged list. A token group's other
454
- * relation — what actually *uses* its member tokens — is the separate
455
- * {@link Config.getTokenGroupUsage} (only containers have it); ranking every
456
- * entity of a kind at once is {@link Config.componentDependentCounts} & co.
457
- */
458
- dependentsOf(query) {
459
- switch (query.kind) {
460
- case "component": return this.#componentDependents(query.name).map((name) => ({
461
- kind: "component",
462
- name
463
- }));
464
- case "compositeStyle": return this.#compositeStyleDependents(query.name).map((name) => ({
465
- kind: "component",
466
- name
467
- }));
468
- case "styleProp":
469
- if (query.value === void 0) return this.#stylePropDependents(query.name).map((d) => ({
470
- kind: "component",
471
- name: d.component
472
- }));
473
- return this.#stylePropKeywordDependents(query.name, query.value).map((name) => ({
474
- kind: "component",
475
- name
476
- }));
477
- case "token": return [...this.#tokenComponentDependents(query.name).map((name) => ({
478
- kind: "component",
479
- name
480
- })), ...this.#tokenTokenDependents(query.name).map((name) => ({
481
- kind: "token",
482
- name
483
- }))];
484
- case "tokenGroup": return this.#tokenGroupDependents(query.name).map((name) => ({
485
- kind: "styleProp",
486
- name
487
- }));
488
- case "modifier": {
489
- const m = this.#modifierDependents(query.name);
490
- return [
491
- ...m.components.map((name) => ({
492
- kind: "component",
493
- name
494
- })),
495
- ...m.compositeStyles.map((name) => ({
496
- kind: "compositeStyle",
497
- name
498
- })),
499
- ...m.tokenGroups.map((name) => ({
500
- kind: "tokenGroup",
501
- name
502
- }))
503
- ];
504
- }
505
- default: return assertNever(query);
506
- }
507
- }
508
- /**
509
- * The outbound mirror of {@link Config.dependentsOf} — "what does this entity
510
- * *use*, and would I have to update if I delete those?". Same tagged
511
- * `DependencyRef[]` shape, same `kind`-keyed exhaustiveness. A `component`
512
- * reports the tokens it references, the keyword values it sets
513
- * (`{ kind: 'styleProp', name, value }`), the composites and style props it
514
- * binds, the components it renders as layers, and the modifiers it styles
515
- * with; a `token` reports the tokens it aliases and the modifiers in its mode
516
- * overrides; a `styleProp` reports the token groups it draws from. `modifier`
517
- * and `tokenGroup` are leaves here — a selector and a container don't *use*
518
- * other registry entities — so they report nothing.
519
- */
520
- dependenciesOf(query) {
521
- switch (query.kind) {
522
- case "component": return this.#componentDependencies(query.name);
523
- case "token": return this.#tokenDependencies(query.name);
524
- case "styleProp": {
525
- const sp = this.styleProps.get(query.name);
526
- if (!sp) return [];
527
- return [...new Set(sp.consumedTokenGroups())].map((name) => ({
528
- kind: "tokenGroup",
529
- name
530
- }));
531
- }
532
- case "compositeStyle": return this.#compositeStyleDependencies(query.name);
533
- case "tokenGroup": {
534
- const group = this.tokenGroups.get(query.name);
535
- if (!group) return [];
536
- return [...group.tokens.keys()].map((leaf) => ({
537
- kind: "token",
538
- name: `${query.name}/${leaf}`
539
- }));
540
- }
541
- case "modifier": return [];
542
- default: return assertNever(query);
543
- }
544
- }
545
- /**
546
- * Components that use a given style-prop *keyword* value — the "what breaks
547
- * if I remove the `flex` value from `display`?" lookup, the value-narrowed
548
- * slice of `dependentsOf({ kind: 'styleProp', name, value })`. Scoped to the
549
- * `(styleProp, value)` pair (keywords aren't global — `col` on `flexDirection`
550
- * is unrelated to `col` elsewhere), reading `derived.stylePropKeywords`.
551
- */
552
- #stylePropKeywordDependents(styleProp, value) {
553
- const out = [];
554
- for (const component of this.components.values()) if (component.derived.stylePropKeywords.some((kw) => kw.styleProp === styleProp && kw.value === value)) out.push(component.name);
555
- return out;
556
- }
557
- /** Outbound edges of a component — its `dependenciesOf` slice: the tokens it
558
- * references, the keyword values it sets, the composites + style props it
559
- * binds, the components it renders as layers, and the modifiers it styles
560
- * with. Deduped across those sources. */
561
- #componentDependencies(name) {
562
- const component = this.getComponent(name);
563
- if (!component) return [];
564
- const out = [];
565
- const d = component.derived;
566
- for (const ref of d.tokenRefs) out.push({
567
- kind: "token",
568
- name: ref
569
- });
570
- for (const kw of d.stylePropKeywords) out.push({
571
- kind: "styleProp",
572
- name: kw.styleProp,
573
- value: kw.value
574
- });
575
- for (const info of d.props) if (info.kind === "composite") out.push({
576
- kind: "compositeStyle",
577
- name: info.compositeName
578
- });
579
- else if (info.kind === "styleProp") out.push({
580
- kind: "styleProp",
581
- name: info.stylePropName
582
- });
583
- for (const layer of component.layers.values()) {
584
- const renders = layer.renders;
585
- if (renders?.kind === "component" && renders.ref !== component.name) out.push({
586
- kind: "component",
587
- name: renders.ref
588
- });
589
- }
590
- const mods = /* @__PURE__ */ new Set();
591
- if (component.base) this.#collectModifierKeys(component.base, mods);
592
- for (const binding of Object.values(component.props)) this.#collectModifierKeys(binding, mods);
593
- for (const entry of component.compoundProps ?? []) this.#collectModifierKeys(entry.layers, mods);
594
- for (const mod of mods) out.push({
595
- kind: "modifier",
596
- name: mod
597
- });
598
- return this.#dedupeRefs(out);
599
- }
600
- /** Outbound edges of a token — the tokens it aliases (value + mode overrides)
601
- * and the modifiers carrying those overrides. `name` must be qualified. */
602
- #tokenDependencies(name) {
603
- const token = this.derived.tokens.get(name);
604
- if (!token) return [];
605
- const out = [];
606
- const refs = /* @__PURE__ */ new Set();
607
- this.#collectStyleTokenRefs(token.value, refs, /* @__PURE__ */ new WeakSet());
608
- if (token.modifiers) this.#collectStyleTokenRefs(token.modifiers, refs, /* @__PURE__ */ new WeakSet());
609
- for (const ref of refs) if (ref !== name) out.push({
610
- kind: "token",
611
- name: ref
612
- });
613
- for (const key of Object.keys(token.modifiers ?? {})) if (this.modifiers.has(key)) out.push({
614
- kind: "modifier",
615
- name: key
616
- });
617
- return out;
618
- }
619
- /** Outbound edges of a composite style — the tokens and modifiers across its
620
- * variant style bags. */
621
- #compositeStyleDependencies(name) {
622
- const cs = this.compositeStyles.get(name);
623
- if (!cs) return [];
624
- const refs = /* @__PURE__ */ new Set();
625
- const mods = /* @__PURE__ */ new Set();
626
- for (const bag of cs.styles.values()) {
627
- this.#collectStyleTokenRefs(bag, refs, /* @__PURE__ */ new WeakSet());
628
- this.#collectModifierKeys(bag, mods);
629
- }
630
- return [...[...refs].map((name) => ({
631
- kind: "token",
632
- name
633
- })), ...[...mods].map((name) => ({
634
- kind: "modifier",
635
- name
636
- }))];
637
- }
638
- /** Collect every registered single-underscore modifier key reachable in a
639
- * styling node (skipping `__`-internals), into `into`. */
640
- #collectModifierKeys(node, into, seen = /* @__PURE__ */ new WeakSet()) {
641
- if (node === null || typeof node !== "object") return;
642
- if (seen.has(node)) return;
643
- seen.add(node);
644
- for (const [key, value] of Object.entries(node)) {
645
- if (key.startsWith("__")) continue;
646
- if (key.startsWith("_") && this.modifiers.has(key)) into.add(key);
647
- this.#collectModifierKeys(value, into, seen);
648
- }
649
- }
650
- /** Dedupe a `DependencyRef[]` by `(kind, name, value)`. */
651
- #dedupeRefs(refs) {
652
- const seen = /* @__PURE__ */ new Set();
653
- const out = [];
654
- for (const ref of refs) {
655
- const key = `${ref.kind} ${ref.name} ${ref.value ?? ""}`;
656
- if (seen.has(key)) continue;
657
- seen.add(key);
658
- out.push(ref);
659
- }
660
- return out;
661
- }
662
- /**
663
- * Everything that references a modifier (`_hover`, `_dataStateOpen`, `_dark`)
664
- * in its own styling. Walks all the places a `_<modifier>` key can appear:
665
- * - **components**: `base`, each variant's per-layer styles, and
666
- * `compoundProps` layer overrides;
667
- * - **compositeStyles**: each variant bag;
668
- * - **tokenGroups**: tokens carrying a `_<modifier>` mode override (e.g.
669
- * `{ value, _dark }`) — grouped by namespace since a mode modifier touches
670
- * many tokens at once.
671
- * Matches single-underscore modifier keys only (skips `__kind` etc.). The
672
- * modifier slice of {@link Config.dependentsOf}, which flattens the three
673
- * buckets into one tagged list.
674
- */
675
- #modifierDependents(modifier) {
676
- const usesModifier = (node, seen = /* @__PURE__ */ new WeakSet()) => {
677
- if (node === null || typeof node !== "object") return false;
678
- if (seen.has(node)) return false;
679
- seen.add(node);
680
- for (const [key, value] of Object.entries(node)) {
681
- if (key === modifier) return true;
682
- if (key.startsWith("__")) continue;
683
- if (usesModifier(value, seen)) return true;
684
- }
685
- return false;
686
- };
687
- const components = [];
688
- for (const component of this.components.values()) {
689
- const inBase = component.base ? usesModifier(component.base) : false;
690
- const inProps = !inBase && Object.values(component.props).some((binding) => usesModifier(binding));
691
- const inCompound = !inBase && !inProps && (component.compoundProps?.some((e) => usesModifier(e.layers)) ?? false);
692
- if (inBase || inProps || inCompound) components.push(component.name);
693
- }
694
- const compositeStyles = [];
695
- for (const cs of this.compositeStyles.values()) for (const bag of cs.styles.values()) if (usesModifier(bag)) {
696
- compositeStyles.push(cs.name);
697
- break;
698
- }
699
- const tokenGroups = [];
700
- for (const group of this.tokenGroups.values()) for (const token of group.tokens.values()) if (token.modifiers && Object.hasOwn(token.modifiers, modifier)) {
701
- tokenGroups.push(group.namespace);
702
- break;
703
- }
704
- return {
705
- components,
706
- compositeStyles,
707
- tokenGroups
708
- };
709
- }
710
- /**
711
- * The aggregate sibling of {@link Config.dependentsOf} — for *every* entity of
712
- * a kind at once, how many dependents each has (`Map<name, count>`, ranked by
713
- * `uds_analyze_usage`). A single entity's count is just
714
- * `dependentsOf(query).length`; this exists for the all-at-once case, where
715
- * calling that per entity would re-walk the component set N times
716
- * (O(N × components)) — each private slice walks once (O(components)) and
717
- * tallies as it goes, kept consistent with `dependentsOf` (a dependent
718
- * counted once per entity, self-references excluded; the cross-check tests
719
- * assert the equivalence against the live config).
720
- */
721
- dependentCounts(kind) {
722
- switch (kind) {
723
- case "component": return this.#componentDependentCounts();
724
- case "compositeStyle": return this.#compositeStyleDependentCounts();
725
- case "styleProp": return this.#stylePropDependentCounts();
726
- case "tokenGroup": return this.#tokenGroupDependentCounts();
727
- case "modifier": return this.#modifierDependentCounts();
728
- case "token": return this.#tokenDependentCounts();
729
- default: return assertNever(kind);
730
- }
731
- }
732
- #componentDependentCounts() {
733
- const counts = /* @__PURE__ */ new Map();
734
- for (const component of this.components.values()) {
735
- const refs = /* @__PURE__ */ new Set();
736
- for (const layer of component.layers.values()) {
737
- const renders = layer.renders;
738
- if (renders?.kind === "component" && renders.ref !== component.name) refs.add(renders.ref);
739
- }
740
- for (const ref of refs) counts.set(ref, (counts.get(ref) ?? 0) + 1);
741
- }
742
- return counts;
743
- }
744
- #compositeStyleDependentCounts() {
745
- const counts = /* @__PURE__ */ new Map();
746
- for (const component of this.components.values()) {
747
- const names = /* @__PURE__ */ new Set();
748
- for (const info of component.derived.props) if (info.kind === "composite") names.add(info.compositeName);
749
- for (const name of names) counts.set(name, (counts.get(name) ?? 0) + 1);
750
- }
751
- return counts;
752
- }
753
- #stylePropDependentCounts() {
754
- const counts = /* @__PURE__ */ new Map();
755
- for (const component of this.components.values()) {
756
- const names = /* @__PURE__ */ new Set();
757
- for (const info of component.derived.props) if (info.kind === "styleProp") names.add(info.stylePropName);
758
- for (const name of names) counts.set(name, (counts.get(name) ?? 0) + 1);
759
- }
760
- return counts;
761
- }
762
- /**
763
- * Single-pass companion to `dependentsOf('tokenGroup', …)` — how many style props
764
- * draw from each token-group namespace. Walks the style props once (the
765
- * forward `consumedTokenGroups` direction) rather than re-deriving each
766
- * group's consumer list, counting a prop once per group it references.
767
- */
768
- #tokenGroupDependentCounts() {
769
- const counts = /* @__PURE__ */ new Map();
770
- for (const styleProp of this.styleProps.values()) for (const namespace of new Set(styleProp.consumedTokenGroups())) counts.set(namespace, (counts.get(namespace) ?? 0) + 1);
771
- return counts;
772
- }
773
- /**
774
- * Single-pass companion to `dependentsOf('modifier', …)` — the total it sums per
775
- * modifier (components + composite styles + token-group namespaces that
776
- * reference it). Mirrors the modifier walk's traversal rules: gathers every
777
- * single-underscore key (skipping `__`-prefixed internals, so a slot's
778
- * `__accepts` child definitions never leak in), counts each source once per
779
- * modifier, and tallies only registered modifiers.
780
- */
781
- #modifierDependentCounts() {
782
- const counts = /* @__PURE__ */ new Map();
783
- const bump = (key, into) => {
784
- if (this.modifiers.has(key)) into.add(key);
785
- };
786
- const collect = (node, into, seen = /* @__PURE__ */ new WeakSet()) => {
787
- if (node === null || typeof node !== "object") return;
788
- if (seen.has(node)) return;
789
- seen.add(node);
790
- for (const [key, value] of Object.entries(node)) {
791
- if (key.startsWith("__")) continue;
792
- if (key.startsWith("_")) bump(key, into);
793
- collect(value, into, seen);
794
- }
795
- };
796
- const tally = (keys) => {
797
- for (const key of keys) counts.set(key, (counts.get(key) ?? 0) + 1);
798
- };
799
- for (const component of this.components.values()) {
800
- const keys = /* @__PURE__ */ new Set();
801
- if (component.base) collect(component.base, keys);
802
- for (const binding of Object.values(component.props)) collect(binding, keys);
803
- for (const entry of component.compoundProps ?? []) collect(entry.layers, keys);
804
- tally(keys);
805
- }
806
- for (const cs of this.compositeStyles.values()) {
807
- const keys = /* @__PURE__ */ new Set();
808
- for (const bag of cs.styles.values()) collect(bag, keys);
809
- tally(keys);
810
- }
811
- for (const group of this.tokenGroups.values()) {
812
- const keys = /* @__PURE__ */ new Set();
813
- for (const token of group.tokens.values()) for (const key of Object.keys(token.modifiers ?? {})) bump(key, keys);
814
- tally(keys);
815
- }
816
- return counts;
817
- }
818
- /** Single-pass `dependentCounts('token')` — referrers per token: each
819
- * component counted once per token it references, plus each token that
820
- * aliases/mode-overrides another (self excluded). Mirrors the union
821
- * `dependentsOf({ kind: 'token' })` returns. */
822
- #tokenDependentCounts() {
823
- const counts = /* @__PURE__ */ new Map();
824
- const bump = (name) => counts.set(name, (counts.get(name) ?? 0) + 1);
825
- for (const component of this.components.values()) for (const ref of new Set(component.derived.tokenRefs)) bump(ref);
826
- for (const [qualifiedName, token] of this.derived.tokens) {
827
- const refs = /* @__PURE__ */ new Set();
828
- this.#collectStyleTokenRefs(token.value, refs, /* @__PURE__ */ new WeakSet());
829
- if (token.modifiers) this.#collectStyleTokenRefs(token.modifiers, refs, /* @__PURE__ */ new WeakSet());
830
- for (const ref of refs) if (ref !== qualifiedName) bump(ref);
831
- }
832
- return counts;
833
- }
834
- /**
835
- * A component's style-prop surface as a delta against a base set, so
836
- * consumers can render "inherits Box (226); adds …; removes …" instead of
837
- * re-emitting the full ~226-name list per component. The base is the
838
- * `extendsFrom` parent's set when value-extending, else the union across the
839
- * `primitives` group (the set every primitive shares). `baseSource` is the
840
- * parent's name, or `undefined` when the base is the primitives union — every
841
- * `extends:Box` primitive has an identical set today, so `adds`/`removes` are
842
- * usually empty, which is the dedup the projection relies on.
843
- */
844
- stylePropSummary(component) {
845
- const own = component.derived.stylePropNames;
846
- const parent = component.extendsFrom ? this.components.get(component.extendsFrom) : void 0;
847
- const baseSource = parent ? component.extendsFrom : void 0;
848
- const base = parent ? parent.derived.stylePropNames : this.#primitivesStyleProps();
849
- const baseSet = new Set(base);
850
- const ownSet = new Set(own);
851
- return {
852
- own,
853
- base,
854
- baseSource,
855
- adds: own.filter((n) => !baseSet.has(n)),
856
- removes: baseSource ? base.filter((n) => !ownSet.has(n)) : []
857
- };
858
- }
859
- /** Union of style-prop names across the `primitives` group — the comparison
860
- * base for root components (no `extendsFrom`, e.g. `Box`). */
861
- #primitivesStyleProps() {
862
- const names = /* @__PURE__ */ new Set();
863
- for (const component of this.components.values()) if (component.derived.componentGroup === "primitives") for (const n of component.derived.stylePropNames) names.add(n);
864
- return [...names];
865
- }
866
- /**
867
- * Author global CSS — a selector → declarations bag. The callback
868
- * form receives the same 2D CSS-var ref table as `derived.cssVarRefs`
869
- * (`tokens.bg.primary` resolves to a `var(--uds-bg-primary)` brand)
870
- * so consumers can reach for token values without re-deriving them.
871
- * Mirrors the old `@yahoo/uds-create-config` `defineGlobalStyles` chain.
872
- */
873
- defineGlobalStyles(input) {
874
- this.globalStyles = typeof input === "function" ? input(this.derived.cssVarRefs) : input;
875
- return this;
876
- }
877
- /**
878
- * Include a raw CSS string verbatim in the generated stylesheet — for
879
- * declarations the token/style-prop system can't express, most commonly
880
- * `@font-face` blocks (the codegen pipeline emits none on its own) plus
881
- * the `:root { --<prefix>-font-family-<id>: … }` definitions that font
882
- * tokens reference. Accumulates across calls.
883
- *
884
- * Takes the CSS *content*, not a path, so `Config` stays free of Node
885
- * `fs` and remains importable in the browser/runtime. Read the file in
886
- * `uds.config.ts` (Node context) and pass the string:
887
- *
888
- * ```ts
889
- * import { readFileSync } from 'node:fs';
890
- * const fontsCss = readFileSync(new URL('./fonts.css', import.meta.url), 'utf8');
891
- * export default defineConfig({ … }).includeCss(fontsCss);
892
- * ```
893
- *
894
- * Injected after Tailwind compilation and the unused-var purge, so
895
- * `@font-face` families and any custom properties it declares are never
896
- * stripped as "unused".
897
- */
898
- includeCss(css) {
899
- this.rawCss.push(css);
900
- return this;
901
- }
902
- registerModes(modes) {
903
- for (const [name, def] of Object.entries(modes)) this.modes.set(name, new Mode({
904
- name,
905
- definition: def
906
- }));
907
- return this;
908
- }
909
- registerModifiers(modifiers) {
910
- for (const [name, def] of Object.entries(modifiers)) {
911
- const key = name;
912
- this.modifiers.set(key, new Modifier({
913
- name: key,
914
- definition: def,
915
- compositeLookup: (ref) => this.#lookupCompositeSelector(ref),
916
- modeLookup: (ref) => this.#lookupModeSelector(ref)
917
- }));
918
- }
919
- return this;
920
- }
921
- registerTokenGroups(groups) {
922
- for (const [namespace, def] of Object.entries(groups)) {
923
- if (def == null) throw new Error(`Config.registerTokenGroups: token group "${namespace}" is undefined — the passed value doesn't exist. If it came from a registry barrel, check the barrel's tokenGroups keys.`);
924
- for (const [tokenName, tokenDef] of Object.entries(def.tokens)) {
925
- if (tokenDef == null) throw new Error(`Config.registerTokenGroups: token "${tokenName}" in group "${namespace}" is undefined — the passed value doesn't exist. If it came from a registry barrel, check the barrel's tokens keys.`);
926
- assertLinkedTokenPinned(namespace, tokenName, tokenDef.link, this.linkedSystems);
927
- }
928
- this.tokenGroups.set(namespace, new TokenGroup({
929
- namespace,
930
- prefixGetter: () => this.prefix,
931
- definition: def,
932
- tokenLookup: (ref, context) => this.resolveTokenRef(ref, context),
933
- linkedVarRef: (system, ref) => {
934
- const prefix = this.linkedNodeVarPrefix(system.id, system.version);
935
- return prefix ? makeTokenRefToCss(prefix)(ref) : void 0;
936
- },
937
- stylePropertiesLookup: (ns) => this.#stylePropertiesFor(ns)
938
- }));
939
- }
940
- this.#invalidate();
941
- return this;
942
- }
943
- registerStyleProps(props) {
944
- for (const [name, def] of Object.entries(props)) this.styleProps.set(name, new StyleProp({
945
- name,
946
- definition: def,
947
- tokenLookup: (groupName) => this.#tokensInGroup(groupName),
948
- prefixGetter: () => this.prefix
949
- }));
950
- return this;
951
- }
952
- registerComposites(composites) {
953
- for (const [name, def] of Object.entries(composites)) {
954
- assertLinkedValuePinned("composite-style", `Config.registerComposites: composite "${name}"`, def.link, this.linkedSystems);
955
- this.compositeStyles.set(name, new CompositeStyle({
956
- name,
957
- definition: def,
958
- prefixGetter: () => this.prefix
959
- }));
960
- }
961
- return this;
962
- }
963
- registerMotion(motion) {
964
- for (const [name, def] of Object.entries(motion)) this.motion.set(name, def.runtime === "js" ? new JsMotionDef({
965
- name,
966
- definition: def
967
- }) : new CssMotionDef({
968
- name,
969
- definition: def
970
- }));
971
- return this;
972
- }
973
- /**
974
- * Pin the source systems this config links values from. Each record key is
975
- * the source system's database UUID (the identity — globally unique,
976
- * immutable, rename-proof); the pin carries the system's display-name
977
- * snapshot at link time and an exact published version.
978
- *
979
- * One pin per system, one version per pin. Every linked value registered
980
- * afterwards (a barrel value carrying a `link` marker) is validated against
981
- * its pin — an unpinned system or a version mismatch throws at
982
- * registration, so pins must appear before the `register*` calls that use
983
- * them. The pin's `name` also feeds the registry folder slug the pull
984
- * pipeline derives.
985
- */
986
- registerLinkedSystems(input) {
987
- for (const [systemId, pin] of Object.entries(input)) {
988
- validateLinkedSystemPin(systemId, pin, this.linkedSystems);
989
- this.linkedSystems.set(systemId, {
990
- name: pin.name,
991
- version: pin.version
992
- });
993
- }
994
- return this;
995
- }
996
- registerComponents(components) {
997
- const parentOf = /* @__PURE__ */ new Map();
998
- const childrenOf = /* @__PURE__ */ new Map();
999
- const flat = new Map(Object.entries(components));
1000
- for (const [parentName, def] of Object.entries(components)) {
1001
- if (def == null) throw new Error(`Config.registerComponents: component "${parentName}" is undefined — the passed value doesn't exist. If it came from a registry barrel, check the barrel's components keys.`);
1002
- const subs = def.__subcomponents;
1003
- if (!subs) continue;
1004
- const childNames = [];
1005
- for (const [childName, child] of Object.entries(subs)) {
1006
- if (child == null) throw new Error(`Config.registerComponents: subcomponent "${childName}" of "${parentName}" is undefined — the passed value doesn't exist.`);
1007
- const existing = parentOf.get(childName);
1008
- if (existing !== void 0) throw new Error(`registerComponents: subcomponent "${childName}" registered under both "${existing}" and "${parentName}"`);
1009
- if (Object.hasOwn(components, childName)) throw new Error(`registerComponents: "${childName}" is registered both as a top-level component and as a subcomponent of "${parentName}" — pick one`);
1010
- flat.set(childName, child);
1011
- parentOf.set(childName, parentName);
1012
- childNames.push(childName);
1013
- }
1014
- childrenOf.set(parentName, childNames);
1015
- }
1016
- for (const [name, def] of flat) {
1017
- if (def.extension && !def.link) throw new Error(`Config.registerComponents: component "${name}" carries an extension record but no link marker — extensions only apply to components linked from another system.`);
1018
- assertLinkedValuePinned("component", `Config.registerComponents: component "${name}"`, def.link, this.linkedSystems);
1019
- }
1020
- for (const [name, def] of flat) Object.defineProperty(def, "__componentName", {
1021
- enumerable: false,
1022
- configurable: true,
1023
- writable: true,
1024
- value: name
1025
- });
1026
- for (const [name, def] of flat) {
1027
- assertNameAvailable(this.components, name, this.namespace);
1028
- assertDefinitionAcceptsPlaceable(name, def);
1029
- this.components.set(name, new Component({
1030
- name,
1031
- definition: def,
1032
- subcomponentOf: parentOf.get(name),
1033
- subcomponents: childrenOf.get(name),
1034
- namespaceGetter: () => this.namespace,
1035
- prefixGetter: () => this.prefix,
1036
- componentResolver: (ref) => this.getComponent(ref),
1037
- resolveStylePropValueToken: (prop, value) => this.#resolveStylePropValueToken(prop, value),
1038
- isStyleProp: (propName) => this.styleProps.has(propName)
1039
- }));
1040
- }
1041
- return this;
1042
- }
1043
- /**
1044
- * Build a single component from its definition with this config's context
1045
- * getters wired — `prefixGetter`, `namespaceGetter`, `componentResolver` —
1046
- * and set it on the registry, replacing any existing entry of the same name.
1047
- *
1048
- * This is the ONE sanctioned way to put a constructed component into the
1049
- * config outside `registerComponents`. Constructing a `Component` by hand and
1050
- * calling `config.components.set(...)` silently drops those getters, so the
1051
- * component's `derived.classNames` fall back to an empty prefix/namespace and
1052
- * its anatomy CSS emits unprefixed (`:where(.button-root)`) — it renders
1053
- * unstyled while the runtime applies the prefixed class. Patch-apply code that
1054
- * rebuilds a component from a mutated definition (e.g.
1055
- * `applyComponentStyleUpdatePatch`) routes through here so the wiring can't be
1056
- * forgotten.
1057
- */
1058
- upsertComponent(name, definition, meta) {
1059
- this.components.set(name, new Component({
1060
- name,
1061
- definition,
1062
- componentGroup: meta?.componentGroup,
1063
- subcomponentOf: meta?.subcomponentOf,
1064
- subcomponents: meta?.subcomponents,
1065
- namespaceGetter: () => this.namespace,
1066
- prefixGetter: () => this.prefix,
1067
- componentResolver: (ref) => this.getComponent(ref),
1068
- resolveStylePropValueToken: (prop, value) => this.#resolveStylePropValueToken(prop, value),
1069
- isStyleProp: (propName) => this.styleProps.has(propName)
1070
- }));
1071
- return this;
1072
- }
1073
- /**
1074
- * Register one or more labeled bundles of components. Each group's
1075
- * `components` record is flattened into `this.components` (same
1076
- * surface as `registerComponents`), and every member's
1077
- * `Component.derived.componentGroup` carries the group key. The
1078
- * group entry itself lives on `this.componentGroups[key]` with a
1079
- * JSON-safe ref list (`components: readonly string[]`).
1080
- *
1081
- * Subcomponents declared on a grouped parent via
1082
- * `.subcomponents({...})` auto-register alongside their parent;
1083
- * they carry `subcomponentOf` instead of `componentGroup` (a
1084
- * subcomponent belongs to a parent, not a group).
1085
- *
1086
- * Component names are the identity (`<namespace>:<name>`, the spec
1087
- * `type`) and must be unique across the whole config — the same name
1088
- * under two groups, repeated in one group, or already registered by a
1089
- * prior `register*` call throws. Two components that should *display*
1090
- * the same use distinct names + `metadata.label`.
1091
- */
1092
- registerComponentGroups(groups) {
1093
- const groupOf = /* @__PURE__ */ new Map();
1094
- const parentOf = /* @__PURE__ */ new Map();
1095
- const childrenOf = /* @__PURE__ */ new Map();
1096
- const flat = /* @__PURE__ */ new Map();
1097
- const groupRecords = /* @__PURE__ */ new Map();
1098
- for (const [groupKey, group] of Object.entries(groups)) {
1099
- const componentNames = [];
1100
- for (const [name, def] of Object.entries(group.components)) {
1101
- if (this.components.has(name) || flat.has(name)) throw new Error(`registerComponentGroups: component "${name}" is already registered${groupOf.get(name) ? ` (group "${groupOf.get(name)}")` : ""} — component names must be unique within a registry. Rename one (e.g. "Studio${name}") and give it metadata.label "${name}" for a shared display name.`);
1102
- flat.set(name, def);
1103
- groupOf.set(name, groupKey);
1104
- componentNames.push(name);
1105
- }
1106
- groupRecords.set(groupKey, {
1107
- label: group.label,
1108
- description: group.description,
1109
- parent: group.parent,
1110
- componentNames
1111
- });
1112
- }
1113
- for (const group of Object.values(groups)) for (const [parentName, def] of Object.entries(group.components)) {
1114
- const subs = def.__subcomponents;
1115
- if (!subs) continue;
1116
- const childNames = [];
1117
- for (const [childName, child] of Object.entries(subs)) {
1118
- const existingParent = parentOf.get(childName);
1119
- if (existingParent !== void 0) throw new Error(`registerComponentGroups: subcomponent "${childName}" registered under both "${existingParent}" and "${parentName}"`);
1120
- if (groupOf.has(childName)) throw new Error(`registerComponentGroups: "${childName}" is registered both as a top-level component and as a subcomponent of "${parentName}" — pick one`);
1121
- flat.set(childName, child);
1122
- parentOf.set(childName, parentName);
1123
- childNames.push(childName);
1124
- }
1125
- childrenOf.set(parentName, childNames);
1126
- }
1127
- for (const [name, def] of flat) assertLinkedValuePinned("component", `Config.registerComponentGroups: component "${name}"`, def.link, this.linkedSystems);
1128
- for (const [name, def] of flat) Object.defineProperty(def, "__componentName", {
1129
- enumerable: false,
1130
- configurable: true,
1131
- writable: true,
1132
- value: name
1133
- });
1134
- for (const [name, def] of flat) {
1135
- assertNameAvailable(this.components, name, this.namespace);
1136
- assertDefinitionAcceptsPlaceable(name, def);
1137
- this.components.set(name, new Component({
1138
- name,
1139
- definition: def,
1140
- componentGroup: groupOf.get(name),
1141
- subcomponentOf: parentOf.get(name),
1142
- subcomponents: childrenOf.get(name),
1143
- namespaceGetter: () => this.namespace,
1144
- prefixGetter: () => this.prefix,
1145
- componentResolver: (ref) => this.getComponent(ref),
1146
- resolveStylePropValueToken: (prop, value) => this.#resolveStylePropValueToken(prop, value),
1147
- isStyleProp: (propName) => this.styleProps.has(propName)
1148
- }));
1149
- }
1150
- for (const [groupKey, record] of groupRecords) this.componentGroups.set(groupKey, new ComponentGroup({
1151
- name: groupKey,
1152
- label: record.label,
1153
- description: record.description,
1154
- parent: record.parent,
1155
- components: record.componentNames
1156
- }));
1157
- for (const groupKey of groupRecords.keys()) {
1158
- const parent = this.componentGroups.get(groupKey)?.parent;
1159
- if (parent === void 0) continue;
1160
- if (!this.componentGroups.has(parent)) throw new Error(`Config.registerComponentGroups: componentGroup "${groupKey}" references unknown parent group "${parent}"`);
1161
- const path = [groupKey];
1162
- let current = parent;
1163
- while (current !== void 0) {
1164
- if (current === groupKey) throw new Error(`Config.registerComponentGroups: componentGroup parent chain forms a cycle: ${[...path, current].join(" -> ")}`);
1165
- path.push(current);
1166
- current = this.componentGroups.get(current)?.parent;
1167
- }
1168
- }
1169
- return this;
1170
- }
1171
- /**
1172
- * Register one or more asset groups. The record key IS the slug —
1173
- * stamped onto the group at registration (late-bound, like
1174
- * `registerComponentGroups` deriving a group's name from its key and
1175
- * `Component.registryKey` reading the namespace getter). Each member
1176
- * then resolves to `${namespace}:asset:${slug}/${assetName}` via
1177
- * `AssetGroup.assetType()`.
1178
- *
1179
- * Trade-off (accepted, same as `registerComponentGroups`): the
1180
- * `{ icons }` shorthand ties the slug to the variable name — write
1181
- * the key out explicitly (`{ icons: phosphorIcons }`) whenever it
1182
- * shouldn't track the variable.
1183
- */
1184
- registerAssetGroups(groups) {
1185
- for (const [slug, def] of Object.entries(groups)) {
1186
- if (!isAssetGroupRef(def)) {
1187
- const looksLikeBuilder = typeof def.config === "function";
1188
- throw new Error(looksLikeBuilder ? `registerAssetGroups: group "${slug}" is missing its .config({...}) call — defineAssetGroup(members).config({ sizes }) is a complete icon registration.` : `registerAssetGroups: value for "${slug}" is not an asset group — author it with defineAssetGroup(members).config({...}).`);
1189
- }
1190
- if (!/^[A-Za-z][A-Za-z0-9]*(?:[-_][A-Za-z0-9]+)*$/.test(slug)) throw new Error(`registerAssetGroups: slug "${slug}" is invalid — slugs become the asset-id group segment (alphanumeric segments separated by single "-" or "_").`);
1191
- if (this.assetGroups.has(slug)) throw new Error(`registerAssetGroups: asset group "${slug}" is already registered — slugs must be unique within a config.`);
1192
- const priorSlug = readAssetGroupSlug(def);
1193
- if (priorSlug !== void 0 && priorSlug !== slug) throw new Error(`registerAssetGroups: this group is already registered as "${priorSlug}" — a group has one identity; re-export it instead of registering it twice.`);
1194
- def[ASSET_GROUP_SLUG_KEY] = slug;
1195
- this.assetGroups.set(slug, new AssetGroup({
1196
- name: slug,
1197
- label: def.label ?? titleCaseSlug(slug),
1198
- version: def.version,
1199
- description: def.description,
1200
- assetKind: def.assetKind,
1201
- assetNames: Object.keys(def.members),
1202
- sizes: def.assetKind === "icon" ? def.sizes : void 0,
1203
- variants: def.assetKind === "icon" ? def.variants : void 0,
1204
- component: def.assetKind === "icon" ? def.component : void 0,
1205
- members: def.members,
1206
- memberMetadata: def.assetKind === "icon" ? def.memberMetadata : void 0,
1207
- namespaceGetter: () => this.namespace
1208
- }));
1209
- }
1210
- return this;
1211
- }
1212
- /**
1213
- * Map the Studio canvas insert tools to registered components.
1214
- *
1215
- * config.registerCanvasRoles({
1216
- * frame: [
1217
- * {
1218
- * component: Box, // or 'Box'
1219
- * default: true,
1220
- * defaultProps: { width: '[200px]', height: '[150px]' },
1221
- * },
1222
- * { component: HStack },
1223
- * ],
1224
- * text: [{ component: Text }],
1225
- * });
1226
- *
1227
- * `component` accepts the authored `defineComponent(...)` value or the
1228
- * registered name. Passing the value typechecks `defaultProps` against
1229
- * that component's real prop surface (see
1230
- * {@link CanvasRolesInputConstraint}); either form is normalized to the
1231
- * name for storage and the wire format.
1232
- *
1233
- * Every entry's component must already be registered — chain this
1234
- * after `registerComponents` / `registerComponentGroups`. At most one
1235
- * entry per role carries `default: true` (with none marked, the first
1236
- * entry is the default). Re-registering a role replaces its entry
1237
- * list wholesale.
1238
- */
1239
- registerCanvasRoles(roles) {
1240
- const incoming = Object.entries(roles);
1241
- for (const [role, entries] of incoming) {
1242
- if (entries.length === 0) throw new Error(`registerCanvasRoles: role "${role}" has no entries — register at least one component or omit the role.`);
1243
- const seenComponents = /* @__PURE__ */ new Set();
1244
- let defaultCount = 0;
1245
- const normalized = [];
1246
- for (const entry of entries) {
1247
- const name = resolveCanvasRoleComponentName(role, entry.component);
1248
- const component = this.getComponent(name);
1249
- if (component === void 0) throw new Error(`registerCanvasRoles: role "${role}" references unknown component "${name}" — register components before canvas roles.`);
1250
- if (component.derived.subcomponentOf !== void 0) throw new Error(`registerCanvasRoles: role "${role}" lists "${name}", a subcomponent of "${component.derived.subcomponentOf}" — canvas tools insert standalone components only.`);
1251
- const canonical = component.name;
1252
- if (seenComponents.has(canonical)) throw new Error(`registerCanvasRoles: role "${role}" lists component "${canonical}" twice.`);
1253
- seenComponents.add(canonical);
1254
- if (entry.default === true) {
1255
- defaultCount += 1;
1256
- if (defaultCount > 1) throw new Error(`registerCanvasRoles: role "${role}" marks more than one entry as default.`);
1257
- }
1258
- normalized.push({
1259
- component: canonical,
1260
- ...entry.default !== void 0 ? { default: entry.default } : {},
1261
- ...entry.defaultProps !== void 0 ? { defaultProps: entry.defaultProps } : {}
1262
- });
1263
- }
1264
- this.canvasRoles.set(role, normalized);
1265
- }
1266
- return this;
1267
- }
1268
- /** Entries registered for a canvas role (empty when unregistered). */
1269
- canvasRoleEntries(role) {
1270
- return this.canvasRoles.get(role) ?? [];
1271
- }
1272
- /**
1273
- * The component a canvas role's tool preselects — the entry marked
1274
- * `default: true`, or the first entry when none is marked. `undefined`
1275
- * when the role isn't registered (the canvas disables the tool).
1276
- */
1277
- defaultCanvasComponent(role) {
1278
- const entries = this.canvasRoles.get(role);
1279
- if (!entries || entries.length === 0) return void 0;
1280
- return (entries.find((entry) => entry.default) ?? entries[0]).component;
1281
- }
1282
- registerProviders(providers) {
1283
- for (const [name, ProviderFC] of Object.entries(providers)) {
1284
- ProviderFC.displayName = name;
1285
- const marker = ProviderFC.__udsProvider;
1286
- marker.name = name;
1287
- this.providers.set(name, new Provider({
1288
- name,
1289
- component: ProviderFC
1290
- }));
1291
- }
1292
- return this;
1293
- }
1294
- get derived() {
1295
- if (!this.#derived) {
1296
- const tokens = /* @__PURE__ */ new Map();
1297
- const tokensBySource = /* @__PURE__ */ new Map();
1298
- const cssVarRefs = {};
1299
- for (const [namespace, group] of this.tokenGroups) {
1300
- const groupRefs = {};
1301
- for (const [name, token] of group.tokens) {
1302
- tokens.set(`${namespace}/${name}`, token);
1303
- if (token.link) tokensBySource.set(`${token.link.systemId}:${token.link.sourceKey}`, token);
1304
- groupRefs[name] = token.derived.cssVarRef;
1305
- }
1306
- cssVarRefs[namespace] = groupRefs;
1307
- }
1308
- const modeOptionsByModifier = /* @__PURE__ */ new Map();
1309
- for (const modifier of this.modifiers.values()) {
1310
- const selector = modifier.selector;
1311
- if (!isModeRef(selector)) continue;
1312
- const option = this.#resolveModeOption(selector.ref);
1313
- if (option) modeOptionsByModifier.set(modifier.name, option);
1314
- }
1315
- this.#derived = {
1316
- tokens,
1317
- tokensBySource,
1318
- cssVarRefs,
1319
- modeOptionsByModifier
1320
- };
1321
- }
1322
- return this.#derived;
1323
- }
1324
- /**
1325
- * Resolve a `token()` ref, provenance-aware — the ONE lookup every ref
1326
- * consumer (registration wiring, `validate()`, codegen emission) should
1327
- * use. A ref authored on a linked token (`context` set) names the SOURCE
1328
- * system's groups, so it resolves through the provenance index only —
1329
- * falling back to the consumer's own keys would let a coincidentally-named
1330
- * local group silently shadow a missing source group. Local refs resolve
1331
- * against consumer keys.
1332
- */
1333
- resolveTokenRef(ref, context) {
1334
- if (context) return this.derived.tokensBySource.get(`${context.systemId}:${ref}`);
1335
- return this.derived.tokens.get(ref);
1336
- }
1337
- /**
1338
- * Resolve a component ref, provenance-aware — the component counterpart of
1339
- * {@link resolveTokenRef}. A layer ref authored on a linked component
1340
- * (`context` set) names the SOURCE system's components, so it resolves
1341
- * only through markers (the co-linked component whose `sourceKey` matches)
1342
- * — never through consumer names, where a coincidentally-named local
1343
- * component would silently shadow the source one.
1344
- */
1345
- resolveComponentRef(ref, context) {
1346
- if (context) {
1347
- for (const component of this.components.values()) if (component.link?.systemId === context.systemId && component.link.sourceKey === ref) return component;
1348
- return;
1349
- }
1350
- return this.getComponent(ref);
1351
- }
1352
- #invalidate() {
1353
- this.#derived = void 0;
1354
- }
1355
- #stylePropertiesFor(namespace) {
1356
- const out = [];
1357
- for (const styleProp of this.styleProps.values()) if (styleProp.consumedTokenGroups().includes(namespace)) out.push(styleProp.name);
1358
- return out;
1359
- }
1360
- /**
1361
- * Resolve a shorthand style-prop value to the qualified token it names —
1362
- * `('bg', 'tertiary')` → `'bg/tertiary'` — by checking the style prop's
1363
- * token groups for a token of that name. Returns `undefined` when the key
1364
- * isn't a registered style prop, or the value is a keyword / arbitrary
1365
- * literal rather than a token. Feeds `Component.derived.tokenRefs` so
1366
- * shorthand token usage is captured structurally, not by string matching.
1367
- */
1368
- #resolveStylePropValueToken(styleProp, value) {
1369
- const sp = this.styleProps.get(styleProp);
1370
- if (!sp) return void 0;
1371
- for (const namespace of sp.consumedTokenGroups()) if (this.tokenGroups.get(namespace)?.tokens.has(value)) return `${namespace}/${value}`;
1372
- }
1373
- *#tokensInGroup(namespace) {
1374
- const group = this.tokenGroups.get(namespace);
1375
- if (!group) return;
1376
- for (const token of group.tokens.values()) yield token;
1377
- }
1378
- #lookupCompositeSelector(ref) {
1379
- const [name, variant] = ref.split("/");
1380
- if (!name || !variant) return void 0;
1381
- const composite = this.compositeStyles.get(name);
1382
- if (!composite) return void 0;
1383
- if (!composite.styles.has(variant)) return void 0;
1384
- const { markerVarName, markerVarValue } = composite.derived;
1385
- return `@container style(${markerVarName}: ${markerVarValue(variant)})`;
1386
- }
1387
- /**
1388
- * Resolve a `mode()` ref (`'colorMode/dark'`) to its `ModeOption`. The single
1389
- * place that knows the ref's `group/option` shape — both the CSS-selector
1390
- * lookup and the modifier→mode reverse map go through here, so the ref syntax
1391
- * lives in exactly one spot.
1392
- */
1393
- #resolveModeOption(ref) {
1394
- const [name, optionName] = ref.split("/");
1395
- if (!name || !optionName) return void 0;
1396
- return this.modes.get(name)?.options.get(optionName);
1397
- }
1398
- #lookupModeSelector(ref) {
1399
- return this.#resolveModeOption(ref)?.css;
1400
- }
1401
- /**
1402
- * Validate cross-references. Throws on cycles in token aliases or on
1403
- * unknown refs in markers (`token()`, `composite()`, `mode()`,
1404
- * `styleProp()`, `tokenGroup()`).
1405
- */
1406
- validate() {
1407
- const tokens = this.derived.tokens;
1408
- for (const [qualified, token] of tokens) visit(qualified, token, (ref, context) => this.resolveTokenRef(ref, context), /* @__PURE__ */ new Set());
1409
- for (const [name, component] of this.components) {
1410
- if (!component.link) continue;
1411
- for (const layer of component.layers.values()) {
1412
- const renders = layer.renders;
1413
- if (renders?.kind !== "component") continue;
1414
- if (this.resolveComponentRef(renders.ref, component.link)) continue;
1415
- throw new Error(`Component '${name}' is linked from system "${component.link.systemId}" and renders its component '${renders.ref}', which nothing in this config provides — link the source system's "${renders.ref}" component too (refs on linked components resolve by provenance, not by local names).`);
1416
- }
1417
- }
1418
- for (const styleProp of this.styleProps.values()) for (const groupName of styleProp.consumedTokenGroups()) if (!this.tokenGroups.has(groupName)) throw new Error(`Style prop '${styleProp.name}' references unknown token group '${groupName}'`);
1419
- for (const modifier of this.modifiers.values()) {
1420
- const selector = modifier.selector;
1421
- if (typeof selector === "object" && selector !== null) {
1422
- if (selector.__kind === "composite") {
1423
- if (this.#lookupCompositeSelector(selector.ref) === void 0) throw new Error(`Modifier '${modifier.name}' references unknown composite '${selector.ref}'`);
1424
- }
1425
- if (selector.__kind === "mode") {
1426
- if (this.#lookupModeSelector(selector.ref) === void 0) throw new Error(`Modifier '${modifier.name}' references unknown mode '${selector.ref}'`);
1427
- }
1428
- }
1429
- }
1430
- }
1431
- /**
1432
- * Deep-fork for Studio drafts. Re-builds every entity from `toJSON` /
1433
- * `fromJSON` rather than sharing instances so the clone is fully
1434
- * isolated.
1435
- *
1436
- * `overlay` shallow-merges into the serialized form before re-hydration —
1437
- * lets callers swap top-level slices (`{ components, tokenGroups }`)
1438
- * without round-tripping through the chain methods. Each overlay key
1439
- * fully replaces the existing slice (no per-entry merge); pass the
1440
- * full record you want for that slice.
1441
- */
1442
- clone(overlay) {
1443
- const base = this.toJSON();
1444
- return Config.fromJSON(overlay ? {
1445
- ...base,
1446
- ...overlay
1447
- } : base);
1448
- }
1449
- /**
1450
- * Serialize to the wire format. Derived data is never included.
1451
- * `meta.builtAt` lands in the JSON when the caller passes it (CLI
1452
- * stamps it at write time so re-serializing in tests stays stable).
1453
- * `meta.projectRoot` relativizes per-component `sourceFilePath`
1454
- * values at the wire boundary — keeps `config.json` portable across
1455
- * machines. Omitting it leaves paths absolute.
1456
- */
1457
- toJSON(meta) {
1458
- const out = {
1459
- schemaVersion: Config.SCHEMA_VERSION,
1460
- prefix: this.prefix,
1461
- preflight: this.preflight
1462
- };
1463
- if (meta?.builtAt !== void 0) out.builtAt = meta.builtAt;
1464
- if (this.namespace !== void 0) out.namespace = this.namespace;
1465
- if (this.designPrinciples.length > 0) out.designPrinciples = this.designPrinciples;
1466
- if (Object.keys(this.globalStyles).length > 0) out.globalStyles = this.globalStyles;
1467
- if (this.rawCss.length > 0) out.rawCss = this.rawCss;
1468
- if (Object.keys(this.buildOptions).length > 0) out.buildOptions = this.buildOptions;
1469
- if (this.modes.size > 0) out.modes = mapToRecord(this.modes, (mode) => mode.toJSON());
1470
- if (this.modifiers.size > 0) out.modifiers = mapToRecord(this.modifiers, stripName);
1471
- if (this.tokenGroups.size > 0) out.tokenGroups = mapToRecord(this.tokenGroups, (group) => group.toJSON());
1472
- if (this.styleProps.size > 0) out.styleProps = mapToRecord(this.styleProps, stripName);
1473
- if (this.compositeStyles.size > 0) out.compositeStyles = mapToRecord(this.compositeStyles, stripName);
1474
- if (this.motion.size > 0) {
1475
- const motionRecord = {};
1476
- for (const [name, motion] of this.motion) {
1477
- const { name: _omit, ...rest } = motion.toJSON();
1478
- motionRecord[name] = rest;
1479
- }
1480
- out.motion = motionRecord;
1481
- }
1482
- if (this.components.size > 0) out.components = mapToRecord(this.components, (component) => {
1483
- const { name: _name, ...rest } = component.toJSON({ projectRoot: meta?.projectRoot });
1484
- return rest;
1485
- });
1486
- if (this.componentGroups.size > 0) out.componentGroups = mapToRecord(this.componentGroups, (group) => {
1487
- const { name: _omit, ...rest } = group.toJSON();
1488
- return rest;
1489
- });
1490
- if (this.assetGroups.size > 0) out.assetGroups = mapToRecord(this.assetGroups, (group) => {
1491
- const { name: _omit, ...rest } = group.toJSON();
1492
- return rest;
1493
- });
1494
- if (this.providers.size > 0) {
1495
- const providers = {};
1496
- for (const name of this.providers.keys()) providers[name] = {};
1497
- out.providers = providers;
1498
- }
1499
- if (this.canvasRoles.size > 0) {
1500
- const canvasRoles = {};
1501
- for (const [role, entries] of this.canvasRoles) canvasRoles[role] = entries;
1502
- out.canvasRoles = canvasRoles;
1503
- }
1504
- if (this.playgroundOptions.port !== void 0) out.playgroundOptions = { port: this.playgroundOptions.port };
1505
- if (this.linkedSystems.size > 0) out.linkedSystems = mapToRecord(this.linkedSystems, (pin) => ({ ...pin }));
1506
- return out;
1507
- }
1508
- /**
1509
- * Hydrate from the wire format. Throws when `schemaVersion` doesn't
1510
- * match — the CLI's job is to translate the error into "your manifest
1511
- * is from an older build; run `uds build`."
1512
- */
1513
- static fromJSON(json) {
1514
- if (json.schemaVersion !== Config.SCHEMA_VERSION) throw new Error(`Config schema version mismatch: file is v${json.schemaVersion}, runtime expects v${Config.SCHEMA_VERSION}. Re-run \`uds build\`.`);
1515
- const config = new Config();
1516
- config.prefix = json.prefix;
1517
- config.preflight = json.preflight;
1518
- if (json.namespace !== void 0) config.namespace = json.namespace;
1519
- if (json.designPrinciples) config.designPrinciples = [...json.designPrinciples];
1520
- if (json.buildOptions) config.buildOptions = { ...json.buildOptions };
1521
- if (json.globalStyles) config.globalStyles = json.globalStyles;
1522
- if (json.rawCss) config.rawCss = [...json.rawCss];
1523
- if (json.modes) config.registerModes(json.modes);
1524
- if (json.linkedSystems) config.registerLinkedSystems(json.linkedSystems);
1525
- if (json.tokenGroups) config.registerTokenGroups(json.tokenGroups);
1526
- if (json.styleProps) config.registerStyleProps(json.styleProps);
1527
- if (json.modifiers) config.registerModifiers(json.modifiers);
1528
- if (json.compositeStyles) config.registerComposites(json.compositeStyles);
1529
- if (json.motion) config.registerMotion(json.motion);
1530
- const componentsByName = json.components ?? {};
1531
- for (const def of Object.values(componentsByName)) if (def.sourceFilePath) attachSourcePath(def, def.sourceFilePath);
1532
- const isSubcomponent = /* @__PURE__ */ new Set();
1533
- for (const parentDef of Object.values(componentsByName)) {
1534
- const subs = parentDef.subcomponents;
1535
- if (!subs || subs.length === 0) continue;
1536
- const childMap = {};
1537
- for (const childName of subs) {
1538
- const child = componentsByName[childName];
1539
- if (!child) continue;
1540
- childMap[childName] = child;
1541
- isSubcomponent.add(childName);
1542
- }
1543
- if (Object.keys(childMap).length === 0) continue;
1544
- Object.defineProperty(parentDef, "__subcomponents", {
1545
- enumerable: false,
1546
- configurable: true,
1547
- writable: true,
1548
- value: childMap
1549
- });
1550
- }
1551
- const grouped = /* @__PURE__ */ new Set();
1552
- if (json.componentGroups) {
1553
- const groupDefs = {};
1554
- for (const [groupKey, group] of Object.entries(json.componentGroups)) {
1555
- const components = {};
1556
- for (const name of group.components) {
1557
- if (isSubcomponent.has(name)) continue;
1558
- const def = componentsByName[name];
1559
- if (def === void 0) throw new Error(`Config.fromJSON: componentGroup "${groupKey}" references unknown component "${name}"`);
1560
- components[name] = def;
1561
- grouped.add(name);
1562
- }
1563
- groupDefs[groupKey] = {
1564
- label: group.label,
1565
- ...group.description !== void 0 ? { description: group.description } : {},
1566
- ...group.parent !== void 0 ? { parent: group.parent } : {},
1567
- components
1568
- };
1569
- }
1570
- config.registerComponentGroups(groupDefs);
1571
- }
1572
- if (json.components) {
1573
- const ungrouped = {};
1574
- for (const [name, def] of Object.entries(componentsByName)) {
1575
- if (grouped.has(name) || isSubcomponent.has(name)) continue;
1576
- ungrouped[name] = def;
1577
- }
1578
- if (Object.keys(ungrouped).length > 0) config.registerComponents(ungrouped);
1579
- }
1580
- if (json.assetGroups) for (const [slug, group] of Object.entries(json.assetGroups)) config.assetGroups.set(slug, AssetGroup.fromJSON(slug, group, () => config.namespace));
1581
- if (json.canvasRoles) for (const [role, entries] of Object.entries(json.canvasRoles)) {
1582
- const resolvable = entries.filter((entry) => config.getComponent(entry.component) !== void 0);
1583
- if (resolvable.length === 0) continue;
1584
- config.registerCanvasRoles({ [role]: resolvable });
1585
- }
1586
- if (json.playgroundOptions?.port !== void 0) config.playgroundOptions = { port: json.playgroundOptions.port };
1587
- return config;
1588
- }
1589
- };
1590
- /**
1591
- * Structural validation for a registry namespace. Guards only the
1592
- * invariants the namespacing scheme depends on — non-empty, no `:`
1593
- * (the `<namespace>:<Component>` separator), no whitespace. Stricter
1594
- * format rules and uniqueness / ownership are enforced at push time
1595
- * against the registered systems. Returns the value unchanged on
1596
- * success; throws otherwise.
1597
- */
1598
- /**
1599
- * Guard against registering two components under the same name. The name
1600
- * is the component's identity (`<namespace>:<name>`, the spec `type`), so
1601
- * a collision is unresolvable — a spec referencing it couldn't tell the
1602
- * two apart. Thrown across every registration path (flat + grouped +
1603
- * cross-call) so author-time mistakes fail loudly instead of silently
1604
- * shadowing a component. Two components that should *display* the same
1605
- * use distinct names + `metadata.label`.
1606
- */
1607
- /** A non-null object — a nested style bag (per-slot styles or a `_<modifier>`
1608
- * sub-object) to descend into when walking component base styles. */
1609
- function isStyleRecord(value) {
1610
- return typeof value === "object" && value !== null;
1611
- }
1612
- /**
1613
- * Collapse a `registerCanvasRoles` entry's `component` to its registered
1614
- * name. A component *value* resolves through the non-enumerable
1615
- * `__componentName` slot `registerComponents` stamps on every definition,
1616
- * so an unregistered value fails loudly instead of storing garbage.
1617
- */
1618
- function resolveCanvasRoleComponentName(role, component) {
1619
- if (typeof component === "string") return component;
1620
- const name = component.__componentName;
1621
- if (typeof name === "string") return name;
1622
- throw new Error(`registerCanvasRoles: role "${role}" received a component value that isn't registered — register components before canvas roles.`);
1623
- }
1624
- function assertNameAvailable(components, name, namespace) {
1625
- if (!components.has(name)) return;
1626
- const qualified = namespace ? `${namespace}:${name}` : name;
1627
- throw new Error(`Config: component "${name}" is already registered (identity "${qualified}") — component names must be unique within a registry. Rename one (e.g. "Studio${name}") and give it metadata.label "${name}" for a shared display name.`);
1628
- }
1629
- /**
1630
- * Split a spec element `type` / registry key into its namespace + bare name.
1631
- * `'uds:Text'` → `{ namespace: 'uds', name: 'Text' }`; a bare `'Text'` →
1632
- * `{ name: 'Text' }`. The namespace is everything before the first `:`
1633
- * (namespaces are validated to contain no `:`, see {@link validateNamespace}),
1634
- * so a scoped namespace like `'@acme/system:Button'` still splits on the right
1635
- * colon. The inverse of {@link Component.registryKey}.
1636
- */
1637
- function parseRegistryKey(type) {
1638
- const sep = type.indexOf(":");
1639
- if (sep < 0) return { name: type };
1640
- return {
1641
- namespace: type.slice(0, sep),
1642
- name: type.slice(sep + 1)
1643
- };
1644
- }
1645
- function validateNamespace(value) {
1646
- if (value.length === 0) throw new Error("Config.configure: namespace must be non-empty.");
1647
- if (value.includes(":")) throw new Error(`Config.configure: namespace "${value}" must not contain ":" — it's the reserved \`<namespace>:<Component>\` separator.`);
1648
- if (/\s/.test(value)) throw new Error(`Config.configure: namespace "${value}" must not contain whitespace.`);
1649
- if (value === "foreign") throw new Error(`Config.configure: namespace "${FOREIGN_NAMESPACE}" is reserved for third-party leaf components (the \`${FOREIGN_NAMESPACE}:<Component>\` sentinel).`);
1650
- return value;
1651
- }
1652
- /**
1653
- * Cross-reference walk for one token's alias chain, provenance-aware.
1654
- * `resolve` is the config's `resolveTokenRef` — a ref on a linked token
1655
- * resolves through the provenance index (source-system keys), a local ref
1656
- * through consumer keys. A dangling ref on a linked token is a closure
1657
- * violation (the source group it names was never linked), which gets its own
1658
- * actionable error; a dangling local ref stays the classic unknown-token
1659
- * error. Modifier refs are checked for linked tokens only — the closure rule
1660
- * is new with linking, and local modifier behavior is unchanged.
1661
- */
1662
- function visit(qualified, token, resolve, seen) {
1663
- if (seen.has(qualified)) throw new Error(`Token cycle detected at '${qualified}'`);
1664
- const followRef = (marker) => {
1665
- let context = token.link;
1666
- if (marker.scope === "consumer") context = void 0;
1667
- else if (marker.system) context = {
1668
- kind: "token",
1669
- systemId: marker.system.id,
1670
- systemVersion: marker.system.version,
1671
- sourceKey: marker.ref
1672
- };
1673
- const next = resolve(marker.ref, context);
1674
- if (!next) {
1675
- if (marker.system) return;
1676
- if (context && token.link) {
1677
- const [sourceGroup] = marker.ref.split("/");
1678
- throw new Error(`Token '${qualified}' is linked from system "${token.link.systemId}" and references its token '${marker.ref}', which nothing in this config provides — link the source system's "${sourceGroup}" group too (refs on linked tokens resolve by provenance, not by local group names).`);
1679
- }
1680
- throw new Error(`Token '${qualified}' references unknown token '${marker.ref}'`);
1681
- }
1682
- visit(`${next.namespace}/${next.name}`, next, resolve, new Set(seen).add(qualified));
1683
- };
1684
- if (isTokenRefValue(token.value)) followRef(token.value);
1685
- if (token.link && token.modifiers) for (const value of Object.values(token.modifiers)) {
1686
- const inner = typeof value === "object" && value !== null && "value" in value ? value.value : value;
1687
- if (isTokenRefValue(inner)) followRef(inner);
1688
- }
1689
- }
1690
- /** A `token()` ref marker — `{ ref: 'ns/name' }` — in a value position. */
1691
- function isTokenRefValue(value) {
1692
- return typeof value === "object" && value !== null && "ref" in value && typeof value.ref === "string";
1693
- }
1694
- function stripName(entity) {
1695
- const { name: _name, namespace: _namespace, ...rest } = entity.toJSON();
1696
- return rest;
1697
- }
1698
- /**
1699
- * Default asset-group label from its slug — `'icons'` → `'Icons'`,
1700
- * `'brand-icons'` → `'Brand Icons'`. `label` is optional and defaults
1701
- * to the title-cased registration key.
1702
- */
1703
- function titleCaseSlug(slug) {
1704
- return slug.split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
1705
- }
1706
- function mapToRecord(map, serializer) {
1707
- const out = {};
1708
- for (const [key, value] of map) out[key] = serializer(value);
1709
- return out;
1710
- }
1711
- //#endregion
1712
- export { Config, parseRegistryKey };