@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
@@ -0,0 +1,49 @@
1
+ import { Ref } from "./utils/refs.js";
2
+ //#region src/framework/inline-sources.d.ts
3
+ /**
4
+ * One value, with every ref through `slug` — or through any source when `slug` is omitted — replaced by
5
+ * what it resolves to.
6
+ *
7
+ * **Resolution hops configs.** A source's own value may be a ref into a third system, under a slug only
8
+ * that source declares, so the recursion continues in the config that answered rather than in ours, and
9
+ * `configFor` is what knows which one that is. Recursing against our own config would leave a ref naming
10
+ * a slug we never declared — the chained-link case in `inline-sources.test.ts`.
11
+ *
12
+ * The recursion takes all sources even when the top-level call is scoped to one, because a value about
13
+ * to become ours must not keep pointing anywhere.
14
+ */
15
+ /**
16
+ * What to do with a sourced ref that names something with no value — a component, a `file`, a style
17
+ * property. There is nothing to substitute for one, so it needs its own answer, and which answer
18
+ * depends on whether the pin survives the edit.
19
+ *
20
+ * **It survives** for publishing and for reading a repo-local chain. The seam stays, so the ref stays:
21
+ * `keepIdentityRefs`. A borrowed component's `file` has to keep naming the system that owns the
22
+ * implementation or nothing can find it.
23
+ *
24
+ * **It is dropped** when a whole source is being taken in. Keeping the ref would dangle it the moment
25
+ * the declaration goes, and `delete` would refuse while the ref is still a referrer. It mustn't be
26
+ * refused either, because what it names is usually about to become this config's own — the same walk
27
+ * materializes it. So the ref is adopted, requalified at the local entity, and only a target that lands
28
+ * nowhere local is refused. See {@link adoptedRefs}.
29
+ */
30
+ interface InlineOptions {
31
+ readonly keepIdentityRefs?: boolean;
32
+ /**
33
+ * Called for a ref that can be neither inlined nor requalified, in place of the refusal. The ref is
34
+ * left as it is so the walk reaches every other ref. A plan sets this to name every such ref rather
35
+ * than the first.
36
+ */
37
+ readonly onUnadoptable?: (ref: Ref) => void;
38
+ /**
39
+ * Where each thing borrowed through the slug will live once the pin is gone, as
40
+ * `style-property:bg@ds` → `style-property:bg`.
41
+ *
42
+ * Derived, not a caller's choice: whoever is dropping the pin builds it ({@link adoptedRefs}) and
43
+ * the walk threads it. It is in the options bag rather than a parameter because the walk is
44
+ * recursive and every hop needs it; its presence is what says "the seam is going".
45
+ */
46
+ readonly adopted?: ReadonlyMap<string, string>;
47
+ }
48
+ //#endregion
49
+ export { InlineOptions };
@@ -0,0 +1,388 @@
1
+ import { rejectData } from "./rejection.js";
2
+ import { MINTED, collectRefs, entityRefOf, isRef, kindOf, pathOf, ref, rewriteRefs, sourceOf } from "./utils/refs.js";
3
+ import { kindBorrowable } from "./registry.js";
4
+ //#region src/framework/inline-sources.ts
5
+ /**
6
+ * Why a sourced ref with no value could not be carried across a dropped pin.
7
+ *
8
+ * Two different dead ends, and telling them apart is the whole point: the advice for one is a real
9
+ * Next step and the advice for the other is impossible, so a single sentence covering both sends half
10
+ * its readers somewhere `link` refuses to go.
11
+ *
12
+ * **A borrowable kind** — a `style-property`, a component. The ref can become ours, but only by being
13
+ * copied first, so `linked-system/copy` is the fix. What this must not say is that we have no entity
14
+ * at the path: we may well have one, and it is simply not the entity this ref names. A local `bg`
15
+ * that happens to share a name with theirs is a different declaration, and quietly repointing at it
16
+ * would change what every callsite means.
17
+ *
18
+ * **A kind that is not borrowable** — a `file`, which is how a borrowed component's implementation
19
+ * travels. `link` refuses these by design (they belong to the system that holds them), so "link it
20
+ * too" is advice with nowhere to go. The honest next step is the other end: the borrow that pulled the
21
+ * file in is what has to be detached or dropped.
22
+ */
23
+ function unadoptableMessage({ value, config, slug }) {
24
+ const kind = kindOf(value);
25
+ const path = pathOf(value);
26
+ const source = config.sourceFor(slug);
27
+ if (!kindBorrowable(config.entityOf(kind), {
28
+ config,
29
+ source
30
+ })) return `Cannot inline "${value.__ref}": ${kind} entries are not borrowable — they belong to "${slug}", and dropping the pin would leave this one unreachable. Detach what borrows it first (a borrowed component's implementation travels as its ${kind}), or drop what references it.`;
31
+ return `Cannot inline "${value.__ref}": nothing there has a value to keep, and this config has not taken over "${slug}"'s ${kind} "${path}" — a local ${kind} at the same path is a different declaration, so pointing at it would change what every callsite means. Link that ${kind} too, then break the link — or drop what references it.`;
32
+ }
33
+ function inlineSourcedRefs(value, config, slug, options) {
34
+ if (isRef(value)) {
35
+ const source = sourceOf(value);
36
+ if (source === void 0) return value;
37
+ if (slug !== void 0 && source !== slug) return value;
38
+ const owner = config.configFor(value);
39
+ if (!owner) rejectData(`Cannot inline "${value.__ref}": link "${source}" has no source attached and the artifact recorded nothing for it. Rebuild with the link installed before breaking it.`);
40
+ const target = config.resolveRef(value);
41
+ const body = target?.toJSON();
42
+ if (!target && !declaresGroup(owner, value)) rejectData(`Cannot inline "${value.__ref}": "${source}" has no ${kindOf(value)} at "${pathOf(value)}". A pin is exact, so this is a ref into a version that does not declare it.`);
43
+ if (body?.value === void 0) {
44
+ if (options?.keepIdentityRefs) return value;
45
+ const adopted = options?.adopted?.get(entityRefOf(value.__ref));
46
+ if (adopted !== void 0) return rewriteRefs(value, entityRefOf(value.__ref), adopted);
47
+ if (config.entityOf(kindOf(value))?.authoredRefs === false) return;
48
+ if (options?.onUnadoptable) {
49
+ options.onUnadoptable(value);
50
+ return value;
51
+ }
52
+ rejectData(unadoptableMessage({
53
+ value,
54
+ config,
55
+ slug: source
56
+ }));
57
+ }
58
+ return inlineSourcedRefs(body.value, owner, void 0, options);
59
+ }
60
+ if (Array.isArray(value)) return value.map((item) => inlineSourcedRefs(item, config, slug, options));
61
+ if (value && typeof value === "object") {
62
+ const out = {};
63
+ for (const [key, nested] of Object.entries(value)) out[key] = inlineSourcedRefs(nested, config, slug, options);
64
+ return out;
65
+ }
66
+ return value;
67
+ }
68
+ /**
69
+ * One entity's body, with a borrowed entity taken whole rather than field by field.
70
+ *
71
+ * A ref names an entity, so borrowing one takes everything it declares: a token's conditional values come
72
+ * with its default, the way they came together in the source. Inlining only `value` is a silent partial
73
+ * copy — a colour that flips in dark mode arrives flat, and nothing says so.
74
+ *
75
+ * The merge is the borrower's fields over the target's, the same `{ ...base, ...override }` rule
76
+ * `resolveDerived` uses for a derived member, so a reader learns it once. Declaring your own `overrides`
77
+ * therefore replaces theirs rather than layering on them, which is the plain reading of a field you did
78
+ * state.
79
+ *
80
+ * Only the primary field position is treated this way. A ref elsewhere in a body — a modifier's
81
+ * `selector`, a canvas role's `component` — is a pointer to a thing rather than a borrowing of it, so it
82
+ * keeps the plain value substitution.
83
+ *
84
+ * The kind says which field that is (`entry.primaryField`), the same field `link` writes a bare ref into.
85
+ * Reading `body.value` instead would name the primary field of `token` and of nothing else, so a borrowed
86
+ * `style-property`, whose primary is `properties`, would miss this path, fall through to the plain
87
+ * substitution and throw "nothing there has a value to keep" — making any system that borrows a
88
+ * non-token entity impossible to publish or take in. See
89
+ * `.claude/rules/config-framework-kind-agnostic.md`.
90
+ */
91
+ function inlineWholeEntity({ body, kind, config, slug, options }) {
92
+ const primary = config.entityOf(kind)?.primaryField;
93
+ const pointer = primary === void 0 ? void 0 : body[primary];
94
+ if (primary !== void 0 && isRef(pointer) && wants(pointer, slug)) {
95
+ const target = config.resolveRef(pointer);
96
+ const owner = config.configFor(pointer);
97
+ if (target && owner) {
98
+ const { [primary]: theirValue, ...theirFields } = borrowWhole({
99
+ fields: target.toJSON(),
100
+ kind,
101
+ source: owner
102
+ });
103
+ const { [primary]: _ourRef, ...ourFields } = body;
104
+ const merged = {
105
+ ...inlineSourcedRefs(theirFields, owner, void 0, options),
106
+ ...inlineSourcedRefs(ourFields, config, slug, options),
107
+ [primary]: inlineSourcedRefs(theirValue, owner, void 0, options)
108
+ };
109
+ assertCarriedRefsResolve(merged, config, pointer.__ref);
110
+ return merged;
111
+ }
112
+ }
113
+ return inlineSourcedRefs(body, config, slug, options);
114
+ }
115
+ /**
116
+ * A borrowed entity's fields, following the source's own aliases to the bottom.
117
+ *
118
+ * Their `color/primary` may alias their `spectrum/gray/0`, and the alias target is often where the
119
+ * interesting fields live: a semantic token points at a palette token, and the palette token carries the
120
+ * light and dark values. Stopping at the first hop leaves a ref to one of their entities sitting in our
121
+ * body, pointing at nothing of ours, and drops the conditional values with it.
122
+ *
123
+ * Each hop merges the way the borrow itself does — the outer entity's fields over the inner's — so a
124
+ * semantic token stating its own `overrides` keeps them, and one stating none inherits the palette
125
+ * Token's. The primary field always comes from the bottom, which is what an alias chain is for.
126
+ *
127
+ * Each hop asks the kind it is in for that field rather than assuming the chain stays one kind: an alias
128
+ */
129
+ function borrowWhole({ fields, kind, source, seen = [] }) {
130
+ const primary = source.entityOf(kind)?.primaryField;
131
+ if (primary === void 0) return fields;
132
+ const alias = fields[primary];
133
+ if (!isRef(alias) || sourceOf(alias) !== void 0) return fields;
134
+ const innerKind = kindOf(alias);
135
+ const inner = source.resolve(innerKind, pathOf(alias));
136
+ if (!inner) return fields;
137
+ if (seen.includes(alias.__ref)) rejectData(`Cannot inline through "${alias.__ref}": the source aliases in a cycle (${[...seen, alias.__ref].join(" → ")}).`);
138
+ const resolved = borrowWhole({
139
+ fields: inner.toJSON(),
140
+ kind: innerKind,
141
+ source,
142
+ seen: [...seen, alias.__ref]
143
+ });
144
+ const innerPrimary = source.entityOf(innerKind)?.primaryField ?? primary;
145
+ return {
146
+ ...resolved,
147
+ ...fields,
148
+ [primary]: resolved[innerPrimary]
149
+ };
150
+ }
151
+ /**
152
+ * Every local ref in a carried body has to resolve here, or the borrowed entity depends on something this
153
+ * Config doesn't have.
154
+ *
155
+ * A borrowed entity arrives whole, and what it brought may point at things: a token's conditional value
156
+ * names the modifier it varies under, and other kinds name other kinds. Those refs were written in the
157
+ * source's vocabulary and now live in ours, so they resolve against us. That is the answer a consumer
158
+ * wants — a token you pulled in flips under your dark mode — and an obligation at the same time.
159
+ *
160
+ * The failure is loud and names the fix. A dangling `on` would emit a conditional value under no
161
+ * condition, reading as "this colour is always that" rather than "this colour changes in dark mode", and
162
+ * nothing downstream could tell the difference. That is the class this refuses: a borrowed entity that
163
+ * silently behaves like a lesser version of itself.
164
+ *
165
+ * A sourced ref is skipped. It still names the source's world, and it either inlined already or belongs
166
+ * to a link we aren't resolving.
167
+ */
168
+ function assertCarriedRefsResolve(fields, config, borrowed) {
169
+ for (const target of collectRefs(fields)) {
170
+ if (sourceOf(target) !== void 0) continue;
171
+ const kind = kindOf(target);
172
+ const name = pathOf(target);
173
+ if (config.resolve(kind, name)) continue;
174
+ rejectData(`Cannot inline "${borrowed}": it depends on ${kind} "${name}", which this config does not declare. A borrowed entity comes whole, and what it brought resolves against YOUR entities of the same name — so declare "${name}", or state your own value for this one to replace what it carried.`);
175
+ }
176
+ }
177
+ /** Whether `owner` declares a group at the path this ref names — the half of "does the target exist"
178
+ * that `resolveRef` cannot answer, because it narrows to an item. */
179
+ function declaresGroup(owner, value) {
180
+ return owner.groups(kindOf(value)).some((group) => group.path === pathOf(value));
181
+ }
182
+ /** Whether this ref is one the caller asked to inline — any source, or the one named. Takes the ref
183
+ * either way it is spelled, because `collectRefs` hands back the string form. */
184
+ function wants(value, slug) {
185
+ const source = sourceOf(value);
186
+ return source !== void 0 && (slug === void 0 || source === slug);
187
+ }
188
+ /**
189
+ * Whether a body holds a ref this walk is asked to take — the cheap prefilter on the way to
190
+ * {@link rewrittenBody}, and the reason a detach doesn't walk entities it has no business touching.
191
+ *
192
+ * It answers "is there anything here for this walk", not "will this walk change anything". The second
193
+ * question is the one a write depends on, and only running the walk answers it — a ref can be present
194
+ * and deliberately kept. So this is the skip, not the decision.
195
+ *
196
+ * Deep, because the walk is: a conditional value that varies under a borrowed modifier holds its ref a
197
+ * level down, and a shallow test would call that item local and leave it pointing at a dropped pin.
198
+ */
199
+ function holdsWantedRef(body, slug) {
200
+ return collectRefs(body).some((target) => wants(target, slug));
201
+ }
202
+ /**
203
+ * Whether two bodies say the same thing — the test that decides whether a walk wrote anything.
204
+ *
205
+ * Structural, because an identity check cannot be: `inlineSourcedRefs` allocates a fresh object for
206
+ * every object it visits, so `!==` reports "changed" for every body it touched. What it does not
207
+ * reallocate is a leaf it decided to leave alone, which is why `Object.is` still short-circuits most
208
+ * of the walk.
209
+ */
210
+ function sameBody(left, right) {
211
+ if (Object.is(left, right)) return true;
212
+ if (Array.isArray(left) || Array.isArray(right)) return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((item, index) => sameBody(item, right[index]));
213
+ if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return false;
214
+ const theirs = right;
215
+ const ours = Object.entries(left);
216
+ return ours.length === Object.keys(theirs).length && ours.every(([key, value]) => key in theirs && sameBody(value, theirs[key]));
217
+ }
218
+ /**
219
+ * One body walked, or `undefined` when the walk gave back what it was handed — the whole of "does this
220
+ * Entity need an `update`?", and the guard every write site here goes through.
221
+ *
222
+ * {@link holdsWantedRef} is a prefilter and cannot be the whole answer, because holding a sourced ref
223
+ * and being rewritten are not the same question. `keepIdentityRefs` is on for publishing and for every
224
+ * scoped detach — the pin survives both — so a row whose only tie to the link is a ref at something
225
+ * with no value (a component's `file`, a `style-property` a prop names) passes the prefilter, keeps its
226
+ * ref exactly as it was by design, and is left byte-identical.
227
+ *
228
+ * Writing the `update` anyway is not harmless in the snapshot's sense of harmless: the row lands in the
229
+ * branch's draft and in the changes panel claiming an edit nobody made, and reports a detach that
230
+ * detached nothing as a success. It is the same thing the prefilter exists to prevent, one step later.
231
+ */
232
+ function rewrittenBody({ body, kind, config, slug, options }) {
233
+ if (!holdsWantedRef(body, slug)) return void 0;
234
+ const inlined = inlineWholeEntity({
235
+ body,
236
+ kind,
237
+ config,
238
+ slug,
239
+ options
240
+ });
241
+ return sameBody(body, inlined) ? void 0 : inlined;
242
+ }
243
+ /**
244
+ * Every item and group body of `config`, rewritten by {@link inlineSourcedRefs}.
245
+ *
246
+ * `linked-system` is skipped: the pins are not values that inline, and they stay — provenance is
247
+ * information a consumer's tooling still wants for attribution and upgrade detection. Deleting one is
248
+ * the pin's own `delete`, once nothing refers through it.
249
+ *
250
+ * Groups are walked as well as items. A group body carries fields like any entity, so a ref hiding in
251
+ * one would otherwise survive a walk that only visited items — and "nothing sourced survives" is the
252
+ * guarantee, not "nothing sourced survives in the places we remembered to look".
253
+ */
254
+ /**
255
+ * Where each thing borrowed through `slug` will live once the pin is gone — `style-property:bg@ds` →
256
+ * `style-property:bg`.
257
+ *
258
+ * The third answer for a ref that names an identity rather than a value, and the one that makes "link
259
+ * everything, then break the link" work. A borrowed component declares a `styleProperty` prop pointing
260
+ * at `style-property:bg@ds`, and taking the source in materializes their style-property locally. So the
261
+ * pointer is neither a seam to keep, since there will be no pin, nor a value to inline, since a
262
+ * style-property has none. It is a pointer at something that is now ours.
263
+ *
264
+ * Read before anything moves, because a borrowed item is a stored pointer and materializing it is what
265
+ * erases the pointer this map is built from.
266
+ *
267
+ * Two ways a borrow lands somewhere, and the map needs both:
268
+ *
269
+ * - an item, whose local path is whatever this config filed it under. `link` takes a `to`, so their
270
+ * `bg` may be our `theirBg`, and path equality would point at an unrelated entity of ours.
271
+ * - a folder, which may have a group node pointing through the pin, or may be emergent from members
272
+ * that each do. The second has nothing stored to read, so the local group's own path answers, and a
273
+ * ref at `token:color@ds` becomes our `token:color`.
274
+ *
275
+ * A target in neither is left out deliberately: the refusal downstream names it, which is the right
276
+ * outcome for borrowing a component without the style-property it uses.
277
+ */
278
+ function adoptedRefs(config, slug) {
279
+ const moved = /* @__PURE__ */ new Map();
280
+ for (const kind of config.ownedKinds()) {
281
+ for (const item of config.list(kind)) {
282
+ const body = config.storedBody(kind, item.path);
283
+ if (!isRef(body) || sourceOf(body) !== slug) continue;
284
+ moved.set(entityRefOf(body.__ref), ref(kind, item.path).__ref);
285
+ }
286
+ for (const group of config.groups(kind)) {
287
+ const local = ref(kind, group.path).__ref;
288
+ moved.set(ref(kind, group.path, slug).__ref, local);
289
+ const pointer = group.meta.ref;
290
+ if (isRef(pointer) && sourceOf(pointer) === slug) moved.set(entityRefOf(pointer.__ref), local);
291
+ }
292
+ }
293
+ return moved;
294
+ }
295
+ function inlineAllSources(config, slug, options) {
296
+ const walk = slug === void 0 ? options : {
297
+ ...options,
298
+ adopted: adoptedRefs(config, slug)
299
+ };
300
+ const materialized = config.withMaterializedExtensions({ slug });
301
+ let next = materialized;
302
+ for (const kind of materialized.ownedKinds()) {
303
+ if (materialized.entityOf(kind)?.sourceIdentity) continue;
304
+ for (const item of materialized.list(kind)) {
305
+ const data = rewrittenBody({
306
+ body: item.toJSON(),
307
+ kind,
308
+ config: materialized,
309
+ slug,
310
+ options: walk
311
+ });
312
+ if (data === void 0) continue;
313
+ next = next.apply({
314
+ kind,
315
+ operation: "update",
316
+ path: item.path,
317
+ data
318
+ });
319
+ }
320
+ for (const group of materialized.groups(kind)) {
321
+ const inlined = detachedGroupMeta({
322
+ config: materialized,
323
+ kind,
324
+ groupPath: group.path,
325
+ slug,
326
+ options: walk
327
+ });
328
+ if (!inlined) continue;
329
+ next = next.apply({
330
+ kind,
331
+ operation: "group-update",
332
+ path: group.path,
333
+ data: inlined
334
+ });
335
+ }
336
+ }
337
+ return next;
338
+ }
339
+ /**
340
+ * A group's body with its borrow through `slug` dissolved — the pointer dropped, and what it was
341
+ * inheriting kept as its own.
342
+ *
343
+ * A group's `ref` is the one sourced ref that must not be inlined, and the difference is what it names.
344
+ * A ref in a value position stands in for a value; this names a folder, and a folder has no value to
345
+ * collapse to. Handed to {@link inlineSourcedRefs} it takes the "nothing there has a value to keep"
346
+ * throw, so a config holding a linked group can't be unlinked or published at all.
347
+ *
348
+ * Dropping it is also right rather than a way around the throw. Being borrowed is derived for a folder,
349
+ * from every member agreeing on one slug, so once the members are local the pointer is a claim that has
350
+ * stopped being true, and leaving it would report the folder as linked to a system nothing else draws
351
+ * through.
352
+ *
353
+ * `ref: null` rather than an omission, because a `group-update` merges over the stored body and a key
354
+ * left out is a key left alone. The label comes from `Config.groups`, which has already merged the
355
+ * source's in, and reading the inherited meta is what keeps "Palette" on the folder after the link that
356
+ * supplied it is gone.
357
+ *
358
+ * `undefined` when the folder draws through nothing this walk takes, including the ordinary case of an
359
+ * emergent folder with no stored node, which a `group-update` would otherwise mint out of nothing for
360
+ * every prefix of every kind.
361
+ */
362
+ /**
363
+ * A folder's meta with its borrow dropped — the pointer, and the mark that was only true while it
364
+ * pointed.
365
+ *
366
+ * A folder the build minted is minted because it points through a link, so once the pointer is gone
367
+ * the mark is a claim that has stopped being true. It also has to go for a mechanical reason: no kind
368
+ * declares `__minted` as a group field, so a `group-update` carrying it is refused outright.
369
+ */
370
+ function withoutPointer(meta) {
371
+ const { ref: _dropped, [MINTED]: _wasMinted, ...kept } = meta;
372
+ return kept;
373
+ }
374
+ function detachedGroupMeta({ config, kind, groupPath, slug, options }) {
375
+ const meta = config.groups(kind).find((group) => group.path === groupPath)?.meta ?? {};
376
+ const pointer = meta.ref;
377
+ if (!isRef(pointer) || !wants(pointer, slug)) {
378
+ if (!holdsWantedRef(meta, slug)) return void 0;
379
+ const inlined = inlineSourcedRefs(meta, config, slug, options);
380
+ return sameBody(meta, inlined) ? void 0 : inlined;
381
+ }
382
+ return {
383
+ ...inlineSourcedRefs(withoutPointer(meta), config, slug, options),
384
+ ref: null
385
+ };
386
+ }
387
+ //#endregion
388
+ export { inlineAllSources };
@@ -0,0 +1,25 @@
1
+ import { Config } from "./Config.js";
2
+
3
+ //#region src/framework/layer-styles.d.ts
4
+ /** Whether every prop a rule names holds the value it asks for. Compared as strings, because a
5
+ * condition is canonicalized into the rule's key and a variant value is a string there. */
6
+ declare function ruleApplies(when: Record<string, unknown> | null | undefined, state: Record<string, unknown>): boolean;
7
+ interface LayerStyleOptions {
8
+ /**
9
+ * Modifiers to treat as active — `['hover']` for a matrix column showing the hover state.
10
+ *
11
+ * A modifier's styles live in a `_<modifier>` block inside a layer's bag, which is where the
12
+ * stylesheet reads them from too. Forcing one flattens that block up into the bag, so a surface sees
13
+ * the values that state actually paints rather than the block itself.
14
+ */
15
+ readonly forced?: readonly string[];
16
+ }
17
+ /**
18
+ * The resolved style bag per layer, for the props an instance was given.
19
+ *
20
+ * Layers with nothing to say are absent rather than empty, so a caller can tell "no styling" from
21
+ * "styled with nothing".
22
+ */
23
+ declare function layerStyles(config: Config, path: string, props?: Record<string, unknown>, options?: LayerStyleOptions): Record<string, Record<string, unknown>>;
24
+ //#endregion
25
+ export { LayerStyleOptions, layerStyles, ruleApplies };
@@ -0,0 +1,49 @@
1
+ import { isRef, refLeaf } from "./utils/refs.js";
2
+ import { authoredBag } from "./projections.js";
3
+ //#region src/framework/layer-styles.ts
4
+ /** Whether every prop a rule names holds the value it asks for. Compared as strings, because a
5
+ * condition is canonicalized into the rule's key and a variant value is a string there. */
6
+ function ruleApplies(when, state) {
7
+ return Object.entries(when ?? {}).every(([prop, value]) => String(state[prop]) === String(refLeaf(value)));
8
+ }
9
+ /**
10
+ * Merge a bag over another, flattening any forced modifier block as it goes.
11
+ *
12
+ * The bag arrives already read back through {@link authoredBag}, so what's merged is the leaf an
13
+ * author wrote rather than the ref the bag stores — this projection answers "what is this layer styled
14
+ * with" for a panel and a matrix cell, and a picker's options are leaves.
15
+ */
16
+ function mergeBag(into, bag, forced) {
17
+ for (const [key, value] of Object.entries(bag)) {
18
+ const modifier = key.startsWith("_") ? key.slice(1) : void 0;
19
+ if (modifier && value && typeof value === "object" && !isRef(value)) {
20
+ if (forced.includes(modifier)) mergeBag(into, value, forced);
21
+ else into[key] = value;
22
+ continue;
23
+ }
24
+ into[key] = value;
25
+ }
26
+ }
27
+ /**
28
+ * The resolved style bag per layer, for the props an instance was given.
29
+ *
30
+ * Layers with nothing to say are absent rather than empty, so a caller can tell "no styling" from
31
+ * "styled with nothing".
32
+ */
33
+ function layerStyles(config, path, props = {}, options = {}) {
34
+ const { styles = {}, defaultProps = {} } = config.resolve("component", path)?.toJSON() ?? {};
35
+ const state = {
36
+ ...defaultProps,
37
+ ...props
38
+ };
39
+ const forced = options.forced ?? [];
40
+ const applicable = Object.values(styles).filter((rule) => ruleApplies(rule.when, state)).sort((left, right) => Object.keys(left.when ?? {}).length - Object.keys(right.when ?? {}).length);
41
+ const out = {};
42
+ for (const rule of applicable) for (const [layer, bag] of Object.entries(rule.layers ?? {})) {
43
+ out[layer] ??= {};
44
+ mergeBag(out[layer], authoredBag(config, bag ?? {}), forced);
45
+ }
46
+ return out;
47
+ }
48
+ //#endregion
49
+ export { layerStyles, ruleApplies };
@@ -0,0 +1,48 @@
1
+ import { Snapshot } from "./snapshot.js";
2
+
3
+ //#region src/framework/memo.d.ts
4
+ /** The structural shape a memo needs — any config-like that can produce its snapshot. Structural
5
+ * rather than an import of `Config`, which would be a cycle (Config memoizes through this). */
6
+ interface HasSnapshot {
7
+ readonly snapshot: Snapshot;
8
+ /**
9
+ * The live source a LINKED kind reads through, when there is one.
10
+ *
11
+ * Optional, so a bare `{ snapshot }` is still a valid config-like — but a real `Config` must
12
+ * implement it, because a linked kind's data isn't in the consumer's snapshot at all.
13
+ */
14
+ linkedSource?(kind: string): {
15
+ readonly config: HasSnapshot;
16
+ } | undefined;
17
+ /**
18
+ * The linked SOURCES attached to this config, in a stable order.
19
+ *
20
+ * Part of every key, and not optional to correctness: a source's entities live in the source's
21
+ * snapshot, so a config that swapped one — hydrating with `{ sources }`, re-attaching after a
22
+ * republish — has the same slices and different data. Keyed on the source configs themselves, a
23
+ * derivation that reads a borrowed value recomputes when the source it read from changes.
24
+ */
25
+ attachedSources?(): readonly object[];
26
+ }
27
+ /**
28
+ * Wrap `compute` in a cache keyed on the identity of the `kinds` slices it reads.
29
+ *
30
+ * Entries are held in a chain of `WeakMap`s — one level per kind — so a cached result dies with the
31
+ * snapshot slices that produced it. Nothing to invalidate, and no version arithmetic.
32
+ *
33
+ * ```ts
34
+ * const tokenViews = createSliceMemo({
35
+ * kinds: ['token', 'modifier'],
36
+ * compute: (config: Config) => …,
37
+ * });
38
+ * ```
39
+ */
40
+ declare function createSliceMemo<C extends HasSnapshot, T>({
41
+ kinds,
42
+ compute
43
+ }: {
44
+ readonly kinds: readonly string[];
45
+ readonly compute: (config: C) => T;
46
+ }): (config: C) => T;
47
+ //#endregion
48
+ export { HasSnapshot, createSliceMemo };
@@ -0,0 +1,84 @@
1
+ //#region src/framework/memo.ts
2
+ /** Distinguishes "cached `undefined`" from "not cached" without an extra `has` lookup per level. */
3
+ const MISS = Symbol("miss");
4
+ /**
5
+ * The level a cached value hangs off, so every slice is an interior key.
6
+ *
7
+ * Chains vary in length — one config links a kind and another doesn't — and without a terminator a
8
+ * short chain would read its value where a long one expects another level.
9
+ */
10
+ const RESULT = Object.freeze({});
11
+ /**
12
+ * Every slice a computation reads for one kind: this config's, then its source's, and so on down
13
+ * the link chain.
14
+ *
15
+ * A linked kind reads THROUGH — `Config.list(kind)` returns the source's items — so the consumer's
16
+ * own slice is the wrong thing to key on by itself. It never changes (the consumer owns nothing of
17
+ * that kind), so a memo keyed on it alone caches the source's first answer forever.
18
+ */
19
+ function sliceChain(config, kind, acc, seen) {
20
+ if (seen.has(config)) return;
21
+ seen.add(config);
22
+ acc.push(config.snapshot.slice(kind));
23
+ const source = config.linkedSource?.(kind)?.config;
24
+ if (source) sliceChain(source, kind, acc, seen);
25
+ }
26
+ /**
27
+ * The identity of the data a computation over `kinds` reads: every attached source, then each kind's
28
+ * slice chain.
29
+ *
30
+ * Exported because two things need it and they must agree. A memo hangs its cache off these objects;
31
+ * a change hook compares this run's against the last one's to decide whether the derivation can be
32
+ * skipped (`runChangeHooks`). Two statements of "what this computation reads" would drift, and the
33
+ * failure is silent both ways — a stale cache hit, or a paint that never happens.
34
+ *
35
+ * Sources come first: they qualify every slice below them, and a config with none contributes nothing.
36
+ */
37
+ function sliceKeys({ config, kinds }) {
38
+ const keys = [...config.attachedSources?.() ?? []];
39
+ for (const kind of kinds) sliceChain(config, kind, keys, /* @__PURE__ */ new Set());
40
+ return keys;
41
+ }
42
+ /** Whether two key lists name the same data — the skip test for a change hook. */
43
+ function sameSliceKeys({ before, after }) {
44
+ return before.length === after.length && before.every((key, index) => key === after[index]);
45
+ }
46
+ /**
47
+ * Wrap `compute` in a cache keyed on the identity of the `kinds` slices it reads.
48
+ *
49
+ * Entries are held in a chain of `WeakMap`s — one level per kind — so a cached result dies with the
50
+ * snapshot slices that produced it. Nothing to invalidate, and no version arithmetic.
51
+ *
52
+ * ```ts
53
+ * const tokenViews = createSliceMemo({
54
+ * kinds: ['token', 'modifier'],
55
+ * compute: (config: Config) => …,
56
+ * });
57
+ * ```
58
+ */
59
+ function createSliceMemo({ kinds, compute }) {
60
+ if (kinds.length === 0) throw new Error("createSliceMemo needs at least one kind — a computation that reads nothing has nothing to key on");
61
+ const root = /* @__PURE__ */ new WeakMap();
62
+ return (config) => {
63
+ const keys = sliceKeys({
64
+ config,
65
+ kinds
66
+ });
67
+ let level = root;
68
+ for (const key of keys) {
69
+ let next = level.get(key);
70
+ if (!next) {
71
+ next = /* @__PURE__ */ new WeakMap();
72
+ level.set(key, next);
73
+ }
74
+ level = next;
75
+ }
76
+ const cached = level.has(RESULT) ? level.get(RESULT) : MISS;
77
+ if (cached !== MISS) return cached;
78
+ const value = compute(config);
79
+ level.set(RESULT, value);
80
+ return value;
81
+ };
82
+ }
83
+ //#endregion
84
+ export { createSliceMemo, sameSliceKeys, sliceKeys };